feat: include refractor card image in tier-up notification embed (#144) #159
No reviewers
Labels
No Label
ai-changes-requested
ai-failed
ai-pr-opened
ai-reviewed
ai-reviewing
ai-working
ai-working
bug
enhancement
feature
in-queue
performance
security
tech-debt
todo
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/paper-dynasty-discord#159
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "issue/144-feat-tier-up-notification-should-include-refractor"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #144
Summary
The tier-up notification embed previously sent text-only. This PR adds the newly rendered refractor card image to the embed so players see card art at the peak moment of the progression loop.
Changes
helpers/refractor_notifs.pybuild_tier_up_embed()accepts optionalimage_url: str | None = None; callsembed.set_image(url=image_url)when providednotify_tier_completion()accepts optionalimage_urland passes it through to the embed buildercommand_logic/logic_gameplay.py_trigger_variant_renders()now captures the render endpoint response and returnsdict[player_id, image_url]instead ofNone_run_post_game_refractor_hook()ordering changed: renders run first (to obtain image URLs from the S3 upload response), then notifications fire withimage_url=kwarg — card art appears in the embed immediatelytests/test_post_game_refractor_hook.py{}from_trigger_variant_renders(was returningAsyncMock)test_channel_passed_to_notifyupdated: assertion now includesimage_url=Nonetest_notifications_before_render_trigger→test_render_before_notification: ordering flipped to["render", "notify"],fake_notifysignature updated, docstring updatedNotes
If the render endpoint returns
Noneor a response withoutimage_url(e.g. Playwright timeout), the notification sends without an image — graceful degradation. Team name/logo was not added as thetier_updict fromevaluate-gamedoes not currently include team data.All 17 tests pass.
AI Code Review
Files Reviewed
helpers/refractor_notifs.py(modified)command_logic/logic_gameplay.py(modified)tests/test_post_game_refractor_hook.py(modified)Findings
Correctness
No issues found.
build_tier_up_embedaddsimage_url: str | None = Nonebeforeset_footer— correct position in the embed build.if image_url:guard correctly skipsset_imagewhen URL is absent or falsy._trigger_variant_rendersnow returnsimage_urls: dict. Theif result and isinstance(result, dict):guard safely handlesNone/non-dict responses (e.g. Playwright timeout). Players with failed renders are omitted from the map, soimage_url_map.get(player_id)returnsNone→ graceful degradation path works correctly._run_post_game_refractor_hookordering:_trigger_variant_renders→ then notify loop withimage_url=img. Thetier_up.get("player_id")safe lookup means a missingplayer_idkey returnsNone→image_url_map.get(None)→None→ no image, no crash._trigger_variant_rendersthrough_run_post_game_refractor_hooknow havereturn_value={}on the mock. Tests where the guard short-circuits (tier_ups=[]or absent key) correctly omit thereturn_valuesince the mock is never called.fake_renderintest_render_before_notificationcorrectly returns{}— necessary because the production code now doesimage_url_map.get(...)on the return value.fake_notifysignature updated to(ch, tu, image_url=None)— required since production now calls withimage_url=imgkwarg; old two-arg signature would have raisedTypeError.test_channel_passed_to_notifyassertion updated tomock_notify.assert_called_once_with(channel, tier_up, image_url=None)— correct given_trigger_variant_rendersreturns{}soimage_url_map.get(1)→None.Security
No issues found.
image_urloriginates from the internal database API response (trusted source), not user input — no injection risk.Style & Conventions
No issues found. Docstrings updated, naming follows existing patterns, return type annotation on
_trigger_variant_renderschanged todict(accurate).Suggestions
_trigger_variant_rendersreturns a real URL that gets forwarded tonotify_tier_completion. A test likemock_render.return_value = {101: "https://example.com/card.png"}then assertingmock_notify.assert_called_once_with(channel, tier_up, image_url="https://example.com/card.png")would close this. Not a blocker — the pass-through logic is simple — but would serve as a regression guard.Verdict: APPROVED
Clean implementation. The ordering flip (renders before notifications) is well-motivated and correctly sequenced. Graceful degradation is handled at every failure point. Tests are updated consistently — no stale assertions. The coverage gap above is a suggestion only.
Automated review by Claude PR Reviewer
Approved via pd-pr
Approved via pd-pr