fix: rename evolution to refractor terminology, fix tier names
All checks were successful
Ruff Lint / lint (pull_request) Successful in 12s
All checks were successful
Ruff Lint / lint (pull_request) Successful in 12s
- Rename helpers/evolution_notifs.py -> helpers/refractor_notifs.py - Rename tests/test_evolution_notifications.py -> tests/test_refractor_notifs.py - Delete utilities/evolution_notifications.py (replaced by helpers/refractor_notifs.py) - Update TIER_NAMES to canonical names: Base Card, Base Chrome, Refractor, Gold Refractor, Superfractor - Update T4 embed title from "FULLY EVOLVED!" to "SUPERFRACTOR!" - Update FOOTER_TEXT from "Paper Dynasty Evolution" to "Paper Dynasty Refractor" - Update non-max tier embed title from "Evolution Tier Up!" to "Refractor Tier Up!" - Add discord.abc.Messageable type annotation to notify_tier_completion channel param - Update all test assertions to match new tier names and strings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3a85564a6d
commit
9940b160db
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Evolution Tier Completion Notifications
|
Refractor Tier Completion Notifications
|
||||||
|
|
||||||
Builds and sends Discord embeds when a player completes an evolution tier
|
Builds and sends Discord embeds when a player completes a refractor tier
|
||||||
during post-game evaluation. Each tier-up event gets its own embed.
|
during post-game evaluation. Each tier-up event gets its own embed.
|
||||||
|
|
||||||
Notification failures are non-fatal: the send is wrapped in try/except so
|
Notification failures are non-fatal: the send is wrapped in try/except so
|
||||||
@ -16,11 +16,11 @@ logger = logging.getLogger("discord_app")
|
|||||||
|
|
||||||
# Human-readable display names for each tier number.
|
# Human-readable display names for each tier number.
|
||||||
TIER_NAMES = {
|
TIER_NAMES = {
|
||||||
0: "Unranked",
|
0: "Base Card",
|
||||||
1: "Initiate",
|
1: "Base Chrome",
|
||||||
2: "Rising",
|
2: "Refractor",
|
||||||
3: "Ascendant",
|
3: "Gold Refractor",
|
||||||
4: "Evolved",
|
4: "Superfractor",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Tier-specific embed colors.
|
# Tier-specific embed colors.
|
||||||
@ -28,10 +28,10 @@ TIER_COLORS = {
|
|||||||
1: 0x2ECC71, # green
|
1: 0x2ECC71, # green
|
||||||
2: 0xF1C40F, # gold
|
2: 0xF1C40F, # gold
|
||||||
3: 0x9B59B6, # purple
|
3: 0x9B59B6, # purple
|
||||||
4: 0x1ABC9C, # teal (fully evolved)
|
4: 0x1ABC9C, # teal (superfractor)
|
||||||
}
|
}
|
||||||
|
|
||||||
FOOTER_TEXT = "Paper Dynasty Evolution"
|
FOOTER_TEXT = "Paper Dynasty Refractor"
|
||||||
|
|
||||||
|
|
||||||
def build_tier_up_embed(tier_up: dict) -> discord.Embed:
|
def build_tier_up_embed(tier_up: dict) -> discord.Embed:
|
||||||
@ -55,11 +55,11 @@ def build_tier_up_embed(tier_up: dict) -> discord.Embed:
|
|||||||
color = TIER_COLORS.get(new_tier, 0x2ECC71)
|
color = TIER_COLORS.get(new_tier, 0x2ECC71)
|
||||||
|
|
||||||
if new_tier >= 4:
|
if new_tier >= 4:
|
||||||
# Fully evolved — special title and description.
|
# Superfractor — special title and description.
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="FULLY EVOLVED!",
|
title="SUPERFRACTOR!",
|
||||||
description=(
|
description=(
|
||||||
f"**{player_name}** has reached maximum evolution on the **{track_name}** track"
|
f"**{player_name}** has reached maximum refractor tier on the **{track_name}** track"
|
||||||
),
|
),
|
||||||
color=color,
|
color=color,
|
||||||
)
|
)
|
||||||
@ -70,7 +70,7 @@ def build_tier_up_embed(tier_up: dict) -> discord.Embed:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="Evolution Tier Up!",
|
title="Refractor Tier Up!",
|
||||||
description=(
|
description=(
|
||||||
f"**{player_name}** reached **Tier {new_tier} ({tier_name})** on the **{track_name}** track"
|
f"**{player_name}** reached **Tier {new_tier} ({tier_name})** on the **{track_name}** track"
|
||||||
),
|
),
|
||||||
@ -81,7 +81,9 @@ def build_tier_up_embed(tier_up: dict) -> discord.Embed:
|
|||||||
return embed
|
return embed
|
||||||
|
|
||||||
|
|
||||||
async def notify_tier_completion(channel, tier_up: dict) -> None:
|
async def notify_tier_completion(
|
||||||
|
channel: discord.abc.Messageable, tier_up: dict
|
||||||
|
) -> None:
|
||||||
"""Send a tier-up notification embed to the given channel.
|
"""Send a tier-up notification embed to the given channel.
|
||||||
|
|
||||||
Non-fatal: any exception during send is caught and logged so that a
|
Non-fatal: any exception during send is caught and logged so that a
|
||||||
@ -90,7 +92,7 @@ async def notify_tier_completion(channel, tier_up: dict) -> None:
|
|||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
channel:
|
channel:
|
||||||
A discord.TextChannel (or any object with an async ``send`` method).
|
A discord.abc.Messageable (e.g. discord.TextChannel).
|
||||||
tier_up:
|
tier_up:
|
||||||
Dict with keys: player_name, old_tier, new_tier, current_value, track_name.
|
Dict with keys: player_name, old_tier, new_tier, current_value, track_name.
|
||||||
"""
|
"""
|
||||||
@ -1,9 +1,9 @@
|
|||||||
"""
|
"""
|
||||||
Tests for Evolution Tier Completion Notification embeds.
|
Tests for Refractor Tier Completion Notification embeds.
|
||||||
|
|
||||||
These tests verify that:
|
These tests verify that:
|
||||||
1. Tier-up embeds are correctly formatted for tiers 1-3 (title, description, color).
|
1. Tier-up embeds are correctly formatted for tiers 1-3 (title, description, color).
|
||||||
2. Tier 4 (Fully Evolved) embeds include the special title, description, and note field.
|
2. Tier 4 (Superfractor) embeds include the special title, description, and note field.
|
||||||
3. Multiple tier-up events each produce a separate embed.
|
3. Multiple tier-up events each produce a separate embed.
|
||||||
4. An empty tier-up list results in no channel sends.
|
4. An empty tier-up list results in no channel sends.
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ from unittest.mock import AsyncMock
|
|||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
from helpers.evolution_notifs import build_tier_up_embed, notify_tier_completion
|
from helpers.refractor_notifs import build_tier_up_embed, notify_tier_completion
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Fixtures
|
# Fixtures
|
||||||
@ -49,11 +49,11 @@ def make_tier_up(
|
|||||||
class TestBuildTierUpEmbed:
|
class TestBuildTierUpEmbed:
|
||||||
"""Verify that build_tier_up_embed produces correctly structured embeds."""
|
"""Verify that build_tier_up_embed produces correctly structured embeds."""
|
||||||
|
|
||||||
def test_title_is_evolution_tier_up(self):
|
def test_title_is_refractor_tier_up(self):
|
||||||
"""Title must read 'Evolution Tier Up!' for any non-max tier."""
|
"""Title must read 'Refractor Tier Up!' for any non-max tier."""
|
||||||
tier_up = make_tier_up(new_tier=2)
|
tier_up = make_tier_up(new_tier=2)
|
||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
assert embed.title == "Evolution Tier Up!"
|
assert embed.title == "Refractor Tier Up!"
|
||||||
|
|
||||||
def test_description_contains_player_name(self):
|
def test_description_contains_player_name(self):
|
||||||
"""Description must contain the player's name."""
|
"""Description must contain the player's name."""
|
||||||
@ -65,11 +65,11 @@ class TestBuildTierUpEmbed:
|
|||||||
"""Description must include the human-readable tier name for the new tier."""
|
"""Description must include the human-readable tier name for the new tier."""
|
||||||
tier_up = make_tier_up(new_tier=2)
|
tier_up = make_tier_up(new_tier=2)
|
||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
# Tier 2 display name is "Rising"
|
# Tier 2 display name is "Refractor"
|
||||||
assert "Rising" in embed.description
|
assert "Refractor" in embed.description
|
||||||
|
|
||||||
def test_description_contains_track_name(self):
|
def test_description_contains_track_name(self):
|
||||||
"""Description must mention the evolution track (e.g., 'Batter')."""
|
"""Description must mention the refractor track (e.g., 'Batter')."""
|
||||||
tier_up = make_tier_up(track_name="Batter", new_tier=2)
|
tier_up = make_tier_up(track_name="Batter", new_tier=2)
|
||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
assert "Batter" in embed.description
|
assert "Batter" in embed.description
|
||||||
@ -92,11 +92,11 @@ class TestBuildTierUpEmbed:
|
|||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
assert embed.color.value == 0x9B59B6
|
assert embed.color.value == 0x9B59B6
|
||||||
|
|
||||||
def test_footer_text_is_paper_dynasty_evolution(self):
|
def test_footer_text_is_paper_dynasty_refractor(self):
|
||||||
"""Footer text must be 'Paper Dynasty Evolution' for brand consistency."""
|
"""Footer text must be 'Paper Dynasty Refractor' for brand consistency."""
|
||||||
tier_up = make_tier_up(new_tier=2)
|
tier_up = make_tier_up(new_tier=2)
|
||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
assert embed.footer.text == "Paper Dynasty Evolution"
|
assert embed.footer.text == "Paper Dynasty Refractor"
|
||||||
|
|
||||||
def test_returns_discord_embed_instance(self):
|
def test_returns_discord_embed_instance(self):
|
||||||
"""Return type must be discord.Embed so it can be sent directly."""
|
"""Return type must be discord.Embed so it can be sent directly."""
|
||||||
@ -106,24 +106,24 @@ class TestBuildTierUpEmbed:
|
|||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Unit: build_tier_up_embed — tier 4 (fully evolved)
|
# Unit: build_tier_up_embed — tier 4 (superfractor)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class TestBuildTierUpEmbedFullyEvolved:
|
class TestBuildTierUpEmbedSuperfractor:
|
||||||
"""Verify that tier 4 (Fully Evolved) embeds use special formatting."""
|
"""Verify that tier 4 (Superfractor) embeds use special formatting."""
|
||||||
|
|
||||||
def test_title_is_fully_evolved(self):
|
def test_title_is_superfractor(self):
|
||||||
"""Tier 4 title must be 'FULLY EVOLVED!' to emphasise max achievement."""
|
"""Tier 4 title must be 'SUPERFRACTOR!' to emphasise max achievement."""
|
||||||
tier_up = make_tier_up(old_tier=3, new_tier=4)
|
tier_up = make_tier_up(old_tier=3, new_tier=4)
|
||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
assert embed.title == "FULLY EVOLVED!"
|
assert embed.title == "SUPERFRACTOR!"
|
||||||
|
|
||||||
def test_description_mentions_maximum_evolution(self):
|
def test_description_mentions_maximum_refractor_tier(self):
|
||||||
"""Tier 4 description must mention 'maximum evolution' per the spec."""
|
"""Tier 4 description must mention 'maximum refractor tier' per the spec."""
|
||||||
tier_up = make_tier_up(player_name="Mike Trout", old_tier=3, new_tier=4)
|
tier_up = make_tier_up(player_name="Mike Trout", old_tier=3, new_tier=4)
|
||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
assert "maximum evolution" in embed.description.lower()
|
assert "maximum refractor tier" in embed.description.lower()
|
||||||
|
|
||||||
def test_description_contains_player_name(self):
|
def test_description_contains_player_name(self):
|
||||||
"""Player name must appear in the tier 4 description."""
|
"""Player name must appear in the tier 4 description."""
|
||||||
@ -138,7 +138,7 @@ class TestBuildTierUpEmbedFullyEvolved:
|
|||||||
assert "Batter" in embed.description
|
assert "Batter" in embed.description
|
||||||
|
|
||||||
def test_tier4_color_is_teal(self):
|
def test_tier4_color_is_teal(self):
|
||||||
"""Tier 4 uses teal (0x1abc9c) to visually distinguish max evolution."""
|
"""Tier 4 uses teal (0x1abc9c) to visually distinguish superfractor."""
|
||||||
tier_up = make_tier_up(old_tier=3, new_tier=4)
|
tier_up = make_tier_up(old_tier=3, new_tier=4)
|
||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
assert embed.color.value == 0x1ABC9C
|
assert embed.color.value == 0x1ABC9C
|
||||||
@ -174,11 +174,11 @@ class TestBuildTierUpEmbedFullyEvolved:
|
|||||||
"future" in note_field.value.lower() or "update" in note_field.value.lower()
|
"future" in note_field.value.lower() or "update" in note_field.value.lower()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_footer_text_is_paper_dynasty_evolution(self):
|
def test_footer_text_is_paper_dynasty_refractor(self):
|
||||||
"""Footer must remain 'Paper Dynasty Evolution' for tier 4 as well."""
|
"""Footer must remain 'Paper Dynasty Refractor' for tier 4 as well."""
|
||||||
tier_up = make_tier_up(old_tier=3, new_tier=4)
|
tier_up = make_tier_up(old_tier=3, new_tier=4)
|
||||||
embed = build_tier_up_embed(tier_up)
|
embed = build_tier_up_embed(tier_up)
|
||||||
assert embed.footer.text == "Paper Dynasty Evolution"
|
assert embed.footer.text == "Paper Dynasty Refractor"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -204,9 +204,9 @@ class TestNotifyTierCompletion:
|
|||||||
tier_up = make_tier_up(new_tier=2)
|
tier_up = make_tier_up(new_tier=2)
|
||||||
await notify_tier_completion(channel, tier_up)
|
await notify_tier_completion(channel, tier_up)
|
||||||
_, kwargs = channel.send.call_args
|
_, kwargs = channel.send.call_args
|
||||||
assert (
|
assert "embed" in kwargs, (
|
||||||
"embed" in kwargs
|
"notify_tier_completion must send an embed, not plain text"
|
||||||
), "notify_tier_completion must send an embed, not plain text"
|
)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_embed_type_is_discord_embed(self):
|
async def test_embed_type_is_discord_embed(self):
|
||||||
@ -241,9 +241,9 @@ class TestNotifyTierCompletion:
|
|||||||
]
|
]
|
||||||
for event in events:
|
for event in events:
|
||||||
await notify_tier_completion(channel, event)
|
await notify_tier_completion(channel, event)
|
||||||
assert (
|
assert channel.send.call_count == 3, (
|
||||||
channel.send.call_count == 3
|
"Each tier-up event must produce its own embed (no batching)"
|
||||||
), "Each tier-up event must produce its own embed (no batching)"
|
)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_no_tier_ups_means_no_sends(self):
|
async def test_no_tier_ups_means_no_sends(self):
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import discord
|
|
||||||
|
|
||||||
# Tier colors as Discord embed color integers
|
|
||||||
TIER_COLORS = {
|
|
||||||
1: 0x57F287, # green
|
|
||||||
2: 0xF1C40F, # gold
|
|
||||||
3: 0x9B59B6, # purple
|
|
||||||
4: 0x1ABC9C, # teal
|
|
||||||
}
|
|
||||||
|
|
||||||
MAX_TIER = 4
|
|
||||||
|
|
||||||
|
|
||||||
def tier_up_embed(
|
|
||||||
player_name: str, tier: int, tier_name: str, track_name: str
|
|
||||||
) -> discord.Embed:
|
|
||||||
"""
|
|
||||||
Build a Discord embed for a single evolution tier-up event.
|
|
||||||
|
|
||||||
For tier 4 (fully evolved), uses a distinct title, description, and footer.
|
|
||||||
For tiers 1–3, uses the standard tier-up format.
|
|
||||||
"""
|
|
||||||
color = TIER_COLORS.get(tier, 0xFFFFFF)
|
|
||||||
|
|
||||||
if tier == MAX_TIER:
|
|
||||||
embed = discord.Embed(
|
|
||||||
title="FULLY EVOLVED!",
|
|
||||||
description=f"{player_name} has reached maximum evolution on the {track_name} track",
|
|
||||||
color=color,
|
|
||||||
)
|
|
||||||
embed.set_footer(text="Rating boosts coming in a future update!")
|
|
||||||
else:
|
|
||||||
embed = discord.Embed(
|
|
||||||
title="Evolution Tier Up!",
|
|
||||||
description=f"{player_name} reached Tier {tier} ({tier_name}) on the {track_name} track",
|
|
||||||
color=color,
|
|
||||||
)
|
|
||||||
|
|
||||||
return embed
|
|
||||||
|
|
||||||
|
|
||||||
def build_tier_embeds(tier_ups: list) -> list:
|
|
||||||
"""
|
|
||||||
Build a list of Discord embeds for all tier-up events in a game.
|
|
||||||
|
|
||||||
Each item in tier_ups should be a dict with keys:
|
|
||||||
player_name (str), tier (int), tier_name (str), track_name (str)
|
|
||||||
|
|
||||||
Returns an empty list if there are no tier-ups.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
tier_up_embed(
|
|
||||||
player_name=t["player_name"],
|
|
||||||
tier=t["tier"],
|
|
||||||
tier_name=t["tier_name"],
|
|
||||||
track_name=t["track_name"],
|
|
||||||
)
|
|
||||||
for t in tier_ups
|
|
||||||
]
|
|
||||||
Loading…
Reference in New Issue
Block a user