Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b61872f298 | ||
|
|
adcf1478d5 | ||
|
|
58eb406c07 | ||
|
|
1e939a0f22 | ||
|
|
53d2103c4e | ||
|
|
ecc2f9c890 | ||
|
|
611bae666b | ||
|
|
95a8bd5352 | ||
|
|
5c4b0a5896 | ||
|
|
e30de01f8c | ||
|
|
203d30a8c6 | ||
|
|
864c3ddce1 |
47
.drone.yml
Normal file
47
.drone.yml
Normal 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
|
||||||
@ -5,6 +5,12 @@ from loguru import logger
|
|||||||
import dota
|
import dota
|
||||||
import utils
|
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')
|
# formatter = logging.Formatter('%(levelname)s %(name)s %(asctime)s %(message)s', '%Y-%m-%d %H:%M:%S')
|
||||||
# log_handler = logging.FileHandler(utils.logger_file)
|
# log_handler = logging.FileHandler(utils.logger_file)
|
||||||
@ -38,7 +44,10 @@ async def send_message(channel):
|
|||||||
for match_ in matches:
|
for match_ in matches:
|
||||||
data = dota.serialize_match_for_discord(match_)
|
data = dota.serialize_match_for_discord(match_)
|
||||||
logger.info(f"sending match {match_['match_id']}, {data}")
|
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']])
|
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
|
@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"
|
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} 的信息')
|
await ctx.respond(content=f'找不到 {name} 的信息')
|
||||||
return
|
return
|
||||||
data = dota.Friend.serialize_recent_matches_for_discord(friends, match_count)
|
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']])
|
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')
|
@bot.command(description='获取朋友', name='list_friends')
|
||||||
async def get_friends(ctx):
|
async def get_friends(ctx):
|
||||||
|
|||||||
41
dota.py
41
dota.py
@ -61,6 +61,7 @@ class Match(BaseModel):
|
|||||||
'radiant_score': match_.radiant_score,
|
'radiant_score': match_.radiant_score,
|
||||||
# isoformat utc+8
|
# isoformat utc+8
|
||||||
'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_.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),
|
'duration': '%d:%02d:%02d' % utils.convert_seconds_to_hms(match_.duration),
|
||||||
'radiant_win': match_.radiant_win,
|
'radiant_win': match_.radiant_win,
|
||||||
'party_size': self.party_size,
|
'party_size': self.party_size,
|
||||||
@ -97,6 +98,7 @@ class Friend(BaseModel):
|
|||||||
'assists': match_.assists,
|
'assists': match_.assists,
|
||||||
'party_size': match_.party_size,
|
'party_size': match_.party_size,
|
||||||
'start_time': match_.start_time,
|
'start_time': match_.start_time,
|
||||||
|
'end_time': match_.start_time + match_.duration,
|
||||||
'duration': match_.duration,
|
'duration': match_.duration,
|
||||||
'average_rank': utils.get_ranking(match_.average_rank),
|
'average_rank': utils.get_ranking(match_.average_rank),
|
||||||
'hero_id': match_.hero_id,
|
'hero_id': match_.hero_id,
|
||||||
@ -122,7 +124,12 @@ class Friend(BaseModel):
|
|||||||
if limit > 10:
|
if limit > 10:
|
||||||
limit = 10
|
limit = 10
|
||||||
for friend in friends:
|
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
|
# sort matches by start_time from latest to oldest
|
||||||
matches.sort(key=lambda x: x['start_time'], reverse=True)
|
matches.sort(key=lambda x: x['start_time'], reverse=True)
|
||||||
name = friends[0].name
|
name = friends[0].name
|
||||||
@ -142,14 +149,15 @@ class Friend(BaseModel):
|
|||||||
if match_['average_rank']:
|
if match_['average_rank']:
|
||||||
summary += '\n' + 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
|
hero_name = Hero.get(hero_id=match_['hero_id']).localized_name
|
||||||
data['embeds'].append({
|
data['embeds'].append({
|
||||||
'title': f"{hero_name} {match_['kills']} 杀 {match_['deaths']} 死 {match_['assists']} 助 ",
|
'title': f"{hero_name} {match_['kills']} 杀 {match_['deaths']} 死 {match_['assists']} 助 ",
|
||||||
'description': summary,
|
'description': summary,
|
||||||
'color': 6732650 if match_['win'] else 16724787, # 66bb6a or FF3333
|
'color': 6732650 if match_['win'] else 16724787, # 66bb6a or FF3333
|
||||||
'fields': [],
|
'fields': [],
|
||||||
'timestamp': start_time,
|
'timestamp': end_time,
|
||||||
'url': f"https://www.opendota.com/matches/{match_['match_id']}",
|
'url': f"https://www.opendota.com/matches/{match_['match_id']}",
|
||||||
})
|
})
|
||||||
return data
|
return data
|
||||||
@ -217,8 +225,11 @@ def serialize_match_for_discord(match_):
|
|||||||
break
|
break
|
||||||
win = is_radiant == match_['radiant_win']
|
win = is_radiant == match_['radiant_win']
|
||||||
if not match_['party_size']:
|
if not match_['party_size']:
|
||||||
summary = f"??黑 {match_['duration']}"
|
if Match.filter(match_id=match_['match_id']).exists():
|
||||||
elif match_['party_size'] > 1:
|
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']}"
|
summary = f"{match_['party_size']}黑 {match_['duration']}"
|
||||||
else:
|
else:
|
||||||
summary = f"单排 {match_['duration']}"
|
summary = f"单排 {match_['duration']}"
|
||||||
@ -235,7 +246,11 @@ def serialize_match_for_discord(match_):
|
|||||||
dire_highest_damage_idx = 0
|
dire_highest_damage_idx = 0
|
||||||
|
|
||||||
for player in match_['players']:
|
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']:
|
if player['is_radiant']:
|
||||||
radiant.append(desc)
|
radiant.append(desc)
|
||||||
@ -254,10 +269,10 @@ def serialize_match_for_discord(match_):
|
|||||||
dire_highest_damage = player['hero_damage']
|
dire_highest_damage = player['hero_damage']
|
||||||
dire_highest_damage_idx = len(dire) - 1
|
dire_highest_damage_idx = len(dire) - 1
|
||||||
|
|
||||||
radiant[radiant_highest_gold_idx] = radiant[radiant_highest_gold_idx] + '💰'
|
radiant[radiant_highest_gold_idx] = '💰' + radiant[radiant_highest_gold_idx]
|
||||||
radiant[radiant_highest_damage_idx] = radiant[radiant_highest_damage_idx] + '🩸'
|
radiant[radiant_highest_damage_idx] = '🩸' + radiant[radiant_highest_damage_idx]
|
||||||
dire[dire_highest_gold_idx] = dire[dire_highest_gold_idx] + '💰'
|
dire[dire_highest_gold_idx] = '💰' + dire[dire_highest_gold_idx]
|
||||||
dire[dire_highest_damage_idx] = dire[dire_highest_damage_idx] + '🩸'
|
dire[dire_highest_damage_idx] = '🩸'+ dire[dire_highest_damage_idx]
|
||||||
|
|
||||||
color = 6732650 if win else 16724787 # 66bb6a or FF3333
|
color = 6732650 if win else 16724787 # 66bb6a or FF3333
|
||||||
|
|
||||||
@ -265,7 +280,7 @@ def serialize_match_for_discord(match_):
|
|||||||
radiant_indicator = ''
|
radiant_indicator = ''
|
||||||
dire_indicator = ''
|
dire_indicator = ''
|
||||||
if is_radiant:
|
if is_radiant:
|
||||||
radiant_indicator = ' 🌟'
|
radiant_indicator = '🌟 '
|
||||||
else:
|
else:
|
||||||
dire_indicator = ' 🌟'
|
dire_indicator = ' 🌟'
|
||||||
|
|
||||||
@ -277,7 +292,7 @@ def serialize_match_for_discord(match_):
|
|||||||
"color": color,
|
"color": color,
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"name": "天辉" + radiant_indicator,
|
"name": radiant_indicator + "天辉",
|
||||||
"value": match_['radiant_score'],
|
"value": match_['radiant_score'],
|
||||||
"inline": True
|
"inline": True
|
||||||
},
|
},
|
||||||
@ -295,7 +310,7 @@ def serialize_match_for_discord(match_):
|
|||||||
"name": "opendota",
|
"name": "opendota",
|
||||||
"url": "https://www.opendota.com/matches/%s" % match_['match_id']
|
"url": "https://www.opendota.com/matches/%s" % match_['match_id']
|
||||||
},
|
},
|
||||||
"timestamp": match_['start_time']
|
"timestamp": match_['end_time']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
110
draw_result.py
Normal file
110
draw_result.py
Normal 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")
|
||||||
@ -3,8 +3,8 @@ import dota
|
|||||||
def serialize_player(player):
|
def serialize_player(player):
|
||||||
friend = dota.Friend.get_or_none(steam_id=player.account_id)
|
friend = dota.Friend.get_or_none(steam_id=player.account_id)
|
||||||
player_data = {
|
player_data = {
|
||||||
'personaname': player.personaname,
|
'personaname': player.personaname if player.personaname else '',
|
||||||
'nickname': friend.name if friend else None,
|
'nickname': friend.name if friend else '',
|
||||||
'kills': player.kills,
|
'kills': player.kills,
|
||||||
'deaths': player.deaths,
|
'deaths': player.deaths,
|
||||||
'assists': player.assists,
|
'assists': player.assists,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user