(campaign): Add reward webhook logging

This commit is contained in:
2026-07-29 15:47:58 +03:00
parent ee70b80120
commit 9203a795e3
+121 -3
View File
@@ -6,22 +6,38 @@ from data import ORDER, Condition
from data.conditions import NULL, condition from data.conditions import NULL, condition
from utils.lib import utc_now from utils.lib import utc_now
from .lib import LOWER, asexpr from .lib import LOWER, asexpr, ThreadedWebhook
from .data import ( from .data import (
CampaignData, CampaignData,
Campaign, Campaign,
EarnedReward, EarnedReward,
) )
from . import logger
class RewardCampaign: class RewardCampaign:
def __init__(self, row: Campaign): def __init__(self, row: Campaign):
self.row = row self.row = row
self._webhook: ThreadedWebhook | None = None
self._cached_webhookurl: str | None = None
@property @property
def is_active(self): def is_active(self):
return self.row.started_at is not None and self.row.completed_at is None return self.row.started_at is not None and self.row.completed_at is None
@property
def webhook(self):
if self._cached_webhookurl != self.row.logging_webhook_url:
url = self._cached_webhookurl = self.row.logging_webhook_url
if url is not None:
# TODO: I don't know if these needs a client
# Might be hard if so given we need to run this from Twitch client as well
self._webhook = ThreadedWebhook.from_url(url)
else:
self._webhook = None
return self._webhook
async def start(self): async def start(self):
"""Start the reward campaign.""" """Start the reward campaign."""
if self.row.started_at is not None: if self.row.started_at is not None:
@@ -53,15 +69,103 @@ class RewardCampaign:
twitch_user_name=twitch_user_name, twitch_user_name=twitch_user_name,
earned_at=earned_at or utc_now(), earned_at=earned_at or utc_now(),
) )
await self.try_to_log_reward(row)
return row return row
async def update_reward(self, rewardid: int, **kwargs):
# In particular, log or update the logged message
if kwargs:
reward = await EarnedReward.fetch(rewardid)
if reward is None:
raise ValueError("Reward doesn't exist")
await reward.update(**kwargs)
await self.try_to_log_reward(reward)
async def get_rewards(self) -> list[EarnedReward]: async def get_rewards(self) -> list[EarnedReward]:
rows = await EarnedReward.fetch_where( rows = await EarnedReward.fetch_where(
campaign_id=self.row.campaign_id campaign_id=self.row.campaign_id
).order_by("earned_at", direction=ORDER.ASC) ).order_by("earned_at", direction=ORDER.ASC)
return rows return rows
async def try_to_log_reward(self, reward: EarnedReward):
import discord
if self.webhook:
embed = await self._log_format_reward(reward)
if reward.log_messageid:
# Try and edit message
try:
await self.webhook.edit_message(reward.log_messageid, embed=embed)
except discord.HTTPException:
await reward.update(log_messageid=None)
if not reward.log_messageid:
try:
message = await self.webhook.send(embed=embed, wait=True)
await reward.update(log_messageid=message.id)
except discord.HTTPException:
# Couldn't send, give up.
logger.warning(f"Failed to log campaign reward {reward!r}", exc_info=True)
async def _log_format_reward(self, reward: EarnedReward):
"""
Quick and ugly embed format for the webhook.
"""
import discord
embed = discord.Embed(
title=f"Reward #{reward.earned_id} in {self.row.campaign_name}",
)
# User Field
embed.add_field(
name="User Information",
value=(
f"Twitch user `{reward.twitch_user_name or 'Unknown'}` `(ID: {reward.twitch_user_id or 'Unknown'})`.\n"
f"Internal ID `{reward.profileid}`"
),
inline=True
)
# Earning field
embed.add_field(
name="Reward Earned",
value=(
f"Earned campaign reward at {discord.utils.format_dt(reward.earned_at, 'F')}.\n"
f"Earned reason: '{reward.earned_from}'"
),
inline=True
)
# Reference field
embed.add_field(
name="Reference",
value=reward.reference or "No Reference information saved.",
inline=False
)
# Modnote field
embed.add_field(
name="Notes",
value=reward.modnote or "No notes added",
inline=False
)
# Fulfilled field
if reward.fulfilled_at is not None:
fluffed = f"Fluffed at {discord.utils.format_dt(reward.fulfilled_at, 'F')}"
if reward.fulfilled_note:
fluffed += '\n' + "Fluff Note: " + reward.fulfilled_note
else:
fluffed = "Not yet fluffed"
embed.add_field(
name="Fulfilled",
value=fluffed
)
embed.set_footer(text="Last Updated")
embed.timestamp = utc_now()
return embed
class CampaignRegistry: class CampaignRegistry:
VERSION = CampaignData.VERSION VERSION = CampaignData.VERSION
@@ -132,7 +236,7 @@ class CampaignRegistry:
self, self,
cid: int, cid: int,
campaign_name: str, campaign_name: str,
target_rewards: Optional[int] = None, **kwargs
) -> RewardCampaign: ) -> RewardCampaign:
""" """
Create a new campaign. Create a new campaign.
@@ -140,7 +244,21 @@ class CampaignRegistry:
""" """
row = await Campaign.create( row = await Campaign.create(
communityid=cid, communityid=cid,
target_rewards=target_rewards,
campaign_name=campaign_name, campaign_name=campaign_name,
**kwargs
) )
return RewardCampaign(row) return RewardCampaign(row)
async def update_campaign(
self,
campaign_id: int,
**kwargs
) -> RewardCampaign:
"""
Update a campaign with the given data args.
Registry handles this for caching and dispatch reasons.
"""
campaign = await Campaign.fetch(campaign_id)
await campaign.update(**kwargs)
return RewardCampaign(campaign)