diff --git a/discord_bot.py b/discord_bot.py index 4ae92e8..8295ce3 100644 --- a/discord_bot.py +++ b/discord_bot.py @@ -40,6 +40,18 @@ async def send_message(channel): logger.info(f"sending match {match_['match_id']}, {data}") await channel.send(content=data['content'], embeds=[discord.Embed.from_dict(embed) for embed in data['embeds']]) +@bot.command(description="获取最近战绩", name='recent_matches') # this decorator makes a slash command +async def get_friends_recent_matches(ctx, name, match_count=5): # a slash command will be created with the name "ping" + logger.info(f"get_friends_recent_matches {name} {match_count}") + friends = dota.Friend.filter(name=name) + match_count = int(match_count) + if friends.count() == 0: + await ctx.respond(content=f'找不到 {name} 的信息') + return + data = dota.Friend.serialize_recent_matches_for_discord(friends, match_count) + await ctx.respond(content=data['content'], embeds=[discord.Embed.from_dict(embed) for embed in data['embeds']]) + + @tasks.loop(minutes=1) async def heartbeat(): utils.heartbeat() diff --git a/dota.py b/dota.py index 0211dad..1417ebb 100644 --- a/dota.py +++ b/dota.py @@ -75,13 +75,77 @@ class Friend(BaseModel): steam_id = peewee.IntegerField(primary_key=True) name = peewee.CharField() - def get_recent_matches(self): + def get_recent_matches(self, limit=1): try: - return player_client.get_players_by_account_id_select_matches(self.steam_id, limit=1) + return player_client.get_players_by_account_id_select_matches(self.steam_id, limit=limit) except: logger.error('fail to get player %s recent matches' % self.steam_id) return [] + def serialize_recent_matches(self, limit=1): + matches = self.get_recent_matches(limit=limit) + data = [] + for match_ in matches: + data.append({ + 'match_id': match_.match_id, + 'win': match_.radiant_win == (match_.player_slot < 128), + 'is_radiant': match_.player_slot < 128, + 'kills': match_.kills, + 'deaths': match_.deaths, + 'assists': match_.assists, + 'party_size': match_.party_size, + 'start_time': match_.start_time, + 'duration': match_.duration, + 'average_rank': utils.get_ranking(match_.average_rank), + 'hero_id': match_.hero_id, + }) + return data + + @classmethod + def serialize_recent_matches_for_discord(cls, friends, limit=5): + # { + # "content": "## 水哥的战报\n", + # "embeds": [ + # { + # "description": "3黑 00:34:23", + # "fields": [], + # "title": "2 杀 5 死 3 助 ", + # "color": 6732650, + # "url": "https://www.opendota.com/matches/7335993790", + # "timestamp": "2023-09-12T16:00:00.000Z" + # } + # ], + # } + matches = [] + if limit > 10: + limit = 10 + for friend in friends: + matches.extend(friend.serialize_recent_matches(limit=limit)) + # sort matches by start_time from latest to oldest + matches.sort(key=lambda x: x['start_time'], reverse=True) + name = friends[0].name + data = { + 'content': f'## {name}的战报', + 'embeds': [], + } + for match_ in matches[:limit]: + duration = '%d:%02d:%02d' % utils.convert_seconds_to_hms(match_['duration']) + if match_['party_size'] > 1: + summary = f"{match_['party_size']}黑 {duration}" + else: + summary = f"单排 {duration}" + start_time = datetime.datetime.fromtimestamp(match_['start_time']).strftime('%Y-%m-%dT%H:%M:%S.000+08:00') + hero_name = Hero.get(hero_id=match_['hero_id']).localized_name + data['embeds'].append({ + 'title': f"{hero_name} {match_['kills']} 杀 {match_['deaths']} 死 {match_['assists']} 助 ", + 'description': summary + '\n' + match_['average_rank'], + 'color': 6732650 if match_['win'] else 16724787, # 66bb6a or FF3333 + 'fields': [], + 'timestamp': start_time, + 'url': f"https://www.opendota.com/matches/{match_['match_id']}", + }) + return data + def get_friends_recent_matches(): matches = [] @@ -98,17 +162,6 @@ def get_friends_recent_matches(): matches.append(match_obj.serialize_match()) return matches -def shorten_digits(num): - # if num like 12345, return 1.2w - # if num like 1234, return 1.2k - # if num < 1000, return num - # if result ends with 0, remove it - if num >= 10000: - return '%.1fw' % (num / 10000) - elif num >= 1000: - return '%.1fk' % (num / 1000) - else: - return str(num) def serialize_match_for_discord(match_): # { @@ -171,7 +224,7 @@ def serialize_match_for_discord(match_): dire_highest_damage_idx = 0 for player in match_['players']: - desc = f"{player['nickname'] or player['personaname']}(Lv.**{player['level']}** {player['hero']}): **{player['kills']}** 杀 **{player['deaths']}** 死 **{player['assists']}** 助 | **{shorten_digits(player['total_gold'])}** 经济 | **{shorten_digits(player['hero_damage'])}** 伤害 " + desc = f"{player['nickname'] or player['personaname']}(Lv.**{player['level']}** {player['hero']}): **{player['kills']}** 杀 **{player['deaths']}** 死 **{player['assists']}** 助 | **{utils.shorten_digits(player['total_gold'])}** 经济 | **{utils.shorten_digits(player['hero_damage'])}** 伤害 " if player['is_radiant']: radiant.append(desc) diff --git a/utils.py b/utils.py index f4bab01..3f34e29 100644 --- a/utils.py +++ b/utils.py @@ -30,3 +30,39 @@ def heartbeat(): except: logger.error('fail to heartbeat, resp status: %s' % resp.status_code) pass + + +def get_ranking(ranking_int): + # (10-15: Herald, 20-25: Guardian, 30-35: Crusader, 40-45: Archon, 50-55: Legend, 60-65: Ancient, 70-75: Divine, 80-85: Immortal). + stars = ranking_int % 10 + if ranking_int < 20: + return '先锋 %s' % stars + elif ranking_int < 30: + return '卫士 %s' % stars + elif ranking_int < 40: + return '中军 %s' % stars + elif ranking_int < 50: + return '统帅 %s' % stars + elif ranking_int < 60: + return '传奇 %s' % stars + elif ranking_int < 70: + return '万古流芳 %s' % stars + elif ranking_int < 80: + return '超凡入圣 %s' % stars + elif ranking_int < 90: + return '不朽 %s' % stars + else: + return 'Unknown %s' % stars + + +def shorten_digits(num): + # if num like 12345, return 1.2w + # if num like 1234, return 1.2k + # if num < 1000, return num + # if result ends with 0, remove it + if num >= 10000: + return '%.1fw' % (num / 10000) + elif num >= 1000: + return '%.1fk' % (num / 1000) + else: + return str(num)