Compare commits

...

12 Commits

6 changed files with 204 additions and 17 deletions

47
.drone.yml Normal file
View File

@ -0,0 +1,47 @@
kind: pipeline
type: docker
name: default
steps:
- name: deploy
image: appleboy/drone-ssh
settings:
host:
- 148.135.109.242
username: root
key:
from_secret: ssh_key
passphrase:
from_secret: ssh_passphrase
port: 22
command_timeout: 2m
script:
- echo "Go to the project directory"
- cd /root/develop/discord-dota-bot
- echo "Pull the latest code"
- git pull
- echo "Restart service"
- supervisorctl restart dotabot
script_stop: true
- name: discord notification
image: appleboy/drone-discord
settings:
webhook_id:
from_secret: discord_webhook_id
webhook_token:
from_secret: discord_webhook_token
# message: |
# Drone Build #${DRONE_BUILD_NUMBER} ${DRONE_BUILD_STATUS}
# Project: ${DRONE_REPO_NAME}
# Branch: ${DRONE_BRANCH}
# Commit: ${DRONE_COMMIT_SHA:0:8}
# [Build Log](${DRONE_BUILD_LINK})
when:
status: [success, failure]
volumes:
- name: dockersock
host:
path: /var/run/docker.sock

BIN
arial.ttf Normal file

Binary file not shown.

View File

@ -5,6 +5,12 @@ from loguru import logger
import dota
import utils
import sentry_sdk
sentry_sdk.init(
dsn="https://272f1e4ecb3847ac8d24be796515e558@o4506942768021504.ingest.us.sentry.io/4506986058743808",
)
# formatter = logging.Formatter('%(levelname)s %(name)s %(asctime)s %(message)s', '%Y-%m-%d %H:%M:%S')
# log_handler = logging.FileHandler(utils.logger_file)
@ -38,7 +44,10 @@ async def send_message(channel):
for match_ in matches:
data = dota.serialize_match_for_discord(match_)
logger.info(f"sending match {match_['match_id']}, {data}")
try:
await channel.send(content=data['content'], embeds=[discord.Embed.from_dict(embed) for embed in data['embeds']])
except Exception as e:
logger.error(f"send match error {e}")
@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"
@ -50,7 +59,13 @@ async def get_friends_recent_matches(ctx, name, match_count=5): # a slash comman
await ctx.respond(content=f'找不到 {name} 的信息')
return
data = dota.Friend.serialize_recent_matches_for_discord(friends, match_count)
if not data:
await ctx.respond(content=f'找不到 {name} 的战绩')
return
try:
await ctx.respond(content=data['content'], embeds=[discord.Embed.from_dict(embed) for embed in data['embeds']])
except Exception as e:
logger.error(f"send recent_matches error {e}")
@bot.command(description='获取朋友', name='list_friends')
async def get_friends(ctx):

39
dota.py
View File

