From dfaa0720eeb937a0a8ac00e3ab6c4150ce55004d Mon Sep 17 00:00:00 2001 From: Ching L Date: Fri, 7 Mar 2025 11:22:40 +0800 Subject: [PATCH] feat: Add daily rank tracking for Discord friends TUN-143 --- discord_bot.py | 28 +++++++++++++++++++++++++++ dota.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/discord_bot.py b/discord_bot.py index d9f211c..2367565 100644 --- a/discord_bot.py +++ b/discord_bot.py @@ -7,6 +7,8 @@ import utils import sentry_sdk +import datetime + sentry_sdk.init( dsn="https://272f1e4ecb3847ac8d24be796515e558@o4506942768021504.ingest.us.sentry.io/4506986058743808", ) @@ -127,10 +129,36 @@ async def activate_friend(ctx, steam_id): async def heartbeat(): utils.heartbeat() +@tasks.loop(hours=24) +async def check_rank_changes(channel): + logger.info("Checking for rank changes") + try: + data = await dota.check_rank_changes_for_discord() + if data: + logger.info(f"Sending rank changes: {data}") + await channel.send(content=data['content'], embeds=[discord.Embed.from_dict(embed) for embed in data['embeds']]) + except Exception as e: + logger.error(f"Error checking rank changes: {e}") + sentry_sdk.capture_exception(e) + @bot.event async def on_ready(): channel = bot.get_channel(channel_id) send_message.start(channel) heartbeat.start() + + # Start the rank check task and make it run at a specific time (e.g., noon) + check_rank_changes.start(channel) + + # Calculate time until next noon + now = datetime.datetime.now() + target_time = now.replace(hour=21, minute=0, second=0, microsecond=0) + if now >= target_time: + target_time = target_time + datetime.timedelta(days=1) + + # Adjust first run to happen at noon + seconds_until_target = (target_time - now).total_seconds() + check_rank_changes.change_interval(hours=24) + check_rank_changes.next_iteration = discord.utils.utcnow() + datetime.timedelta(seconds=seconds_until_target) bot.run('MTE1MjE2NTc3NDMwNDIyMzI2Mg.GEi-17.VvuIkRy_cFD9XF6wtTagY95LKEbTxKaxy-FxGw') # 这里替换成你自己的 token diff --git a/dota.py b/dota.py index 3d01315..1228acd 100644 --- a/dota.py +++ b/dota.py @@ -90,6 +90,7 @@ class Friend(BaseModel): steam_id = peewee.IntegerField(primary_key=True) name = peewee.CharField() active = peewee.BooleanField(default=True) + rank_tier = peewee.IntegerField(null=True) def get_recent_matches(self, limit=1): try: @@ -98,6 +99,20 @@ class Friend(BaseModel): logger.error('fail to get player %s recent matches. error: %s' % (self.steam_id, e)) return [] + def update_rank_tier(self): + """Update player's rank tier from OpenDota API""" + try: + player_info = player_client.get_players_by_account_id(self.steam_id) + if player_info and hasattr(player_info, 'rank_tier') and player_info.rank_tier: + old_rank_tier = self.rank_tier + self.rank_tier = player_info.rank_tier + self.save() + return old_rank_tier != self.rank_tier, old_rank_tier + return False, None + except Exception as e: + logger.error(f'Failed to update rank tier for player {self.steam_id}. Error: {e}') + return False, None + def serialize_recent_matches(self, limit=1): matches = self.get_recent_matches(limit=limit) data = [] @@ -369,3 +384,40 @@ async def serialize_match_for_discord(match_): } return data + + +async def check_rank_changes_for_discord(): + """Check for rank changes among all active friends and format for Discord""" + rank_changes = [] + + for friend in Friend.filter(active=True): + changed, old_rank_tier = friend.update_rank_tier() + if changed and friend.rank_tier is not None: + old_rank = utils.get_ranking(old_rank_tier) if old_rank_tier else "未校准" + new_rank = utils.get_ranking(friend.rank_tier) + rank_changes.append({ + 'name': friend.name, + 'old_rank': old_rank, + 'new_rank': new_rank, + 'increased': friend.rank_tier > (old_rank_tier or 0) + }) + + if not rank_changes: + return None + + data = { + 'content': '## 天梯更新', + 'embeds': [] + } + + for change in rank_changes: + direction = "⬆️ 上升" if change['increased'] else "⬇️ 下降" + color = 6732650 if change['increased'] else 16724787 # Green if increased, red if decreased + + data['embeds'].append({ + 'title': f"{change['name']} 的天梯等级变化", + 'description': f"{direction}:{change['old_rank']} → {change['new_rank']}", + 'color': color + }) + + return data