init
6
.gitignore
vendored
@ -150,5 +150,9 @@ cython_debug/
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
.idea/
|
||||
|
||||
*.session
|
||||
config.ini
|
||||
data/
|
||||
resources/player/
|
||||
|
42
ci.py
Normal file
@ -0,0 +1,42 @@
|
||||
from configparser import RawConfigParser
|
||||
from os import mkdir, sep
|
||||
from os.path import exists
|
||||
|
||||
import pyromod.listen
|
||||
from pyrogram import Client
|
||||
from httpx import AsyncClient, get
|
||||
from sqlitedict import SqliteDict
|
||||
|
||||
try:
|
||||
import uvloop
|
||||
uvloop.install()
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# init folders
|
||||
if not exists("data"):
|
||||
mkdir("data")
|
||||
sqlite = SqliteDict(f"data{sep}data.sqlite", autocommit=True)
|
||||
# 读取配置文件
|
||||
config = RawConfigParser()
|
||||
config.read("config.ini")
|
||||
bot_token: str = ""
|
||||
api_id: int = 0
|
||||
api_hash: str = ""
|
||||
channel_id: int = 0
|
||||
admin_id: int = 0
|
||||
bot_token = config.get("basic", "bot_token", fallback=bot_token)
|
||||
channel_id = config.get("basic", "channel_id", fallback=channel_id)
|
||||
admin_id = config.get("basic", "admin_id", fallback=admin_id)
|
||||
api_id = config.get("pyrogram", "api_id", fallback=api_id)
|
||||
api_hash = config.get("pyrogram", "api_hash", fallback=api_hash)
|
||||
guess_time = 30 # 猜语音游戏持续时间
|
||||
""" Init httpx client """
|
||||
# 使用自定义 UA
|
||||
headers = {
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
|
||||
}
|
||||
client = AsyncClient(timeout=10.0, headers=headers)
|
||||
me = get(f"https://api.telegram.org/bot{bot_token}/getme").json()
|
||||
# 初始化客户端
|
||||
app = Client("bot", bot_token=bot_token, api_id=api_id, api_hash=api_hash, plugins={"root": "plugins"})
|
8
config.gen.ini
Normal file
@ -0,0 +1,8 @@
|
||||
[pyrogram]
|
||||
api_id = 12345
|
||||
api_hash = 0123456789abc0123456789abc
|
||||
|
||||
[basic]
|
||||
bot_token = 111:abc
|
||||
channel_id = 0
|
||||
admin_id = 0
|
25
defs/bind.py
Normal file
@ -0,0 +1,25 @@
|
||||
from ci import sqlite
|
||||
|
||||
|
||||
def get_bind_list() -> dict:
|
||||
return sqlite.get("bind", {})
|
||||
|
||||
|
||||
def get_bind_uid(uid: int) -> str:
|
||||
return get_bind_list().get(uid, None)
|
||||
|
||||
|
||||
def set_bind(uid: int, player: str):
|
||||
data = get_bind_list()
|
||||
data[uid] = player
|
||||
sqlite["bind"] = data
|
||||
|
||||
|
||||
def remove_bind(uid: int):
|
||||
data = get_bind_list()
|
||||
data.pop(uid, None)
|
||||
sqlite["bind"] = data
|
||||
|
||||
|
||||
def check_bind(uid: int) -> bool:
|
||||
return get_bind_uid(uid) is not None
|
301
defs/drawCharCard.py
Normal file
@ -0,0 +1,301 @@
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from os import sep
|
||||
import math
|
||||
|
||||
from defs.sources import TEXT_PATH, GACHA_PATH, ICON_PATH, RELIC_PATH
|
||||
|
||||
COLOR_MAP = {"Anemo": (3, 90, 77),
|
||||
"Cryo": (5, 85, 151),
|
||||
"Dendro": (4, 87, 3),
|
||||
"Electro": (47, 1, 85),
|
||||
"Geo": (85, 34, 1),
|
||||
"Hydro": (4, 6, 114),
|
||||
"Pyro": (88, 4, 4)}
|
||||
|
||||
|
||||
def genshin_font_origin(size: int) -> ImageFont:
|
||||
return ImageFont.truetype(str(TEXT_PATH / 'yuanshen_origin.ttf'), size=size)
|
||||
|
||||
|
||||
def get_star_png(star: int) -> Image:
|
||||
png = Image.open(TEXT_PATH / 's-{}.png'.format(str(star)))
|
||||
return png
|
||||
|
||||
|
||||
def str_len(r: str, size: int, limit: int = 540) -> str:
|
||||
result = ''
|
||||
temp = 0
|
||||
for i in r:
|
||||
if temp >= limit:
|
||||
result += '\n' + i
|
||||
temp = 0
|
||||
else:
|
||||
result += i
|
||||
|
||||
if i.isdigit():
|
||||
temp += round(size / 10 * 6)
|
||||
elif i == '/':
|
||||
temp += round(size / 10 * 2.2)
|
||||
elif i == '.':
|
||||
temp += round(size / 10 * 3)
|
||||
elif i == '%':
|
||||
temp += round(size / 10 * 9.4)
|
||||
else:
|
||||
temp += size
|
||||
return result
|
||||
|
||||
|
||||
async def draw_char_card(raw_data: dict) -> str:
|
||||
img = Image.open(TEXT_PATH / '{}.png'.format(raw_data['avatarElement']))
|
||||
char_info_1 = Image.open(TEXT_PATH / 'char_info_1.png')
|
||||
char_imfo_mask = Image.open(TEXT_PATH / 'char_info_mask.png')
|
||||
|
||||
based_w, based_h = 600, 1200
|
||||
try:
|
||||
char_img = Image.open(GACHA_PATH / 'UI_Gacha_AvatarImg_{}.png'.format(raw_data['avatarEnName'])) # 角色图像
|
||||
except FileNotFoundError:
|
||||
char_img = Image.open(GACHA_PATH / 'default.jpg') # 角色图像
|
||||
|
||||
# 确定图片的长宽
|
||||
w, h = char_img.size
|
||||
if (w, h) != (based_w, based_h):
|
||||
offset = 200
|
||||
based_new_w, based_new_h = based_w + offset, based_h + offset
|
||||
based_scale = '%.3f' % (based_new_w / based_new_h)
|
||||
scale_f = '%.3f' % (w / h)
|
||||
new_w = math.ceil(based_new_h * float(scale_f))
|
||||
new_h = math.ceil(based_new_w / float(scale_f))
|
||||
if scale_f > based_scale:
|
||||
bg_img2 = char_img.resize((new_w, based_new_h), Image.Resampling.LANCZOS)
|
||||
char_img = bg_img2.crop(
|
||||
(new_w / 2 - based_new_w / 2 + offset, 0, new_w / 2 + based_new_w / 2, based_new_h - offset))
|
||||
else:
|
||||
bg_img2 = char_img.resize((based_new_w, new_h), Image.Resampling.LANCZOS)
|
||||
char_img = bg_img2.crop(
|
||||
(0 + offset, new_h / 2 - based_new_h / 2, based_new_w, new_h / 2 + based_new_h / 2 - offset))
|
||||
else:
|
||||
pass
|
||||
|
||||
img_temp = Image.new('RGBA', (based_w, based_h), (0, 0, 0, 0))
|
||||
img_temp.paste(char_img, (0, 0), char_imfo_mask)
|
||||
img.paste(img_temp, (0, 0), img_temp)
|
||||
img.paste(char_info_1, (0, 0), char_info_1)
|
||||
|
||||
# holo_img = Image.open(TEXT_PATH / 'icon_holo.png')
|
||||
# skill_holo_img = Image.open(TEXT_PATH / 'skillHolo.png')
|
||||
lock_img = Image.open(TEXT_PATH / 'icon_lock.png')
|
||||
|
||||
# color_soild = Image.new('RGBA', (950, 1850), COLOR_MAP[raw_data['avatarElement']])
|
||||
# img.paste(color_soild, (0, 0), skill_holo_img)
|
||||
|
||||
# color_holo_img = Image.new('RGBA', (100, 100), COLOR_MAP[raw_data['avatarElement']])
|
||||
|
||||
# 命座处理
|
||||
for talent_num in range(0, 6):
|
||||
if talent_num + 1 <= len(raw_data['talentList']):
|
||||
talent = raw_data['talentList'][talent_num]
|
||||
# img.paste(color_holo_img, (13,270 + talent_num * 66), holo_img)
|
||||
talent_img = Image.open(ICON_PATH / '{}.png'.format(talent['talentIcon']))
|
||||
talent_img_new = talent_img.resize((50, 50), Image.Resampling.LANCZOS).convert("RGBA")
|
||||
img.paste(talent_img_new, (850, 375 + talent_num * 81), talent_img_new)
|
||||
else:
|
||||
img.paste(lock_img, (850, 375 + talent_num * 81), lock_img)
|
||||
|
||||
# 天赋处理
|
||||
skillList = raw_data['avatarSkill']
|
||||
a_skill_name = skillList[0]['skillName'].replace('普通攻击·', '')
|
||||
a_skill_level = skillList[0]['skillLevel']
|
||||
e_skill_name = skillList[1]['skillName']
|
||||
e_skill_level = skillList[1]['skillLevel']
|
||||
q_skill_name = skillList[-1]['skillName']
|
||||
q_skill_level = skillList[-1]['skillLevel']
|
||||
for skill_num, skill in enumerate(skillList[0:2] + [skillList[-1]]):
|
||||
skill_img = Image.open(ICON_PATH / '{}.png'.format(skill['skillIcon']))
|
||||
skill_img_new = skill_img.resize((50, 50), Image.Resampling.LANCZOS).convert("RGBA")
|
||||
img.paste(skill_img_new, (78, 756 + 101 * skill_num), skill_img_new)
|
||||
|
||||
# 武器部分
|
||||
weapon_img = Image.open(TEXT_PATH / 'char_info_weapon.png')
|
||||
weapon_star_img = get_star_png(raw_data['weaponInfo']['weaponStar'])
|
||||
weaponName = raw_data['weaponInfo']['weaponName']
|
||||
|
||||
weaponAtk = raw_data['weaponInfo']['weaponStats'][0]['statValue']
|
||||
weaponLevel = raw_data['weaponInfo']['weaponLevel']
|
||||
weaponAffix = raw_data['weaponInfo']['weaponAffix']
|
||||
weaponEffect = raw_data['weaponInfo']['weaponEffect']
|
||||
weapon_type = raw_data['weaponInfo']['weaponType']
|
||||
|
||||
weapon_img.paste(weapon_star_img, (25, 235), weapon_star_img)
|
||||
weapon_text = ImageDraw.Draw(weapon_img)
|
||||
weapon_text.text((35, 80), weaponName, (255, 255, 255), genshin_font_origin(50), anchor='lm')
|
||||
weapon_text.text((35, 120), weapon_type, (255, 255, 255), genshin_font_origin(20), anchor='lm')
|
||||
weapon_text.text((35, 160), '基础攻击力', (255, 255, 255), genshin_font_origin(32), anchor='lm')
|
||||
weapon_text.text((368, 160), str(weaponAtk), (255, 255, 255), genshin_font_origin(32), anchor='rm')
|
||||
if len(raw_data['weaponInfo']['weaponStats']) == 2:
|
||||
weapon_sub_info = raw_data['weaponInfo']['weaponStats'][1]['statName']
|
||||
weapon_sub_value = raw_data['weaponInfo']['weaponStats'][1]['statValue']
|
||||
weapon_text.text((35, 211), weapon_sub_info, (255, 255, 255), genshin_font_origin(32), anchor='lm')
|
||||
weapon_text.text((368, 211), str(weapon_sub_value), (255, 255, 255), genshin_font_origin(32), anchor='rm')
|
||||
else:
|
||||
weapon_text.text((35, 211), '该武器无副词条', (255, 255, 255), genshin_font_origin(32), anchor='lm')
|
||||
weapon_text.text((73, 303), f'Lv.{weaponLevel}', (255, 255, 255), genshin_font_origin(28), anchor='mm')
|
||||
weapon_text.text((130, 305), f'精炼{str(weaponAffix)}阶', (255, 239, 173), genshin_font_origin(28), anchor='lm')
|
||||
|
||||
weaponEffect = str_len(weaponEffect, 25, 455)
|
||||
weapon_text.text((25, 335), weaponEffect, (255, 255, 255), genshin_font_origin(25))
|
||||
img.paste(weapon_img, (387, 570), weapon_img)
|
||||
|
||||
# 圣遗物部分
|
||||
artifactsAllScore = 0
|
||||
for aritifact in raw_data['equipList']:
|
||||
artifacts_img = Image.open(TEXT_PATH / 'char_info_artifacts.png')
|
||||
artifacts_piece_img = Image.open(RELIC_PATH / '{}.png'.format(aritifact['icon']))
|
||||
artifacts_piece_new_img = artifacts_piece_img.resize((180, 180), Image.Resampling.LANCZOS).convert("RGBA")
|
||||
artifacts_piece_new_img.putalpha(
|
||||
artifacts_piece_new_img.getchannel('A').point(lambda x: round(x * 0.5) if x > 0 else 0))
|
||||
|
||||
artifacts_img.paste(artifacts_piece_new_img, (100, 35), artifacts_piece_new_img)
|
||||
aritifactStar_img = get_star_png(aritifact['aritifactStar'])
|
||||
artifactsPos = aritifact['aritifactPieceName']
|
||||
|
||||
artifacts_img.paste(aritifactStar_img, (20, 165), aritifactStar_img)
|
||||
artifacts_text = ImageDraw.Draw(artifacts_img)
|
||||
artifacts_text.text((30, 66), aritifact['aritifactName'], (255, 255, 255), genshin_font_origin(34), anchor='lm')
|
||||
artifacts_text.text((30, 102), artifactsPos, (255, 255, 255), genshin_font_origin(20), anchor='lm')
|
||||
|
||||
mainValue = aritifact['reliquaryMainstat']['statValue']
|
||||
mainName = aritifact['reliquaryMainstat']['statName']
|
||||
mainLevel = aritifact['aritifactLevel']
|
||||
|
||||
if mainName in ['攻击力', '血量', '防御力', '元素精通']:
|
||||
mainValueStr = str(mainValue)
|
||||
else:
|
||||
mainValueStr = str(mainValue) + '%'
|
||||
|
||||
mainNameNew = mainName.replace('百分比', '')
|
||||
|
||||
artifacts_text.text((26, 140), mainNameNew, (255, 255, 255), genshin_font_origin(28), anchor='lm')
|
||||
artifacts_text.text((268, 140), mainValueStr, (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
||||
artifacts_text.text((55, 219), '+{}'.format(str(mainLevel)), (255, 255, 255), genshin_font_origin(24),
|
||||
anchor='mm')
|
||||
|
||||
artifactsScore = 0
|
||||
for index, i in enumerate(aritifact['reliquarySubstats']):
|
||||
subName = i['statName']
|
||||
subValue = i['statValue']
|
||||
if subName in ['攻击力', '血量', '防御力', '元素精通']:
|
||||
subValueStr = str(subValue)
|
||||
if subName == '血量':
|
||||
artifactsScore += subValue * 0.014
|
||||
elif subName == '攻击力':
|
||||
artifactsScore += subValue * 0.12
|
||||
elif subName == '防御力':
|
||||
artifactsScore += subValue * 0.18
|
||||
elif subName == '元素精通':
|
||||
artifactsScore += subValue * 0.25
|
||||
else:
|
||||
subValueStr = str(subValue) + '%'
|
||||
if subName == '暴击率':
|
||||
artifactsScore += subValue * 2
|
||||
elif subName == '暴击伤害':
|
||||
artifactsScore += subValue * 1
|
||||
elif subName == '元素精通':
|
||||
artifactsScore += subValue * 0.25
|
||||
elif subName == '元素充能效率':
|
||||
artifactsScore += subValue * 0.65
|
||||
elif subName == '百分比血量':
|
||||
artifactsScore += subValue * 0.86
|
||||
elif subName == '百分比攻击力':
|
||||
artifactsScore += subValue * 1
|
||||
elif subName == '百分比防御力':
|
||||
artifactsScore += subValue * 0.7
|
||||
artifacts_text.text((20, 263 + index * 30), '·{}+{}'.format(subName, subValueStr), (255, 255, 255),
|
||||
genshin_font_origin(25), anchor='lm')
|
||||
artifactsAllScore += artifactsScore
|
||||
artifacts_text.text((268, 190), f'{math.ceil(artifactsScore)}分', (255, 255, 255), genshin_font_origin(23),
|
||||
anchor='rm')
|
||||
|
||||
if artifactsPos == '生之花':
|
||||
img.paste(artifacts_img, (18, 1075), artifacts_img)
|
||||
elif artifactsPos == '死之羽':
|
||||
img.paste(artifacts_img, (318, 1075), artifacts_img)
|
||||
elif artifactsPos == '时之沙':
|
||||
img.paste(artifacts_img, (618, 1075), artifacts_img)
|
||||
elif artifactsPos == '空之杯':
|
||||
img.paste(artifacts_img, (18, 1447), artifacts_img)
|
||||
elif artifactsPos == '理之冠':
|
||||
img.paste(artifacts_img, (318, 1447), artifacts_img)
|
||||
|
||||
char_name = raw_data['avatarName']
|
||||
char_level = raw_data['avatarLevel']
|
||||
char_fetter = raw_data['avatarFetter']
|
||||
|
||||
# 评分算法
|
||||
# 圣遗物总分 + 角色等级 + (a+e+q)*4 + 武器等级 * ( 1+(武器精炼数 -1) * 0.25)
|
||||
charAllScore = artifactsAllScore + int(char_level) + \
|
||||
(a_skill_level + e_skill_level + q_skill_level) * 4 + \
|
||||
int(weaponLevel) * (1 + ((int(weaponAffix) - 1) * 0.25))
|
||||
|
||||
# 角色基本信息
|
||||
img_text = ImageDraw.Draw(img)
|
||||
img_text.text((411, 72), char_name, (255, 255, 255), genshin_font_origin(55), anchor='lm')
|
||||
img_text.text((411, 122), '等级{}'.format(char_level), (255, 255, 255), genshin_font_origin(40), anchor='lm')
|
||||
img_text.text((747, 126), str(char_fetter), (255, 255, 255), genshin_font_origin(28), anchor='lm')
|
||||
|
||||
# aeq
|
||||
# img_text.text((110, 771), a_skill_name, (255, 255, 255), genshin_font_origin(26), anchor='lm')
|
||||
img_text.text((103, 812), f'{str(a_skill_level)}', (255, 255, 255), genshin_font_origin(30), anchor='mm')
|
||||
|
||||
# img_text.text((110, 872), e_skill_name, (255, 255, 255), genshin_font_origin(26), anchor='lm')
|
||||
img_text.text((103, 915), f'{str(e_skill_level)}', (255, 255, 255), genshin_font_origin(30), anchor='mm')
|
||||
|
||||
# img_text.text((110, 973), q_skill_name, (255, 255, 255), genshin_font_origin(26), anchor='lm')
|
||||
img_text.text((103, 1016), f'{str(q_skill_level)}', (255, 255, 255), genshin_font_origin(30), anchor='mm')
|
||||
|
||||
fight_prop = raw_data['avatarFightProp']
|
||||
hp = fight_prop['hp']
|
||||
attack = fight_prop['atk']
|
||||
defense = fight_prop['def']
|
||||
em = fight_prop['elementalMastery']
|
||||
critrate = fight_prop['critRate']
|
||||
critdmg = fight_prop['critDmg']
|
||||
ce = fight_prop['energyRecharge']
|
||||
dmgBonus = fight_prop['dmgBonus']
|
||||
|
||||
hp_green = fight_prop['addHp']
|
||||
attack_green = fight_prop['addAtk']
|
||||
defense_green = fight_prop['addDef']
|
||||
|
||||
# 属性
|
||||
img_text.text((785, 174), str(round(hp)), (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
||||
img_text.text((785, 227), str(round(attack)), (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
||||
img_text.text((785, 280), str(round(defense)), (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
||||
img_text.text((785, 333), str(round(em)), (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
||||
img_text.text((785, 386), f'{str(round(critrate * 100, 2))}%', (255, 255, 255), genshin_font_origin(28),
|
||||
anchor='rm')
|
||||
img_text.text((785, 439), f'{str(round(critdmg * 100, 2))}%', (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
||||
img_text.text((785, 492), f'{str(round(ce * 100, 1))}%', (255, 255, 255), genshin_font_origin(28), anchor='rm')
|
||||
img_text.text((785, 545), f'{str(round(dmgBonus * 100, 1))}%', (255, 255, 255), genshin_font_origin(28),
|
||||
anchor='rm')
|
||||
|
||||
img_text.text((805, 174), f'(+{str(round(hp_green))})', (95, 251, 80), genshin_font_origin(28), anchor='lm')
|
||||
img_text.text((805, 227), f'(+{str(round(attack_green))})', (95, 251, 80), genshin_font_origin(28), anchor='lm')
|
||||
img_text.text((805, 280), f'(+{str(round(defense_green))})', (95, 251, 80), genshin_font_origin(28), anchor='lm')
|
||||
|
||||
uid = raw_data['playerUid']
|
||||
data_time = raw_data['dataTime']
|
||||
# uid
|
||||
img_text.text((350, 1035), f'UID{uid}', (255, 255, 255), genshin_font_origin(24), anchor='rm')
|
||||
|
||||
# 数据最后更新时间
|
||||
img_text.text((780, 600), f'数据最后更新于{data_time}', (255, 255, 255), genshin_font_origin(22), anchor='rm')
|
||||
|
||||
# 角色评分
|
||||
img_text.text((904, 1505), f'圣遗物总分', (255, 255, 255), genshin_font_origin(45), anchor='rm')
|
||||
img_text.text((904, 1570), f'{round(artifactsAllScore, 1)}', (255, 255, 255), genshin_font_origin(60), anchor='rm')
|
||||
|
||||
img_text.text((904, 1655), f'角色评分', (255, 255, 255), genshin_font_origin(45), anchor='rm')
|
||||
img_text.text((904, 1720), f'{round(charAllScore, 1)}', (255, 255, 255), genshin_font_origin(60), anchor='rm')
|
||||
|
||||
img.save(f"data{sep}{uid}.png", format='PNG', subsampling=0, quality=100)
|
||||
return f"data{sep}{uid}.png"
|
194
defs/enkaToData.py
Normal file
@ -0,0 +1,194 @@
|
||||
from typing import Optional
|
||||
from ci import client, sqlite
|
||||
import json
|
||||
import time
|
||||
|
||||
from defs.sources import PLAYER_PATH, avatarId2Name, avatarName2Element, skillId2Name, talentId2Name, weaponHash2Type, \
|
||||
weaponHash2Name, propId2Name, artifact2attr, artifactId2Piece, icon2Name
|
||||
|
||||
|
||||
async def enkaToData(uid: str) -> Optional[str]:
|
||||
enka_data = await client.get(f'https://enka.shinshin.moe/u/{str(uid)}/__data.json')
|
||||
enka_data = enka_data.json()
|
||||
if not enka_data:
|
||||
return None
|
||||
now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
|
||||
playerInfo = enka_data['playerInfo']
|
||||
path = PLAYER_PATH / str(uid)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
with open(path / '{}.json'.format(str(uid)), 'w', encoding='UTF-8') as file:
|
||||
json.dump(playerInfo, file, ensure_ascii=False)
|
||||
with open(path / 'rawData.json', 'w', encoding='UTF-8') as file:
|
||||
json.dump(enka_data, file, ensure_ascii=False)
|
||||
|
||||
if 'avatarInfoList' not in enka_data:
|
||||
return f'UID{uid}刷新失败!未打开角色展柜!'
|
||||
|
||||
char_name_list = []
|
||||
for char in enka_data['avatarInfoList']:
|
||||
# 处理基本信息
|
||||
char_data = {}
|
||||
avatarId = char['avatarId']
|
||||
char_data['playerUid'] = str(uid)
|
||||
char_data['playerName'] = enka_data['playerInfo']['nickname']
|
||||
char_data['avatarId'] = avatarId
|
||||
avatarName = avatarId2Name[str(char['avatarId'])]
|
||||
char_data['avatarName'] = avatarId2Name[str(char['avatarId'])]
|
||||
char_name_list.append(char_data['avatarName'])
|
||||
char_data['avatarFetter'] = char['fetterInfo']['expLevel']
|
||||
char_data['avatarLevel'] = char['propMap']['4001']['val']
|
||||
|
||||
try:
|
||||
char_data['avatarElement'] = avatarName2Element[char_data['avatarName']]
|
||||
except KeyError:
|
||||
check = skillId2Name['Name'][str(list(char['skillLevelMap'].keys())[0])]
|
||||
if '风' in check:
|
||||
char_data['avatarElement'] = 'Anemo'
|
||||
elif '雷' in check:
|
||||
char_data['avatarElement'] = 'Electro'
|
||||
elif '岩' in check:
|
||||
char_data['avatarElement'] = 'Geo'
|
||||
elif '草' in check:
|
||||
char_data['avatarElement'] = 'Dendro'
|
||||
elif '冰' in check:
|
||||
char_data['avatarElement'] = 'Cryo'
|
||||
elif '水' in check:
|
||||
char_data['avatarElement'] = 'Hydro'
|
||||
else:
|
||||
char_data['avatarElement'] = 'Pyro'
|
||||
|
||||
char_data['dataTime'] = now
|
||||
|
||||
char_data['avatarSkill'] = []
|
||||
# 处理天赋
|
||||
for skill in char['skillLevelMap']:
|
||||
skill_temp = {}
|
||||
skill_temp['skillId'] = skill
|
||||
skill_temp['skillName'] = skillId2Name['Name'][skill_temp['skillId']]
|
||||
skill_temp['skillLevel'] = char['skillLevelMap'][skill]
|
||||
skill_temp['skillIcon'] = skillId2Name['Icon'][skill_temp['skillId']]
|
||||
char_data['avatarSkill'].append(skill_temp)
|
||||
|
||||
if char_data['avatarName'] == '神里绫华':
|
||||
char_data['avatarSkill'][0], char_data['avatarSkill'][-1] = char_data['avatarSkill'][-1], \
|
||||
char_data['avatarSkill'][0]
|
||||
char_data['avatarSkill'][2], char_data['avatarSkill'][-1] = char_data['avatarSkill'][-1], \
|
||||
char_data['avatarSkill'][2]
|
||||
char_data['avatarEnName'] = char_data['avatarSkill'][1]['skillIcon'].split('_')[-2]
|
||||
else:
|
||||
char_data['avatarEnName'] = char_data['avatarSkill'][-1]['skillIcon'].split('_')[-2]
|
||||
|
||||
# 处理命座
|
||||
talent_temp = []
|
||||
if 'talentIdList' in char:
|
||||
for index, talent in enumerate(char['talentIdList']):
|
||||
talentTemp = {}
|
||||
talentTemp['talentId'] = char['talentIdList'][index]
|
||||
talentTemp['talentName'] = talentId2Name['Name'][str(talent)]
|
||||
talentTemp['talentIcon'] = talentId2Name['Icon'][str(talent)]
|
||||
talent_temp.append(talentTemp)
|
||||
char_data['talentList'] = talent_temp
|
||||
|
||||
# 处理属性
|
||||
fight_prop = {}
|
||||
# 血量
|
||||
fight_prop['hp'] = char["fightPropMap"]["2000"]
|
||||
fight_prop['baseHp'] = char["fightPropMap"]["1"]
|
||||
fight_prop['addHp'] = char["fightPropMap"]["2000"] - char["fightPropMap"]["1"]
|
||||
# 攻击力
|
||||
fight_prop['atk'] = char["fightPropMap"]["2001"]
|
||||
fight_prop['baseAtk'] = char["fightPropMap"]["4"]
|
||||
fight_prop['addAtk'] = char["fightPropMap"]["2001"] - char["fightPropMap"]["4"]
|
||||
# 防御力
|
||||
fight_prop['def'] = char["fightPropMap"]["2002"]
|
||||
fight_prop['baseDef'] = char["fightPropMap"]["7"]
|
||||
fight_prop['addDef'] = char["fightPropMap"]["2002"] - char["fightPropMap"]["7"]
|
||||
# 元素精通
|
||||
fight_prop['elementalMastery'] = char["fightPropMap"]["28"]
|
||||
# 暴击率
|
||||
fight_prop['critRate'] = char["fightPropMap"]["20"]
|
||||
# 暴击伤害
|
||||
fight_prop['critDmg'] = char["fightPropMap"]["22"]
|
||||
# 充能效率
|
||||
fight_prop['energyRecharge'] = char["fightPropMap"]["23"]
|
||||
# 治疗&受治疗
|
||||
fight_prop['healBonus'] = char["fightPropMap"]["26"]
|
||||
fight_prop['healedBonus'] = char["fightPropMap"]["27"]
|
||||
# 物理伤害加成 & 抗性
|
||||
fight_prop['physicalDmgSub'] = char["fightPropMap"]["29"]
|
||||
fight_prop['physicalDmgBonus'] = char["fightPropMap"]["30"]
|
||||
# 伤害加成
|
||||
for i in range(40, 47):
|
||||
if char["fightPropMap"][str(i)] > 0:
|
||||
fight_prop['dmgBonus'] = char["fightPropMap"][str(i)]
|
||||
break
|
||||
else:
|
||||
fight_prop['dmgBonus'] = 0
|
||||
|
||||
char_data['avatarFightProp'] = fight_prop
|
||||
|
||||
# 处理武器
|
||||
weapon_info = {}
|
||||
weapon_data = char['equipList'][-1]
|
||||
weapon_info['itemId'] = weapon_data['itemId']
|
||||
weapon_info['nameTextMapHash'] = weapon_data['flat']['nameTextMapHash']
|
||||
weapon_info['weaponIcon'] = weapon_data['flat']['icon']
|
||||
weapon_info['weaponType'] = weaponHash2Type[weapon_info['nameTextMapHash']]
|
||||
weapon_info['weaponName'] = weaponHash2Name[weapon_info['nameTextMapHash']]
|
||||
weapon_info['weaponStar'] = weapon_data['flat']['rankLevel']
|
||||
# 防止未精炼
|
||||
if 'promoteLevel' in weapon_data['weapon']:
|
||||
weapon_info['promoteLevel'] = weapon_data['weapon']['promoteLevel']
|
||||
else:
|
||||
weapon_info['promoteLevel'] = 0
|
||||
weapon_info['weaponLevel'] = weapon_data['weapon']['level']
|
||||
if 'affixMap' in weapon_data['weapon']:
|
||||
weapon_info['weaponAffix'] = list(weapon_data['weapon']['affixMap'].values())[0] + 1
|
||||
else:
|
||||
weapon_info['weaponAffix'] = 1
|
||||
weapon_info['weaponStats'] = []
|
||||
for k in weapon_data['flat']['weaponStats']:
|
||||
weapon_prop_temp = {}
|
||||
weapon_prop_temp['appendPropId'] = k['appendPropId']
|
||||
weapon_prop_temp['statName'] = propId2Name[k['appendPropId']]
|
||||
weapon_prop_temp['statValue'] = k['statValue']
|
||||
weapon_info['weaponStats'].append(weapon_prop_temp)
|
||||
# 武器特效,须请求API
|
||||
effect_raw = await client.get('https://info.minigg.cn/weapons?query={}'.format(weapon_info['weaponName']))
|
||||
effect_raw = effect_raw.json()
|
||||
if 'effect' in effect_raw:
|
||||
effect = effect_raw['effect'].format(*effect_raw['r{}'.format(str(weapon_info['weaponAffix']))])
|
||||
else:
|
||||
effect = '无特效。'
|
||||
weapon_info['weaponEffect'] = effect
|
||||
char_data['weaponInfo'] = weapon_info
|
||||
|
||||
# 处理圣遗物
|
||||
artifacts_info = []
|
||||
artifacts_data = char['equipList'][:-1]
|
||||
for artifact in artifacts_data:
|
||||
artifact_temp = {}
|
||||
artifact_temp['itemId'] = artifact['itemId']
|
||||
artifact_temp['nameTextMapHash'] = artifact['flat']['nameTextMapHash']
|
||||
artifact_temp['icon'] = artifact['flat']['icon']
|
||||
artifact_temp['aritifactName'] = icon2Name[artifact['flat']['icon']]
|
||||
artifact_temp['aritifactSetsName'] = artifact2attr['mapping'].get(artifact_temp['aritifactName'], "")
|
||||
artifact_temp['aritifactSetPiece'] = artifactId2Piece[artifact_temp['icon'].split('_')[-1]][0]
|
||||
artifact_temp['aritifactPieceName'] = artifactId2Piece[artifact_temp['icon'].split('_')[-1]][1]
|
||||
|
||||
artifact_temp['aritifactStar'] = artifact['flat']['rankLevel']
|
||||
artifact_temp['aritifactLevel'] = artifact['reliquary']['level'] - 1
|
||||
|
||||
artifact_temp['reliquaryMainstat'] = artifact['flat']['reliquaryMainstat']
|
||||
artifact_temp['reliquaryMainstat']['statName'] = propId2Name[
|
||||
artifact_temp['reliquaryMainstat']['mainPropId']]
|
||||
|
||||
artifact_temp['reliquarySubstats'] = artifact['flat']['reliquarySubstats']
|
||||
for sub in artifact_temp['reliquarySubstats']:
|
||||
sub['statName'] = propId2Name[sub['appendPropId']]
|
||||
artifacts_info.append(artifact_temp)
|
||||
char_data['equipList'] = artifacts_info
|
||||
with open(path / '{}.json'.format(avatarName), 'w', encoding='UTF-8') as file:
|
||||
json.dump(char_data, file, ensure_ascii=False)
|
||||
char_name_list_str = ','.join(char_name_list)
|
||||
return f'UID {uid} 刷新成功!刷新角色:{char_name_list_str}'
|
102
defs/player.py
Normal file
@ -0,0 +1,102 @@
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
from os import listdir
|
||||
from os.path import exists
|
||||
from typing import List
|
||||
|
||||
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
|
||||
from defs.drawCharCard import draw_char_card
|
||||
from defs.sources import PLAYER_PATH
|
||||
from ci import channel_id, app, sqlite
|
||||
|
||||
|
||||
def gen_char_dict(name: str, file_id: str) -> dict:
|
||||
return {"name": name, "file_id": file_id, "time": int(time.time())}
|
||||
|
||||
|
||||
class Player:
|
||||
name: str = ""
|
||||
uid: str = ""
|
||||
all_char: List[dict] = []
|
||||
time: int = 0
|
||||
|
||||
def __init__(self, uid: str):
|
||||
self.uid = uid
|
||||
self.time = int(time.time())
|
||||
if not exists(PLAYER_PATH / uid / f"{uid}.json"):
|
||||
return
|
||||
with open(PLAYER_PATH / uid / f"{uid}.json", "r", encoding="utf-8") as fp:
|
||||
data = json.load(fp)
|
||||
self.name = data.get("nickname", "")
|
||||
|
||||
def update_name(self):
|
||||
with open(PLAYER_PATH / self.uid / f"{self.uid}.json", "r", encoding="utf-8") as fp:
|
||||
data = json.load(fp)
|
||||
self.name = data.get("nickname", "")
|
||||
|
||||
async def update_char(self):
|
||||
all_char = listdir(PLAYER_PATH / self.uid)
|
||||
try:
|
||||
all_char.remove(f"{self.uid}.json")
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
all_char.remove("rawData.json")
|
||||
except ValueError:
|
||||
pass
|
||||
all_char = [i[:-5] for i in all_char]
|
||||
for i in all_char:
|
||||
for f in self.all_char:
|
||||
if f["name"] == i:
|
||||
self.all_char.remove(f)
|
||||
break
|
||||
try:
|
||||
with open(PLAYER_PATH / self.uid / f"{i}.json", "r", encoding="utf-8") as fp:
|
||||
data = json.load(fp)
|
||||
path = await draw_char_card(data)
|
||||
msg = await app.send_photo(channel_id, path)
|
||||
self.all_char.append(gen_char_dict(i, msg.photo.file_id))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
continue
|
||||
|
||||
def export(self):
|
||||
return {"name": self.name, "uid": self.uid, "time": int(time.time()), "all_char": self.all_char}
|
||||
|
||||
def restore(self):
|
||||
sources = sqlite.get(self.uid, None)
|
||||
if sources:
|
||||
self.name = sources.get("name", "")
|
||||
self.time = sources.get("time", 0)
|
||||
self.all_char = sources.get("all_char", [])
|
||||
|
||||
def gen_keyboard(self) -> InlineKeyboardMarkup:
|
||||
data = []
|
||||
temp_ = []
|
||||
num = 0
|
||||
for i in self.all_char:
|
||||
name = i.get("name", "")
|
||||
temp_.append(InlineKeyboardButton(name, callback_data=f"{self.uid}|{name}"))
|
||||
num += 1
|
||||
if num == 3:
|
||||
data.append(temp_)
|
||||
temp_ = []
|
||||
num = 0
|
||||
return InlineKeyboardMarkup(data)
|
||||
|
||||
def gen_back(self) -> InlineKeyboardMarkup:
|
||||
return InlineKeyboardMarkup([[InlineKeyboardButton("返回", callback_data=self.uid)]])
|
||||
|
||||
@staticmethod
|
||||
def parse_time(time_stamp: int) -> str:
|
||||
return datetime.strftime(datetime.fromtimestamp(time_stamp), '%Y-%m-%d %H:%M:%S')
|
||||
|
||||
def gen_all_char(self) -> str:
|
||||
if not self.all_char:
|
||||
return ""
|
||||
text = "缓存角色有:\n"
|
||||
for i in self.all_char:
|
||||
text += "🔸 " + i.get("name", "") + f" `{self.parse_time(i.get('time', time.time()))}`\n"
|
||||
return text
|
18
defs/refresh.py
Normal file
@ -0,0 +1,18 @@
|
||||
from defs.enkaToData import enkaToData
|
||||
from defs.player import Player
|
||||
from ci import sqlite
|
||||
import time
|
||||
|
||||
|
||||
async def refresh_player(uid: str) -> str:
|
||||
data = Player(uid)
|
||||
data.restore()
|
||||
if data.time + 60 * 5 > int(time.time()):
|
||||
return "刷新过快,请稍等一会儿再试"
|
||||
text = await enkaToData(uid)
|
||||
if not text:
|
||||
return "数据刷新失败,请重试"
|
||||
data.update_name()
|
||||
await data.update_char()
|
||||
sqlite[uid] = data.export()
|
||||
return text
|
50
defs/sources.py
Normal file
@ -0,0 +1,50 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
R_PATH = Path("resources")
|
||||
MAP_PATH = R_PATH / "map"
|
||||
TEXT_PATH = R_PATH / "texture2D"
|
||||
ICON_PATH = R_PATH / "icon"
|
||||
GACHA_PATH = R_PATH / "gachaImg"
|
||||
PLAYER_PATH = R_PATH / "player"
|
||||
RELIC_PATH = R_PATH / "relicIcon"
|
||||
|
||||
verison = '2.7.0'
|
||||
|
||||
avatarName2Element_fileName = f'avatarName2Element_mapping_{verison}.json'
|
||||
weaponHash2Name_fileName = f'weaponHash2Name_mapping_{verison}.json'
|
||||
weaponHash2Type_fileName = f'weaponHash2Type_mapping_{verison}.json'
|
||||
skillId2Name_fileName = f'skillId2Name_mapping_{verison}.json'
|
||||
talentId2Name_fileName = f'talentId2Name_mapping_{verison}.json'
|
||||
avatarId2Name_fileName = f'avatarId2Name_mapping_{verison}.json'
|
||||
|
||||
|
||||
with open(MAP_PATH / avatarId2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
avatarId2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / 'icon2Name_mapping_2.6.0.json', "r", encoding='UTF-8') as f:
|
||||
icon2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / 'artifact2attr_mapping_2.6.0.json', "r", encoding='UTF-8') as f:
|
||||
artifact2attr = json.load(f)
|
||||
|
||||
with open(MAP_PATH / 'propId2Name_mapping.json', "r", encoding='UTF-8') as f:
|
||||
propId2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / weaponHash2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
weaponHash2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / weaponHash2Type_fileName, "r", encoding='UTF-8') as f:
|
||||
weaponHash2Type = json.load(f)
|
||||
|
||||
with open(MAP_PATH / 'artifactId2Piece_mapping.json', "r", encoding='UTF-8') as f:
|
||||
artifactId2Piece = json.load(f)
|
||||
|
||||
with open(MAP_PATH / skillId2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
skillId2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / talentId2Name_fileName, "r", encoding='UTF-8') as f:
|
||||
talentId2Name = json.load(f)
|
||||
|
||||
with open(MAP_PATH / avatarName2Element_fileName, 'r', encoding='UTF-8') as f:
|
||||
avatarName2Element = json.load(f)
|
7
main.py
Normal file
@ -0,0 +1,7 @@
|
||||
import logging
|
||||
from ci import app
|
||||
|
||||
# 日志记录
|
||||
logging.basicConfig(level=logging.ERROR)
|
||||
logging.info("Bot 已启动")
|
||||
app.run()
|
18
plugins/admin.py
Normal file
@ -0,0 +1,18 @@
|
||||
from pyrogram import filters, Client
|
||||
from pyrogram.types import Message
|
||||
from ci import app, admin_id
|
||||
from defs.refresh import refresh_player
|
||||
|
||||
|
||||
@app.on_message(filters.command(["refresh_admin"]) & filters.private)
|
||||
async def refresh_command(_: Client, message: Message):
|
||||
if message.from_user.id != admin_id:
|
||||
return
|
||||
if len(message.command) == 1:
|
||||
return await message.reply("请输入 uid", quote=True)
|
||||
if not message.command[1].isnumeric():
|
||||
return await message.reply("请输入正确的 uid", quote=True)
|
||||
uid = message.command[1]
|
||||
msg = await message.reply(f"正在刷新数据,请稍等。。。", quote=True)
|
||||
text = await refresh_player(uid)
|
||||
await msg.edit(text)
|
28
plugins/bind.py
Normal file
@ -0,0 +1,28 @@
|
||||
from pyrogram import filters, Client
|
||||
from pyrogram.types import Message
|
||||
from defs.bind import check_bind, get_bind_uid, set_bind, remove_bind
|
||||
from defs.refresh import refresh_player
|
||||
from defs.player import Player
|
||||
from ci import app, me
|
||||
|
||||
|
||||
@app.on_message(filters.command(["bind", f"bind@{me['result']['username']}"]) & filters.private)
|
||||
async def bind_command(_: Client, message: Message):
|
||||
if len(message.command) == 1:
|
||||
if check_bind(message.from_user.id):
|
||||
data = Player(get_bind_uid(message.from_user.id))
|
||||
data.restore()
|
||||
return await message.reply(f"您绑定的游戏 uid 为:{get_bind_uid(message.from_user.id)}\n\n"
|
||||
f"{data.gen_all_char()}", quote=True)
|
||||
else:
|
||||
return await message.reply(f"请使用 <code>/bind [uid]</code> 绑定游戏 uid", quote=True)
|
||||
if not message.command[1].isdigit():
|
||||
if message.command[1] == "remove":
|
||||
remove_bind(message.from_user.id)
|
||||
return await message.reply("已解除绑定", quote=True)
|
||||
return await message.reply("uid 非数字", quote=True)
|
||||
uid = message.command[1]
|
||||
set_bind(message.from_user.id, uid)
|
||||
msg = await message.reply(f"绑定成功,您绑定的游戏 uid 为:{uid},正在刷新数据。。。", quote=True)
|
||||
text = await refresh_player(uid)
|
||||
await msg.edit(text)
|
33
plugins/callback.py
Normal file
@ -0,0 +1,33 @@
|
||||
from os import sep
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.types import CallbackQuery, InputMediaPhoto
|
||||
|
||||
from ci import app
|
||||
from defs.player import Player
|
||||
|
||||
|
||||
@app.on_callback_query()
|
||||
async def answer_callback(_: Client, callback_query: CallbackQuery):
|
||||
data = callback_query.data.split("|")
|
||||
uid = data[0]
|
||||
char = None
|
||||
if len(data) > 1:
|
||||
char = callback_query.data.split("|")[1]
|
||||
data = Player(uid)
|
||||
data.restore()
|
||||
if not data.all_char:
|
||||
return await callback_query.answer("没有可展示的角色,可能是数据未刷新", show_alert=True)
|
||||
if not char:
|
||||
await callback_query.message.edit_media(InputMediaPhoto(media=f"resources{sep}Kitsune.png",
|
||||
caption=f"请选择 {data.name} 的一个角色:"))
|
||||
return await callback_query.message.edit_reply_markup(reply_markup=data.gen_keyboard())
|
||||
char_data = None
|
||||
for i in data.all_char:
|
||||
if i.get("name", "") == char:
|
||||
char_data = i
|
||||
break
|
||||
if not char_data:
|
||||
return await callback_query.answer("没有可展示的角色,可能是数据未刷新", show_alert=True)
|
||||
await callback_query.message.edit_media(InputMediaPhoto(media=char_data["file_id"]))
|
||||
await callback_query.message.edit_reply_markup(reply_markup=data.gen_back())
|
37
plugins/inline.py
Normal file
@ -0,0 +1,37 @@
|
||||
from pyrogram import Client, emoji
|
||||
from pyrogram.types import InlineQuery, InlineQueryResultCachedPhoto
|
||||
|
||||
from ci import app
|
||||
from defs.bind import check_bind, get_bind_uid
|
||||
from defs.player import Player
|
||||
|
||||
|
||||
@app.on_inline_query()
|
||||
async def answer_callback(_: Client, query: InlineQuery):
|
||||
uid = None
|
||||
if check_bind(query.from_user.id):
|
||||
uid = get_bind_uid(query.from_user.id)
|
||||
if query.query:
|
||||
uid = query.query
|
||||
if not uid:
|
||||
return await query.answer(
|
||||
results=[],
|
||||
switch_pm_text=f'{emoji.CROSS_MARK} 没有搜索到任何结果',
|
||||
switch_pm_parameter="start",
|
||||
)
|
||||
data = Player(uid)
|
||||
data.restore()
|
||||
if not data.all_char:
|
||||
return await query.answer(
|
||||
results=[],
|
||||
switch_pm_text=f'{emoji.CROSS_MARK} 没有搜索到任何结果',
|
||||
switch_pm_parameter="start",
|
||||
)
|
||||
inline_data = []
|
||||
for i in data.all_char:
|
||||
inline_data.append(InlineQueryResultCachedPhoto(photo_file_id=i["file_id"],
|
||||
title=i["name"],
|
||||
description=data.name))
|
||||
await query.answer(inline_data,
|
||||
switch_pm_text=f'{emoji.KEY} 搜索到了 {len(data.all_char)} 个角色',
|
||||
switch_pm_parameter="start")
|
15
plugins/refresh.py
Normal file
@ -0,0 +1,15 @@
|
||||
from pyrogram import filters, Client
|
||||
from pyrogram.types import Message
|
||||
from defs.bind import check_bind, get_bind_uid
|
||||
from ci import app, me
|
||||
from defs.refresh import refresh_player
|
||||
|
||||
|
||||
@app.on_message(filters.command(["refresh", f"refresh@{me['result']['username']}"]) & filters.private)
|
||||
async def refresh_command(_: Client, message: Message):
|
||||
if not check_bind(message.from_user.id):
|
||||
return await message.reply(f"请使用 <code>/bind [uid]</code> 绑定游戏 uid", quote=True)
|
||||
uid = get_bind_uid(message.from_user.id)
|
||||
msg = await message.reply(f"正在刷新数据,请稍等。。。", quote=True)
|
||||
text = await refresh_player(uid)
|
||||
await msg.edit(text)
|
30
plugins/search.py
Normal file
@ -0,0 +1,30 @@
|
||||
from os import sep
|
||||
|
||||
from pyrogram import filters, Client
|
||||
from pyrogram.types import Message
|
||||
|
||||
from defs.bind import check_bind, get_bind_uid
|
||||
from ci import app, me
|
||||
from defs.player import Player
|
||||
|
||||
|
||||
@app.on_message(filters.command(["search", f"search@{me['result']['username']}"]))
|
||||
async def search_command(_: Client, message: Message):
|
||||
if message.sender_chat or not message.from_user:
|
||||
return
|
||||
uid = None
|
||||
if check_bind(message.from_user.id):
|
||||
uid = get_bind_uid(message.from_user.id)
|
||||
if len(message.command) > 1:
|
||||
if message.command[1].isnumeric():
|
||||
uid = message.command[1]
|
||||
if not uid:
|
||||
return await message.reply("请使用 /search [uid] 或 /bind [uid] 绑定账号后搜索", quote=True)
|
||||
data = Player(uid)
|
||||
data.restore()
|
||||
if not data.all_char:
|
||||
return await message.reply("没有可展示的角色,可能是数据未刷新", quote=True)
|
||||
await message.reply_photo(f"resources{sep}Kitsune.png",
|
||||
caption=f"请选择 {data.name} 的一个角色:",
|
||||
quote=True,
|
||||
reply_markup=data.gen_keyboard())
|
29
plugins/start.py
Normal file
@ -0,0 +1,29 @@
|
||||
from pyrogram import filters, Client
|
||||
from pyrogram.types import Message
|
||||
from ci import app, me
|
||||
|
||||
des = """
|
||||
你好!{} 我是 [{}]({})
|
||||
|
||||
> 请先使用 `/bind [uid]` 绑定游戏 uid 进行更新数据,然后使用 `/search [uid(可选)]` 获取角色卡片。
|
||||
|
||||
我基于公共 API 提供的数据来合成图片,支持以下数据:
|
||||
`
|
||||
- 等级
|
||||
- 天赋
|
||||
- 武器
|
||||
- 面板数据
|
||||
- 圣遗物
|
||||
`
|
||||
角色数据基于 [enka](https://enka.shinshin.moe)
|
||||
图片模板基于 [GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID)
|
||||
"""
|
||||
|
||||
|
||||
@app.on_message(filters.command(["start", f"start@{me['result']['username']}"]) & filters.private)
|
||||
async def start_command(_: Client, message: Message):
|
||||
await message.reply(des.format(message.from_user.mention(),
|
||||
me["result"]["first_name"],
|
||||
f"https://t.me/{me['result']['username']}"),
|
||||
disable_web_page_preview=True,
|
||||
quote=True)
|
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
httpx
|
||||
pillow
|
||||
pyrogram==2.0.26
|
||||
pyromod
|
||||
TGCrypto
|
||||
sqlitedict
|
BIN
resources/Kitsune.png
Normal file
After Width: | Height: | Size: 971 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Albedo.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Aloy.png
Normal file
After Width: | Height: | Size: 772 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Ambor.png
Normal file
After Width: | Height: | Size: 595 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Ambor_TT.png
Normal file
After Width: | Height: | Size: 609 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Ayaka.png
Normal file
After Width: | Height: | Size: 1.7 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Ayato.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Barbara.png
Normal file
After Width: | Height: | Size: 635 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Beidou.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Bennett.png
Normal file
After Width: | Height: | Size: 892 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Chongyun.png
Normal file
After Width: | Height: | Size: 935 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Diluc.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Diona.png
Normal file
After Width: | Height: | Size: 440 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Eula.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Feiyan.png
Normal file
After Width: | Height: | Size: 858 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Fischl.png
Normal file
After Width: | Height: | Size: 694 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Ganyu.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Gorou.png
Normal file
After Width: | Height: | Size: 642 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Hutao.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Itto.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Kaeya.png
Normal file
After Width: | Height: | Size: 590 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Kazuha.png
Normal file
After Width: | Height: | Size: 759 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Keqing.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Klee.png
Normal file
After Width: | Height: | Size: 486 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Kokomi.png
Normal file
After Width: | Height: | Size: 964 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Lisa.png
Normal file
After Width: | Height: | Size: 495 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Mona.png
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Mona_TT.png
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Ningguang.png
Normal file
After Width: | Height: | Size: 902 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Noel.png
Normal file
After Width: | Height: | Size: 636 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Qin.png
Normal file
After Width: | Height: | Size: 974 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Qin_TT.png
Normal file
After Width: | Height: | Size: 979 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Qiqi.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Razor.png
Normal file
After Width: | Height: | Size: 949 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Rosaria #2526820.png
Normal file
After Width: | Height: | Size: 749 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Rosaria.png
Normal file
After Width: | Height: | Size: 795 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Rosaria_TT.png
Normal file
After Width: | Height: | Size: 807 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Sara.png
Normal file
After Width: | Height: | Size: 574 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Sayu.png
Normal file
After Width: | Height: | Size: 613 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Shenhe.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Shinobu.png
Normal file
After Width: | Height: | Size: 462 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Shougun.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Sucrose.png
Normal file
After Width: | Height: | Size: 693 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Tartaglia.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Tohma.png
Normal file
After Width: | Height: | Size: 792 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Venti.png
Normal file
After Width: | Height: | Size: 996 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Xiangling.png
Normal file
After Width: | Height: | Size: 579 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Xiao.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Xingqiu.png
Normal file
After Width: | Height: | Size: 542 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Xinyan.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Yae.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Yelan.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Yoimiya.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Yunjin.png
Normal file
After Width: | Height: | Size: 474 KiB |
BIN
resources/gachaImg/UI_Gacha_AvatarImg_Zhongli.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
resources/gachaImg/default.jpg
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
resources/icon/Skill_A_01.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
resources/icon/Skill_A_02.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
resources/icon/Skill_A_03.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
resources/icon/Skill_A_04.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
resources/icon/Skill_A_CC_Electric.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
resources/icon/Skill_A_CC_Rock.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
resources/icon/Skill_A_Catalyst_MD.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
resources/icon/Skill_A_Dvalin_AirGun.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
resources/icon/Skill_A_Thunderbolt.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
resources/icon/Skill_B_Barbara_01.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
resources/icon/Skill_C_ChargeNormal.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/icon/Skill_C_ElectricTransfer.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
resources/icon/Skill_C_FireCracker.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
resources/icon/Skill_C_Stamine_1.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
resources/icon/Skill_C_Stamine_2.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/icon/Skill_C_Stamine_3.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/icon/Skill_C_Stamine_4.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icon/Skill_C_Stamine_5.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
resources/icon/Skill_C_ThunderCoil.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
resources/icon/Skill_E_Albedo_01.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
resources/icon/Skill_E_Albedo_01_HD.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
resources/icon/Skill_E_Aloy_01.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
resources/icon/Skill_E_Aloy_01_HD.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
resources/icon/Skill_E_Ambor.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
resources/icon/Skill_E_Ambor_HD.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
resources/icon/Skill_E_Ayaka.png
Normal file
After Width: | Height: | Size: 6.7 KiB |