@ -61,6 +61,7 @@ class Match(BaseModel):
'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'),
'end_time': datetime.datetime.fromtimestamp(match_.start_time + match_.duration).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,
@ -97,6 +98,7 @@ class Friend(BaseModel):
'assists': match_.assists,
'party_size': match_.party_size,
'start_time': match_.start_time,
'end_time': match_.start_time + match_.duration,
'duration': match_.duration,
'average_rank': utils.get_ranking(match_.average_rank),
'hero_id': match_.hero_id,
@ -122,7 +124,12 @@ class Friend(BaseModel):
if limit > 10:
limit = 10
for friend in friends:
matches.extend(friend.serialize_recent_matches(limit=limit))
matches_ = friend.serialize_recent_matches(limit=limit)
if not matches_:
continue
matches.extend(matches_)
if not matches:
return None
# sort matches by start_time from latest to oldest
matches.sort(key=lambda x: x['start_time'], reverse=True)
name = friends[0].name
@ -142,14 +149,15 @@ class Friend(BaseModel):
if match_['average_rank']:
summary += '\n' + match_['average_rank']
start_time = datetime.datetime.fromtimestamp(match_['start_time']).strftime('%Y-%m-%dT%H:%M:%S.000+08:00')
# start_time = datetime.datetime.fromtimestamp(match_['start_time']).strftime('%Y-%m-%dT%H:%M:%S.000+08:00')
end_time = datetime.datetime.fromtimestamp(match_['end_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,
'color': 6732650 if match_['win'] else 16724787, # 66bb6a or FF3333
'fields': [],
'timestamp': start_time,
'timestamp': end_time,
'url': f"https://www.opendota.com/matches/{match_['match_id']}",
})
return data
@ -217,8 +225,11 @@ def serialize_match_for_discord(match_):
break
win = is_radiant == match_['radiant_win']
if not match_['party_size']:
summary = f"??黑 {match_['duration']}"
elif match_['party_size'] > 1:
if Match.filter(match_id=match_['match_id']).exists():
match_['party_size'] = Match.get(match_id=match_['match_id']).party_size
else:
summary = f"{match_['duration']}"
if match_['party_size'] > 1:
summary = f"{match_['party_size']}{match_['duration']}"
else:
summary = f"单排 {match_['duration']}"
@ -235,7 +246,11 @@ 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']}** 助 **{utils.shorten_digits(player['total_gold'])}** 经济 **{utils.shorten_digits(player['hero_damage'])}** 伤害 "
player_name = player['personaname']
if player['nickname']:
player_name = f"**{player['nickname']}**"
desc = f"{player_name}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)
@ -254,10 +269,10 @@ def serialize_match_for_discord(match_):
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] + '🩸'
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
@ -277,7 +292,7 @@ def serialize_match_for_discord(match_):
"color": color,
"fields": [
{
"name": "天辉" + radiant_indicator,
"name": radiant_indicator + "天辉",
"value": match_['radiant_score'],
"inline": True
},
@ -295,7 +310,7 @@ def serialize_match_for_discord(match_):
"name": "opendota",
"url": "https://www.opendota.com/matches/%s" % match_['match_id']
},
"timestamp": match_['start_time']
"timestamp": match_['end_time']
}
],
}

110
draw_result.py Normal file
View File

