292 lines
9.7 KiB
Python
292 lines
9.7 KiB
Python
import peewee
|
||
import opendota
|
||
import datetime
|
||
import logging
|
||
|
||
import players
|
||
import utils
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
db = peewee.SqliteDatabase('dota.db')
|
||
hero_client = opendota.HeroesApi()
|
||
player_client = opendota.PlayersApi()
|
||
match_client = opendota.MatchesApi()
|
||
|
||
|
||
class BaseModel(peewee.Model):
|
||
class Meta:
|
||
database = db
|
||
|
||
|
||
class Hero(BaseModel):
|
||
hero_id = peewee.IntegerField(primary_key=True)
|
||
name = peewee.CharField()
|
||
localized_name = peewee.CharField()
|
||
primary_attr = peewee.CharField()
|
||
attack_type = peewee.CharField()
|
||
roles = peewee.CharField()
|
||
|
||
@classmethod
|
||
def fetch_heroes(cls):
|
||
heroes = hero_client.get_heroes()
|
||
for hero in heroes:
|
||
cls.get_or_create(
|
||
hero_id=hero.id,
|
||
defaults={
|
||
'name': hero.name,
|
||
'localized_name': hero.localized_name,
|
||
'primary_attr': hero.primary_attr,
|
||
'attack_type': hero.attack_type,
|
||
'roles': ','.join(hero.roles),
|
||
}
|
||
)
|
||
|
||
|
||
class Match(BaseModel):
|
||
match_id = peewee.IntegerField(primary_key=True)
|
||
start_time = peewee.DateTimeField()
|
||
duration = peewee.IntegerField()
|
||
radiant_win = peewee.BooleanField()
|
||
party_size = peewee.IntegerField()
|
||
|
||
def serialize_match(self):
|
||
try:
|
||
match_ = match_client.get_matches_by_match_id(self.match_id)
|
||
except Exception as e:
|
||
logger.error('fail to get match %s' % self.match_id)
|
||
raise e
|
||
match_data = {
|
||
'players': [players.serialize_player(player) for player in match_.players],
|
||
'dire_score': match_.dire_score,
|
||
'radiant_score': match_.radiant_score,
|
||
# isoformat utc+8
|
||
'start_time': datetime.datetime.fromtimestamp(match_.start_time).strftime('%Y-%m-%dT%H:%M:%S.000+08:00'),
|
||
'duration': '%d:%02d:%02d' % utils.convert_seconds_to_hms(match_.duration),
|
||
'radiant_win': match_.radiant_win,
|
||
'party_size': self.party_size,
|
||
'match_id': self.match_id,
|
||
}
|
||
return match_data
|
||
|
||
|
||
class Friend(BaseModel):
|
||
steam_id = peewee.IntegerField(primary_key=True)
|
||
name = peewee.CharField()
|
||
|
||
def get_recent_matches(self, limit=1):
|
||
try:
|
||
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 = []
|
||
for friend in Friend.select():
|
||
for match_ in friend.get_recent_matches():
|
||
if not Match.select().where(Match.match_id == match_.match_id).exists():
|
||
match_obj = Match.create(
|
||
match_id=match_.match_id,
|
||
start_time=datetime.datetime.fromtimestamp(match_.start_time),
|
||
duration=match_.duration,
|
||
radiant_win=match_.radiant_win,
|
||
party_size=match_.party_size,
|
||
)
|
||
matches.append(match_obj.serialize_match())
|
||
return matches
|
||
|
||
|
||
def serialize_match_for_discord(match_):
|
||
# {
|
||
# "content": "## 天辉\n\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n\n## 夜魇\n\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n我(LV23 大鱼人): 2 杀 5 死 3 助 | 12345 经济 | 13442 伤害\n",
|
||
# "tts": false,
|
||
# "embeds": [
|
||
# {
|
||
# "id": 652627557,
|
||
# "color": 6732650,
|
||
# "fields": [
|
||
# {
|
||
# "id": 878517961,
|
||
# "name": "天辉",
|
||
# "value": "23",
|
||
# "inline": true
|
||
# },
|
||
# {
|
||
# "id": 159867313,
|
||
# "name": "夜魇 ",
|
||
# "value": "23",
|
||
# "inline": true
|
||
# },
|
||
# {
|
||
# "id": 202767768,
|
||
# "name": "3黑(我, 受风,小金 )00:34:23 赢",
|
||
# "value": ""
|
||
# }
|
||
# ],
|
||
# "author": {
|
||
# "name": "opendota",
|
||
# "url": "https://www.opendota.com/matches/7335993790"
|
||
# },
|
||
# "timestamp": "2022-01-01T13:22:00.000Z"
|
||
# }
|
||
# ],
|
||
# "components": [],
|
||
# "actions": {}
|
||
# }
|
||
party = [player['nickname'] for player in match_['players'] if player['nickname']]
|
||
is_radiant = False
|
||
for player in match_['players']:
|
||
if player['nickname']:
|
||
is_radiant = player['is_radiant']
|
||
break
|
||
win = is_radiant == match_['radiant_win']
|
||
if match_['party_size'] > 1:
|
||
summary = f"{match_['party_size']}黑 {match_['duration']}"
|
||
else:
|
||
summary = f"单排 {match_['duration']}"
|
||
|
||
radiant = []
|
||
dire = []
|
||
radiant_highest_gold = 0
|
||
radiant_highest_gold_idx = 0
|
||
radiant_highest_damage = 0
|
||
radiant_highest_damage_idx = 0
|
||
dire_highest_gold = 0
|
||
dire_highest_gold_idx = 0
|
||
dire_highest_damage = 0
|
||
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']}** 助 | **{utils.shorten_digits(player['total_gold'])}** 经济 | **{utils.shorten_digits(player['hero_damage'])}** 伤害 "
|
||
|
||
if player['is_radiant']:
|
||
radiant.append(desc)
|
||
if radiant_highest_gold < player['total_gold']:
|
||
radiant_highest_gold = player['total_gold']
|
||
radiant_highest_gold_idx = len(radiant) - 1
|
||
if radiant_highest_damage < player['hero_damage']:
|
||
radiant_highest_damage = player['hero_damage']
|
||
radiant_highest_damage_idx = len(radiant) - 1
|
||
else:
|
||
dire.append(desc)
|
||
if dire_highest_gold < player['total_gold']:
|
||
dire_highest_gold = player['total_gold']
|
||
dire_highest_gold_idx = len(dire) - 1
|
||
if dire_highest_damage < player['hero_damage']:
|
||
dire_highest_damage = player['hero_damage']
|
||
dire_highest_damage_idx = len(dire) - 1
|
||
|
||
radiant[radiant_highest_gold_idx] = radiant[radiant_highest_gold_idx] + '💰'
|
||
radiant[radiant_highest_damage_idx] = radiant[radiant_highest_damage_idx] + '🩸'
|
||
dire[dire_highest_gold_idx] = dire[dire_highest_gold_idx] + '💰'
|
||
dire[dire_highest_damage_idx] = dire[dire_highest_damage_idx] + '🩸'
|
||
|
||
color = 6732650 if win else 16724787 # 66bb6a or FF3333
|
||
|
||
content = '## 天辉\n\n' + '\n'.join(radiant) + '\n\n## 夜魇\n\n' + '\n'.join(dire) + '\n'
|
||
radiant_indicator = ''
|
||
dire_indicator = ''
|
||
if is_radiant:
|
||
radiant_indicator = ' 🌟'
|
||
else:
|
||
dire_indicator = ' 🌟'
|
||
|
||
data = {
|
||
"content": content,
|
||
"tts": False,
|
||
"embeds": [
|
||
{
|
||
"color": color,
|
||
"fields": [
|
||
{
|
||
"name": "天辉" + radiant_indicator,
|
||
"value": match_['radiant_score'],
|
||
"inline": True
|
||
},
|
||
{
|
||
"name": "夜魇" + dire_indicator,
|
||
"value": match_['dire_score'],
|
||
"inline": True
|
||
},
|
||
{
|
||
"name": summary,
|
||
"value": f"{','.join(party)}"
|
||
}
|
||
],
|
||
"author": {
|
||
"name": "opendota",
|
||
"url": "https://www.opendota.com/matches/%s" % match_['match_id']
|
||
},
|
||
"timestamp": match_['start_time']
|
||
}
|
||
],
|
||
}
|
||
return data
|