@ -0,0 +1,110 @@
from PIL import Image, ImageDraw, ImageFont
icon_width = 256
icon_height = 144
banner_height = 70
# 创建空白画布
width = icon_width * 5
height = icon_height * 2 * 5 + banner_height
background_color = (255, 255, 255)
image = Image.new("RGB", (width, height), background_color)
draw = ImageDraw.Draw(image)
# 定义字体和颜色
font_color = (0, 0, 0)
font = ImageFont.truetype("arial.ttf", 34)
# 定义玩家信息的位置
player_info_x = 0
player_info_y = banner_height
player_info_2_x = player_info_x + icon_width
player_info_2_x_end = int(player_info_2_x + icon_width * 1.5)
lv_height = int(icon_height / 2)
# 添加天辉标题
radiant_color = "#66bb6a"
draw.rectangle([0, 0, player_info_2_x_end, 70], fill=radiant_color)
draw.text((20, 20), "天辉", font=font, fill=font_color)
# 添加玩家信息
for i in range(5):
# 左侧等级信息
draw.rectangle([player_info_x, player_info_y, player_info_2_x, player_info_y + lv_height], fill="#808080")
draw.text((player_info_x + 10, player_info_y + 10), "LV. 25", font=font, fill=font_color)
# 左侧头像信息
icon = Image.open("hero_img/muerta.png")
# icon = icon.resize((144, 256))
image.paste(icon, (player_info_x, player_info_y+lv_height))
# 左侧KDA信息
draw.rectangle([player_info_x, player_info_y+lv_height+icon_height, player_info_2_x, player_info_y+lv_height+icon_height+lv_height], fill="#808080")
draw.text((player_info_x + 10, player_info_y+lv_height+icon_height + 20), "10/0/10", font=font, fill=font_color)
# 右侧玩家名称
draw.rectangle([player_info_2_x, player_info_y, player_info_2_x_end, player_info_y + lv_height], fill="#808080")
draw.text((player_info_2_x, player_info_y + 20), "muerta", font=font, fill=font_color)
# 右侧金钱信息
draw.rectangle([player_info_2_x, player_info_y + lv_height, player_info_2_x_end, player_info_y + lv_height+lv_height], fill="#808080")
draw.text((player_info_2_x, player_info_y + lv_height + 20), "金钱10000", font=font, fill=font_color)
# 右侧伤害信息
draw.rectangle([player_info_2_x, player_info_y + lv_height*2, player_info_2_x_end, player_info_y + lv_height*3], fill="#808080")
draw.text((player_info_2_x, player_info_y + lv_height*2 +20), "伤害23456", font=font, fill=font_color)
# 右侧段位信息
draw.rectangle([player_info_2_x, player_info_y + lv_height*3, player_info_2_x_end, player_info_y + lv_height*4], fill="#808080")
draw.text((player_info_2_x, player_info_y + lv_height*3 +20), "中军 3", font=font, fill=font_color)
player_info_y += lv_height * 2 + icon_height
# 添加夜魇标题
dire_color = "#f44336"
draw.rectangle([player_info_2_x_end, 0, width, 70], fill=dire_color)
draw.text((player_info_2_x_end + 20,20), "夜魇", font=font, fill=(255, 255, 255))
player_info_x = int(width / 2)
player_info_y = 70
player_info_2_x = player_info_x + icon_width
player_info_2_x_end = player_info_2_x + icon_width * 1.5
lv_height = 72
# 添加玩家信息
for i in range(5):
# 左侧等级信息
draw.rectangle([player_info_x, player_info_y, player_info_2_x, player_info_y + lv_height], fill="#808080")
draw.text((player_info_x + 10, player_info_y + 10), "LV. 25", font=font, fill=font_color)
# 左侧头像信息
icon = Image.open("hero_img/muerta.png")
# icon = icon.resize((144, 256))
image.paste(icon, (player_info_x, player_info_y+lv_height))
# 左侧KDA信息
draw.rectangle([player_info_x, player_info_y+lv_height+icon_height, player_info_2_x, player_info_y+lv_height+icon_height+lv_height], fill="#808080")
draw.text((player_info_x + 10, player_info_y+lv_height+icon_height + 20), "10/0/10", font=font, fill=font_color)
# 右侧玩家名称
draw.rectangle([player_info_2_x, player_info_y, player_info_2_x_end, player_info_y + lv_height], fill="#808080")
draw.text((player_info_2_x, player_info_y + 20), "muerta", font=font, fill=font_color)
# 右侧金钱信息
draw.rectangle([player_info_2_x, player_info_y + lv_height, player_info_2_x_end, player_info_y + lv_height+lv_height], fill="#808080")
draw.text((player_info_2_x, player_info_y + lv_height + 20), "金钱10000", font=font, fill=font_color)
# 右侧伤害信息
draw.rectangle([player_info_2_x, player_info_y + lv_height*2, player_info_2_x_end, player_info_y + lv_height*3], fill="#808080")
draw.text((player_info_2_x, player_info_y + lv_height*2 +20), "伤害23456", font=font, fill=font_color)
# 右侧段位信息
draw.rectangle([player_info_2_x, player_info_y + lv_height*3, player_info_2_x_end, player_info_y + lv_height*4], fill="#808080")
draw.text((player_info_2_x, player_info_y + lv_height*3 +20), "中军 3", font=font, fill=font_color)
player_info_y += lv_height * 2 + icon_height
# 保存图片
image.save("dota_match_result.png")

View File

@ -3,8 +3,8 @@ import dota
def serialize_player(player):
friend = dota.Friend.get_or_none(steam_id=player.account_id)
player_data = {
'personaname': player.personaname,
'nickname': friend.name if friend else None,
'personaname': player.personaname if player.personaname else '',
'nickname': friend.name if friend else '',
'kills': player.kills,
'deaths': player.deaths,
'assists': player.assists,