🚧完成sr抽卡记录

This commit is contained in:
qwerdvd 2023-05-08 21:57:20 +08:00
parent ce323eb967
commit f9cb3bfb84
15 changed files with 753 additions and 528 deletions

View File

@ -5,8 +5,7 @@ from gsuid_core.models import Event
from ..utils.convert import get_uid
from ..utils.error_reply import UID_HINT
from .get_gachalogs import save_gachalogs
# from .draw_gachalogs import draw_gachalogs_img
from .draw_gachalogs import draw_gachalogs_img
sv_gacha_log = SV('sr抽卡记录')
sv_get_gachalog_by_link = SV('sr导入抽卡链接', area='DIRECT')
@ -18,8 +17,7 @@ async def send_gacha_log_card_info(bot: Bot, ev: Event):
uid, user_id = await get_uid(bot, ev, True)
if uid is None:
return await bot.send(UID_HINT)
# im = await draw_gachalogs_img(uid, user_id)
im = '画个饼先,在做了在做了'
im = await draw_gachalogs_img(uid, user_id)
await bot.send(im)

View File

@ -1,426 +1,438 @@
# import json
# import random
# import asyncio
# import datetime
# from pathlib import Path
# from typing import List, Tuple, Union
#
# from PIL import Image, ImageDraw
# from gsuid_core.logger import logger
#
# from ..utils.image.convert import convert_img
# from ..utils.map.name_covert import name_to_avatar_id
# from ..utils.resource.RESOURCE_PATH import
# CHAR_PATH, PLAYER_PATH, WEAPON_PATH
# from ..utils.image.image_tools import (
# get_color_bg,
# get_qq_avatar,
# draw_pic_with_ring,
# )
# from ..utils.fonts.starrail_fonts import (
# sr_font_24,
# sr_font_28,
# sr_font_36,
# sr_font_40,
# sr_font_62,
# )
#
# TEXT_PATH = Path(__file__).parent / 'texture2d'
#
import json
import asyncio
import datetime
from pathlib import Path
from typing import List, Tuple, Union
from PIL import Image, ImageDraw
from gsuid_core.logger import logger
from ..utils.image.convert import convert_img
from ..utils.map.name_covert import name_to_avatar_id
from ..utils.resource.RESOURCE_PATH import (
PLAYER_PATH,
WEAPON_PATH,
CHAR_ICON_PATH,
)
from ..utils.image.image_tools import (
get_color_bg,
get_qq_avatar,
draw_pic_with_ring,
)
from ..utils.fonts.starrail_fonts import (
sr_font_20,
sr_font_24,
sr_font_28,
sr_font_38,
sr_font_40,
)
TEXT_PATH = Path(__file__).parent / 'texture2d'
EMO_PATH = Path(__file__).parent / 'texture2d' / 'emo'
# up_tag = Image.open(TEXT_PATH / 'up.png')
#
# first_color = (29, 29, 29)
# brown_color = (41, 25, 0)
# red_color = (255, 66, 66)
# green_color = (74, 189, 119)
#
# CHANGE_MAP = {'常驻祈愿': 'normal', '角色祈愿': 'char', '武器祈愿': 'weapon'}
# HOMO_TAG = ['非到极致', '运气不好', '平稳保底', '小欧一把', '欧狗在此']
# NORMAL_LIST = [
# '莫娜',
# '迪卢克',
# '七七',
# '琴',
# '阿莫斯之弓',
# '天空之翼',
# '四风原典',
# '天空之卷',
# '和璞鸢',
# '天空之脊',
# '狼的末路',
# '天空之傲',
# '风鹰剑',
# '天空之刃',
# ]
#
# UP_LIST = {
# '刻晴': [(2021, 2, 17, 18, 0, 0), (2021, 3, 2, 15, 59, 59)],
# '提纳里': [(2022, 8, 24, 11, 0, 0), (2022, 9, 9, 17, 59, 59)],
# '迪希雅': [(2023, 3, 1, 11, 0, 0), (2023, 3, 21, 17, 59, 59)],
# }
#
#
# async def _draw_card(
# img: Image.Image,
# xy_point: Tuple[int, int],
# name: str,
# gacha_num: int,
# is_up: bool,
# ):
# card_img = Image.open(TEXT_PATH / 'item_bg.png')
# card_img_draw = ImageDraw.Draw(card_img)
# point = (1, 0)
# text_point = (55, 124)
# if type == '角色':
# _id = await name_to_avatar_id(name)
# item_pic = (
# Image.open(CHAR_PATH / f'{_id}.png')
# .convert('RGBA')
# .resize((108, 108))
# )
# else:
# item_pic = (
# Image.open(WEAPON_PATH / f'{name}.png')
# .convert('RGBA')
# .resize((108, 108))
# )
# card_img.paste(item_pic, point, item_pic)
# if gacha_num >= 81:
# text_color = red_color
# elif gacha_num <= 55:
# text_color = green_color
# else:
# text_color = brown_color
# card_img_draw.text(
# text_point, f'{gacha_num}抽', text_color, gs_font_24, 'mm'
# )
# if is_up:
# card_img.paste(up_tag, (47, -2), up_tag)
# img.paste(card_img, xy_point, card_img)
#
#
# async def random_emo_pic(level: int) -> Image.Image:
# emo_fold = TEXT_PATH / str(level)
# return Image.open(random.choice(list(emo_fold.iterdir())))
#
#
# async def get_level_from_list(ast: int, lst: List) -> int:
# if ast == 0:
# return 3
#
# for num_index, num in enumerate(lst):
# if ast <= num:
# level = 5 - num_index
# break
# else:
# level = 1
# return level
#
#
# def check_up(name: str, _time: str) -> bool:
# for char in UP_LIST:
# if char == name:
# time = UP_LIST[char]
# s_time = datetime.datetime(*time[0])
# e_time = datetime.datetime(*time[1])
# gacha_time = datetime.datetime.strptime
# (_time, '%Y-%m-%d %H:%M:%S')
# if gacha_time < s_time or gacha_time > e_time:
# return False
# else:
# return True
# else:
# return False
#
#
# async def draw_gachalogs_img(uid: str, user_id: str) -> Union[bytes, str]:
# path = PLAYER_PATH / str(uid) / 'gacha_logs.json'
# if not path.exists():
# return '你还没有祈愿数据噢~\n请添加Stoken后使用命令`刷新抽卡记录`更新祈愿数据~'
# with open(path, 'r', encoding='UTF-8') as f:
# gacha_data = json.load(f)
#
# # 数据初始化
# total_data = {}
# for i in ['群星跃迁', '角色跃迁', '光锥跃迁']:
# total_data[i] = {
# 'total': 0, # 五星总数
# 'avg': 0, # 抽卡平均数
# 'avg_up': 0, # up平均数
# 'remain': 0, # 已xx抽未出金
# 'r_num': [], # 不包含首位的抽卡数量
# 'e_num': [], # 包含首位的up抽卡数量
# 'up_list': [], # 抽到的UP列表(不包含首位)
# 'normal_list': [], # 抽到的五星列表(不包含首位)
# 'list': [], # 抽到的五星列表
# 'time_range': '', # 抽卡时间
# 'all_time': 0, # 抽卡总计秒数
# 'type': '一般型', # 抽卡类型: 随缘型, 氪金型, 规划型, 仓鼠型, 佛系型
# 'short_gacha_data': {'time': 0, 'num': 0},
# 'long_gacha_data': {'time': 0, 'num': 0},
# }
# # 拿到数据列表
# data_list = gacha_data['list'][i]
# # 初始化开关
# is_not_first = False
# # 开始初始化抽卡数
# num = 1
# # 从后面开始循环
# temp_time = datetime.datetime(2023, 4, 26, 8, 0, 0)
# for index, data in enumerate(data_list[::-1]):
# # 计算抽卡时间跨度
# if index == 0:
# total_data[i]['time_range'] = data['time']
# if index == len(data_list) - 1:
# total_data[i]['all_time'] = (
# datetime.datetime.strptime(
# data['time'], '%Y-%m-%d %H:%M:%S'
# )
# - datetime.datetime.strptime(
# total_data[i]['time_range'], '%Y-%m-%d %H:%M:%S'
# )
# ).total_seconds()
# total_data[i]['time_range'] += '~' + data['time']
#
# # 计算时间间隔
# if index != 0:
# now_time = datetime.datetime.strptime(
# data['time'], '%Y-%m-%d %H:%M:%S'
# )
# dis = (now_time - temp_time).total_seconds()
# temp_time = now_time
# if dis <= 5000:
# total_data[i]['short_gacha_data']['num'] += 1
# total_data[i]['short_gacha_data']['time'] += dis
# elif dis >= 86400:
# total_data[i]['long_gacha_data']['num'] += 1
# total_data[i]['long_gacha_data']['time'] += dis
# else:
# temp_time = datetime.datetime.strptime(
# data['time'], '%Y-%m-%d %H:%M:%S'
# )
#
# # 如果这是个五星
# if data['rank_type'] == '5':
# # 抽到这个五星花了多少抽
# data['gacha_num'] = num
#
# # 判断是否是UP
# if data['name'] in NORMAL_LIST:
# data['is_up'] = False
# elif data['name'] in UP_LIST:
# data['is_up'] = check_up(data['name'], data['time'])
# else:
# data['is_up'] = True
#
# # 往里加东西
# if is_not_first:
# total_data[i]['r_num'].append(num)
# total_data[i]['normal_list'].append(data)
# if data['is_up']:
# total_data[i]['up_list'].append(data)
#
# # 把这个数据扔到抽到的五星列表内
# total_data[i]['list'].append(data)
#
# # 判断经过了第一个
# if total_data[i]['list']:
# is_not_first = True
#
# num = 1
# # 五星总数增加1
# total_data[i]['total'] += 1
# else:
# num += 1
#
# # 计算已多少抽
# total_data[i]['remain'] = num - 1
#
# # 计算平均抽卡数
# if len(total_data[i]['normal_list']) == 0:
# total_data[i]['avg'] = 0
# else:
# total_data[i]['avg'] = float(
# '{:.2f}'.format(
# sum(total_data[i]['r_num']) / len(total_data[i]['r_num'])
# )
# )
# # 计算平均up数量
# if len(total_data[i]['up_list']) == 0:
# total_data[i]['avg_up'] = 0
# else:
# total_data[i]['avg_up'] = float(
# '{:.2f}'.format(
# sum(total_data[i]['r_num'])
# / len(total_data[i]['up_list'])
# )
# )
#
# # 计算抽卡类型
# # 如果抽卡总数小于40
# if gacha_data[f'{CHANGE_MAP[i]}_gacha_num'] <= 40:
# total_data[i]['type'] = '佛系型'
# # 如果长时抽卡总数占据了总抽卡数的70%
# elif (
# total_data[i]['long_gacha_data']['num']
# / gacha_data[f'{CHANGE_MAP[i]}_gacha_num']
# >= 0.7
# ):
# total_data[i]['type'] = '随缘型'
# # 如果短时抽卡总数占据了总抽卡数的70%
# elif (
# total_data[i]['short_gacha_data']['num']
# / gacha_data[f'{CHANGE_MAP[i]}_gacha_num']
# >= 0.7
# ):
# total_data[i]['type'] = '规划型'
# # 如果抽卡数量远远大于标称抽卡数量
# elif (
# total_data[i]['all_time'] / 30000
# <= gacha_data[f'{CHANGE_MAP[i]}_gacha_num']
# ):
# # 如果长时抽卡数量大于短时抽卡数量
# if (
# total_data[i]['long_gacha_data']['num']
# >= total_data[i]['short_gacha_data']['num']
# ):
# total_data[i]['type'] = '规划型'
# else:
# total_data[i]['type'] = '氪金型'
# # 如果抽卡数量远远小于标称抽卡数量
# elif (
# total_data[i]['all_time'] / 32000
# >= gacha_data[f'{CHANGE_MAP[i]}_gacha_num'] * 2
# ):
# total_data[i]['type'] = '仓鼠型'
#
# # 常量偏移数据
# single_y = 150
#
# # 计算图片尺寸
# normal_y = (1 + ((total_data['常驻祈愿']['total'] - 1) // 6)) * single_y
# char_y = (1 + ((total_data['角色祈愿']['total'] - 1) // 6)) * single_y
# weapon_y = (1 + ((total_data['武器祈愿']['total'] - 1) // 6)) * single_y
#
# # 获取背景图片各项参数
# _id = str(user_id)
# if _id.startswith('http'):
# char_pic = await get_qq_avatar(avatar_url=_id)
# else:
# char_pic = await get_qq_avatar(qid=user_id)
# char_pic = await draw_pic_with_ring(char_pic, 320)
#
# avatar_title = Image.open(TEXT_PATH / 'avatar_title.png')
# img = await get_color_bg(950, 530 + 900 + normal_y + char_y + weapon_y)
# img.paste(avatar_title, (0, 0), avatar_title)
# img.paste(char_pic, (318, 83), char_pic)
# img_draw = ImageDraw.Draw(img)
# img_draw.text((475, 454), f'UID {uid}', first_color, gs_font_36, 'mm')
#
# # 处理title
# # {'total': 0, 'avg': 0, 'remain': 0, 'list': []}
# type_list = ['常驻祈愿', '角色祈愿', '武器祈愿']
# y_extend = 0
# level = 3
# for index, i in enumerate(type_list):
# title = Image.open(TEXT_PATH / 'gahca_title.png')
# if i == '常驻祈愿':
# level = await get_level_from_list(
# total_data[i]['avg'], [54, 61, 67, 73, 80]
# )
# else:
# if i == '武器祈愿':
# level = await get_level_from_list(
# total_data[i]['avg_up'], [62, 75, 88, 99, 111]
# )
# else:
# level = await get_level_from_list(
# total_data[i]['avg_up'], [74, 87, 99, 105, 120]
# )
#
# emo_pic = await random_emo_pic(level)
# emo_pic = emo_pic.resize((154, 154))
# title.paste(emo_pic, (703, 28), emo_pic)
# title_draw = ImageDraw.Draw(title)
# # 欧非描述
# title_draw.text(
# (778, 207), HOMO_TAG[level - 1], first_color, gs_font_36, 'mm'
# )
# # 卡池
# title_draw.text((69, 72), i, first_color, gs_font_62, 'lm')
# # 抽卡时间
# if total_data[i]['time_range']:
# time_range = total_data[i]['time_range']
# else:
# time_range = '暂未抽过卡!'
# title_draw.text((68, 122), time_range, brown_color, gs_font_28, 'lm')
# # 平均抽卡数量
# title_draw.text(
# (123, 176),
# str(total_data[i]['avg']),
# first_color,
# gs_font_40,
# 'mm',
# )
# # 平均up
# title_draw.text(
# (272, 176),
# str(total_data[i]['avg_up']),
# first_color,
# gs_font_40,
# 'mm',
# )
# # 抽卡总数
# title_draw.text(
# (424, 176),
# str(gacha_data[f'{CHANGE_MAP[i]}_gacha_num']),
# first_color,
# gs_font_40,
# 'mm',
# )
# # 抽卡类型
# title_draw.text(
# (585, 176),
# str(total_data[i]['type']),
# first_color,
# gs_font_40,
# 'mm',
# )
# # 已抽数
# title_draw.text(
# (383, 85),
# str(total_data[i]['remain']),
# red_color,
# gs_font_28,
# 'mm',
# )
# y_extend += (
# (1 + ((total_data[type_list[index - 1]]
# ['total'] - 1) // 6)) * 150
# if index != 0
# else 0
# )
# y = 540 + index * 300 + y_extend
# img.paste(title, (0, y), title)
# tasks = []
# for item_index, item in enumerate(total_data[i]['list']):
# item_x = (item_index % 6) * 138 + 60
# item_y = (item_index // 6) * 150 + y + 275
# xy_point = (item_x, item_y)
# tasks.append(
# _draw_card(
# img,
# xy_point,
# item['item_type'],
# item['name'],
# item['gacha_num'],
# item['is_up'],
# )
# )
# await asyncio.gather(*tasks)
# tasks.clear()
#
# # 发送图片
# res = await convert_img(img)
# logger.info('[查询抽卡]绘图已完成,等待发送!')
# return res
Abg3_img = Image.open(TEXT_PATH / 'Abg3.png')
bg1_img = Image.open(TEXT_PATH / 'bg1.png')
first_color = (29, 29, 29)
brown_color = (41, 25, 0)
red_color = (255, 66, 66)
green_color = (74, 189, 119)
white_color = (213, 213, 213)
whole_white_color = (255, 255, 255)
CHANGE_MAP = {
'始发跃迁': 'begin',
'群星跃迁': 'normal',
'角色跃迁': 'char',
'光锥跃迁': 'weapon',
}
HOMO_TAG = ['非到极致', '运气不好', '平稳保底', '小欧一把', '欧狗在此']
NORMAL_LIST = [
'彦卿',
'白露',
'姬子',
'瓦尔特',
'布洛妮娅',
'克拉拉',
'杰帕德',
'银河铁道之夜',
'以世界之名',
'但战斗还未结束',
'制胜的瞬间',
'无可取代的东西',
'时节不居',
]
UP_LIST = {
'刻晴': [(2021, 2, 17, 18, 0, 0), (2021, 3, 2, 15, 59, 59)],
'提纳里': [(2022, 8, 24, 11, 0, 0), (2022, 9, 9, 17, 59, 59)],
'迪希雅': [(2023, 3, 1, 11, 0, 0), (2023, 3, 21, 17, 59, 59)],
}
async def _draw_card(
img: Image.Image,
xy_point: Tuple[int, int],
type: str,
name: str,
gacha_num: int,
is_up: bool,
):
card_img = Image.open(TEXT_PATH / 'char_bg.png')
card_img_draw = ImageDraw.Draw(card_img)
point = (47, 31)
text_point = (100, 165)
if type == '角色':
_id = await name_to_avatar_id(name)
item_pic = (
Image.open(CHAR_ICON_PATH / f'{_id}.png')
.convert('RGBA')
.crop((0, 0, 112, 112))
.resize((105, 105))
)
else:
item_pic = (
Image.open(WEAPON_PATH / f'{name}.png')
.convert('RGBA')
.crop((0, 0, 867, 867))
)
item_pic = item_pic.resize((105, 105))
card_img.paste(item_pic, point, item_pic)
if gacha_num >= 81:
text_color = red_color
elif gacha_num <= 55:
text_color = green_color
else:
text_color = brown_color
card_img_draw.text(
text_point, f'{gacha_num}', text_color, sr_font_24, 'mm'
)
if is_up:
print(f'up: {name}')
# card_img.paste(up_tag, (47, -2), up_tag)
img.paste(card_img, xy_point, card_img)
async def random_emo_pic(level: int) -> Image.Image:
emo_fold = EMO_PATH / f'3000{level}.png'
return Image.open(emo_fold)
async def get_level_from_list(ast: int, lst: List) -> int:
if ast == 0:
return 3
for num_index, num in enumerate(lst):
if ast <= num:
level = 5 - num_index
break
else:
level = 1
return level
def check_up(name: str, _time: str) -> bool:
for char in UP_LIST:
if char == name:
time = UP_LIST[char]
s_time = datetime.datetime(*time[0])
e_time = datetime.datetime(*time[1])
gacha_time = datetime.datetime.strptime(_time, '%Y-%m-%d %H:%M:%S')
if gacha_time < s_time or gacha_time > e_time:
return False
else:
return True
else:
return False
async def draw_gachalogs_img(uid: str, user_id: str) -> Union[bytes, str]:
path = PLAYER_PATH / str(uid) / 'gacha_logs_test.json'
if not path.exists():
return '你还没有跃迁数据噢~\n请使用命令`sr导入抽卡链接`更新跃迁数据~'
with open(path, 'r', encoding='UTF-8') as f:
gacha_data = json.load(f)
# 数据初始化
total_data = {}
for i in ['群星跃迁', '始发跃迁', '角色跃迁', '光锥跃迁']:
total_data[i] = {
'total': 0, # 五星总数
'avg': 0, # 抽卡平均数
'avg_up': 0, # up平均数
'remain': 0, # 已xx抽未出金
'r_num': [], # 不包含首位的抽卡数量
'e_num': [], # 包含首位的up抽卡数量
'up_list': [], # 抽到的UP列表(不包含首位)
'normal_list': [], # 抽到的五星列表(不包含首位)
'list': [], # 抽到的五星列表
'time_range': '', # 抽卡时间
'all_time': 0, # 抽卡总计秒数
'type': '一般型', # 抽卡类型: 随缘型, 氪金型, 规划型, 仓鼠型, 佛系型
'short_gacha_data': {'time': 0, 'num': 0},
'long_gacha_data': {'time': 0, 'num': 0},
}
# 拿到数据列表
data_list = gacha_data['data'][i]
print(data_list)
# 初始化开关
is_not_first = True
# 开始初始化抽卡数
num = 1
# 从后面开始循环
temp_time = datetime.datetime(2023, 4, 26, 8, 0, 0)
for index, data in enumerate(data_list[::-1]):
# 计算抽卡时间跨度
if index == 0:
total_data[i]['time_range'] = data['time']
if index == len(data_list) - 1:
total_data[i]['all_time'] = (
datetime.datetime.strptime(
data['time'], '%Y-%m-%d %H:%M:%S'
)
- datetime.datetime.strptime(
total_data[i]['time_range'], '%Y-%m-%d %H:%M:%S'
)
).total_seconds()
total_data[i]['time_range'] += '~' + data['time']
# 计算时间间隔
if index != 0:
now_time = datetime.datetime.strptime(
data['time'], '%Y-%m-%d %H:%M:%S'
)
dis = (now_time - temp_time).total_seconds()
temp_time = now_time
if dis <= 5000:
total_data[i]['short_gacha_data']['num'] += 1
total_data[i]['short_gacha_data']['time'] += dis
elif dis >= 86400:
total_data[i]['long_gacha_data']['num'] += 1
total_data[i]['long_gacha_data']['time'] += dis
else:
temp_time = datetime.datetime.strptime(
data['time'], '%Y-%m-%d %H:%M:%S'
)
# 如果这是个五星
if data['rank_type'] == '5':
# 抽到这个五星花了多少抽
data['gacha_num'] = num
# 判断是否是UP
if data['name'] in NORMAL_LIST:
data['is_up'] = False
elif data['name'] in UP_LIST:
data['is_up'] = check_up(data['name'], data['time'])
else:
data['is_up'] = True
# 往里加东西
if is_not_first:
total_data[i]['r_num'].append(num)
total_data[i]['normal_list'].append(data)
if data['is_up']:
total_data[i]['up_list'].append(data)
# 把这个数据扔到抽到的五星列表内
total_data[i]['list'].append(data)
# 判断经过了第一个
if total_data[i]['list']:
is_not_first = True
num = 1
# 五星总数增加1
total_data[i]['total'] += 1
else:
num += 1
# 计算已多少抽
total_data[i]['remain'] = num - 1
# 计算平均抽卡数
if len(total_data[i]['normal_list']) == 0:
total_data[i]['avg'] = 0
else:
total_data[i]['avg'] = float(
'{:.2f}'.format(
sum(total_data[i]['r_num']) / len(total_data[i]['r_num'])
)
)
# 计算平均up数量
if len(total_data[i]['up_list']) == 0:
total_data[i]['avg_up'] = 0
else:
total_data[i]['avg_up'] = float(
'{:.2f}'.format(
sum(total_data[i]['r_num']) / len(total_data[i]['up_list'])
)
)
# 计算抽卡类型
# 如果抽卡总数小于40
if gacha_data[f'{CHANGE_MAP[i]}_gacha_num'] <= 40:
total_data[i]['type'] = '佛系型'
# 如果长时抽卡总数占据了总抽卡数的70%
elif (
total_data[i]['long_gacha_data']['num']
/ gacha_data[f'{CHANGE_MAP[i]}_gacha_num']
>= 0.7
):
total_data[i]['type'] = '随缘型'
# 如果短时抽卡总数占据了总抽卡数的70%
elif (
total_data[i]['short_gacha_data']['num']
/ gacha_data[f'{CHANGE_MAP[i]}_gacha_num']
>= 0.7
):
total_data[i]['type'] = '规划型'
# 如果抽卡数量远远大于标称抽卡数量
elif (
total_data[i]['all_time'] / 30000
<= gacha_data[f'{CHANGE_MAP[i]}_gacha_num']
):
# 如果长时抽卡数量大于短时抽卡数量
if (
total_data[i]['long_gacha_data']['num']
>= total_data[i]['short_gacha_data']['num']
):
total_data[i]['type'] = '规划型'
else:
total_data[i]['type'] = '氪金型'
# 如果抽卡数量远远小于标称抽卡数量
elif (
total_data[i]['all_time'] / 32000
>= gacha_data[f'{CHANGE_MAP[i]}_gacha_num'] * 2
):
total_data[i]['type'] = '仓鼠型'
print(total_data)
# 常量偏移数据
single_y = 150
# 计算图片尺寸
normal_y = (1 + ((total_data['群星跃迁']['total'] - 1) // 6)) * single_y
begin_y = (1 + ((total_data['始发跃迁']['total'] - 1) // 6)) * single_y
char_y = (1 + ((total_data['角色跃迁']['total'] - 1) // 6)) * single_y
weapon_y = (1 + ((total_data['光锥跃迁']['total'] - 1) // 6)) * single_y
# 获取背景图片各项参数
_id = str(user_id)
if _id.startswith('http'):
char_pic = await get_qq_avatar(avatar_url=_id)
else:
char_pic = await get_qq_avatar(qid=user_id)
char_pic = await draw_pic_with_ring(char_pic, 206)
# 获取背景图片各项参数
img = Abg3_img.copy()
img = await get_color_bg(
800, 1600 + 400 + normal_y + char_y + weapon_y + begin_y
)
gacha_title = bg1_img.copy()
gacha_title.paste(char_pic, (297, 81), char_pic)
img.paste(gacha_title, (0, 0), gacha_title)
img_draw = ImageDraw.Draw(img)
img_draw.text((400, 345), f'UID {uid}', white_color, sr_font_28, 'mm')
# 处理title
# {'total': 0, 'avg': 0, 'remain': 0, 'list': []}
type_list = ['群星跃迁', '始发跃迁', '角色跃迁', '光锥跃迁']
y_extend = 0
level = 3
for index, i in enumerate(type_list):
title = Image.open(TEXT_PATH / 'bg2.png')
if i == '群星跃迁':
level = await get_level_from_list(
total_data[i]['avg'], [54, 61, 67, 73, 80]
)
elif i == '始发跃迁':
level = await get_level_from_list(
total_data[i]['avg'], [62, 75, 88, 99, 111]
)
else:
if i == '光锥跃迁':
level = await get_level_from_list(
total_data[i]['avg_up'], [62, 75, 88, 99, 111]
)
else:
level = await get_level_from_list(
total_data[i]['avg_up'], [74, 87, 99, 105, 120]
)
emo_pic = await random_emo_pic(level)
emo_pic = emo_pic.resize((195, 195))
title.paste(emo_pic, (500, 123), emo_pic)
title_draw = ImageDraw.Draw(title)
# 卡池
title_draw.text((110, 73), i, whole_white_color, sr_font_38, 'lm')
# 抽卡时间
if total_data[i]['time_range']:
time_range = total_data[i]['time_range']
else:
time_range = '暂未抽过卡!'
title_draw.text((78, 340), time_range, brown_color, sr_font_20, 'lm')
# 平均抽卡数量
title_draw.text(
(143, 215),
str(total_data[i]['avg']),
first_color,
sr_font_40,
'mm',
)
# 平均up
title_draw.text(
(280, 215),
str(total_data[i]['avg_up']),
first_color,
sr_font_40,
'mm',
)
# 抽卡总数
title_draw.text(
(413, 215),
str(gacha_data[f'{CHANGE_MAP[i]}_gacha_num']),
first_color,
sr_font_40,
'mm',
)
# 已抽数
title_draw.text(
(333, 75),
str(total_data[i]['remain']),
red_color,
sr_font_28,
'mm',
)
y_extend += (
(1 + ((total_data[type_list[index - 1]]['total'] - 1) // 6)) * 150
if index != 0
else 0
)
y = 350 + index * 400 + y_extend
img.paste(title, (0, y), title)
tasks = []
for item_index, item in enumerate(total_data[i]['list']):
item_x = (item_index % 6) * 138 + 45
item_y = (item_index // 6) * 150 + y + 355
xy_point = (item_x, item_y)
tasks.append(
_draw_card(
img,
xy_point,
item['item_type'],
item['name'],
item['gacha_num'],
item['is_up'],
)
)
await asyncio.gather(*tasks)
tasks.clear()
# 发送图片
res = await convert_img(img)
logger.info('[查询抽卡]绘图已完成,等待发送!')
# res = 123
return res

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

View File

@ -0,0 +1,264 @@
import math
import random
from io import BytesIO
from pathlib import Path
from typing import Tuple, Union, Optional
from PIL import Image
from httpx import get
from ...starrailuid_config.sr_config import srconfig
from ..resource.RESOURCE_PATH import CU_BG_PATH, TEXT2D_PATH
BG_PATH = Path(__file__).parent / 'bg'
TEXT_PATH = Path(__file__).parent / 'texture2d'
ring_pic = Image.open(TEXT_PATH / 'ring.png')
mask_pic = Image.open(TEXT_PATH / 'mask.png')
NM_BG_PATH = BG_PATH / 'nm_bg'
SP_BG_PATH = BG_PATH / 'sp_bg'
if list(CU_BG_PATH.iterdir()) != []:
bg_path = CU_BG_PATH
else:
bg_path = NM_BG_PATH
async def get_qq_avatar(
qid: Optional[Union[int, str]] = None, avatar_url: Optional[str] = None
) -> Image.Image:
if qid:
avatar_url = f'http://q1.qlogo.cn/g?b=qq&nk={qid}&s=640'
elif avatar_url is None:
avatar_url = 'https://q1.qlogo.cn/g?b=qq&nk=3399214199&s=640'
char_pic = Image.open(BytesIO(get(avatar_url).content)).convert('RGBA')
return char_pic
async def get_color_bg(
based_w: int,
based_h: int,
bg: Optional[str] = None,
without_mask: bool = False,
) -> Image.Image:
image = ''
if bg and srconfig.get_config('DefaultBaseBG').data:
path = SP_BG_PATH / f'{bg}.jpg'
path2 = CU_BG_PATH / f'{bg}.jpg'
if path2.exists():
image = Image.open(path2)
elif path.exists():
image = Image.open(path)
CI_img = CustomizeImage(image, based_w, based_h)
img = CI_img.bg_img
color = CI_img.bg_color
if not without_mask:
color_mask = Image.new('RGBA', (based_w, based_h), color)
enka_mask = Image.open(TEXT2D_PATH / 'mask.png').resize(
(based_w, based_h)
)
img.paste(color_mask, (0, 0), enka_mask)
return img
def crop_center_img(
img: Image.Image, based_w: int, based_h: int
) -> Image.Image:
# 确定图片的长宽
based_scale = '%.3f' % (based_w / based_h)
w, h = img.size
scale_f = '%.3f' % (w / h)
new_w = math.ceil(based_h * float(scale_f))
new_h = math.ceil(based_w / float(scale_f))
if scale_f > based_scale:
resize_img = img.resize((new_w, based_h), Image.ANTIALIAS)
x1 = int(new_w / 2 - based_w / 2)
y1 = 0
x2 = int(new_w / 2 + based_w / 2)
y2 = based_h
else:
resize_img = img.resize((based_w, new_h), Image.ANTIALIAS)
x1 = 0
y1 = int(new_h / 2 - based_h / 2)
x2 = based_w
y2 = int(new_h / 2 + based_h / 2)
crop_img = resize_img.crop((x1, y1, x2, y2))
return crop_img
async def draw_pic_with_ring(
pic: Image.Image,
size: int,
bg_color: Optional[Tuple[int, int, int]] = None,
):
'''
:说明:
绘制一张带白色圆环的1:1比例图片
:参数:
* pic: `Image.Image`: 要修改的图片
* size: `int`: 最后传出图片的大小(1:1)
* bg_color: `Optional[Tuple[int, int, int]]`: 是否指定圆环内背景颜色
:返回:
* img: `Image.Image`: 图片对象
'''
img = Image.new('RGBA', (size, size))
mask = mask_pic.resize((size, size))
# ring = ring_pic.resize((size, size))
resize_pic = crop_center_img(pic, size, size)
if bg_color:
img_color = Image.new('RGBA', (size, size), bg_color)
img_color.paste(resize_pic, (0, 0), resize_pic)
img.paste(img_color, (0, 0), mask)
else:
img.paste(resize_pic, (0, 0), mask)
# img.paste(ring, (0, 0), ring)
return img
class CustomizeImage:
def __init__(
self, image: Union[str, Image.Image], based_w: int, based_h: int
) -> None:
self.bg_img = self.get_image(image, based_w, based_h)
self.bg_color = self.get_bg_color(self.bg_img, is_light=True)
self.text_color = self.get_text_color(self.bg_color)
self.highlight_color = self.get_highlight_color(self.bg_color)
self.char_color = self.get_char_color(self.bg_color)
self.bg_detail_color = self.get_bg_detail_color(self.bg_color)
self.char_high_color = self.get_char_high_color(self.bg_color)
@staticmethod
def get_image(
image: Union[str, Image.Image], based_w: int, based_h: int
) -> Image.Image:
# 获取背景图片
if isinstance(image, Image.Image):
edit_bg = image
elif image:
edit_bg = Image.open(BytesIO(get(image).content)).convert('RGBA')
else:
path = random.choice(list(bg_path.iterdir()))
edit_bg = Image.open(path).convert('RGBA')
# 确定图片的长宽
bg_img = crop_center_img(edit_bg, based_w, based_h)
return bg_img
@staticmethod
def get_dominant_color(pil_img: Image.Image) -> Tuple[int, int, int]:
img = pil_img.copy()
img = img.convert("RGBA")
img = img.resize((1, 1), resample=0)
dominant_color = img.getpixel((0, 0))
return dominant_color
@staticmethod
def get_bg_color(
edit_bg: Image.Image, is_light: Optional[bool] = False
) -> Tuple[int, int, int]:
# 获取背景主色
color = 8
q = edit_bg.quantize(colors=color, method=2)
bg_color = (0, 0, 0)
if is_light:
based_light = 195
else:
based_light = 120
temp = 9999
for i in range(color):
bg = tuple(
q.getpalette()[ # type:ignore
i * 3 : (i * 3) + 3 # noqa:E203
]
)
light_value = bg[0] * 0.3 + bg[1] * 0.6 + bg[2] * 0.1
if abs(light_value - based_light) < temp: # noqa:E203
bg_color = bg
temp = abs(light_value - based_light)
return bg_color
@staticmethod
def get_text_color(bg_color: Tuple[int, int, int]) -> Tuple[int, int, int]:
# 通过背景主色bg_color确定文字主色
r = 125
if max(*bg_color) > 255 - r:
r *= -1
text_color = (
math.floor(bg_color[0] + r if bg_color[0] + r <= 255 else 255),
math.floor(bg_color[1] + r if bg_color[1] + r <= 255 else 255),
math.floor(bg_color[2] + r if bg_color[2] + r <= 255 else 255),
)
return text_color
@staticmethod
def get_char_color(bg_color: Tuple[int, int, int]) -> Tuple[int, int, int]:
r = 140
if max(*bg_color) > 255 - r:
r *= -1
char_color = (
math.floor(bg_color[0] + 5 if bg_color[0] + r <= 255 else 255),
math.floor(bg_color[1] + 5 if bg_color[1] + r <= 255 else 255),
math.floor(bg_color[2] + 5 if bg_color[2] + r <= 255 else 255),
)
return char_color
@staticmethod
def get_char_high_color(
bg_color: Tuple[int, int, int]
) -> Tuple[int, int, int]:
r = 140
d = 20
if max(*bg_color) > 255 - r:
r *= -1
char_color = (
math.floor(bg_color[0] + d if bg_color[0] + r <= 255 else 255),
math.floor(bg_color[1] + d if bg_color[1] + r <= 255 else 255),
math.floor(bg_color[2] + d if bg_color[2] + r <= 255 else 255),
)
return char_color
@staticmethod
def get_bg_detail_color(
bg_color: Tuple[int, int, int]
) -> Tuple[int, int, int]:
r = 140
if max(*bg_color) > 255 - r:
r *= -1
bg_detail_color = (
math.floor(bg_color[0] - 20 if bg_color[0] + r <= 255 else 255),
math.floor(bg_color[1] - 20 if bg_color[1] + r <= 255 else 255),
math.floor(bg_color[2] - 20 if bg_color[2] + r <= 255 else 255),
)
return bg_detail_color
@staticmethod
def get_highlight_color(
color: Tuple[int, int, int]
) -> Tuple[int, int, int]:
red_color = color[0]
green_color = color[1]
blue_color = color[2]
highlight_color = {
'red': red_color - 127 if red_color > 127 else 127,
'green': green_color - 127 if green_color > 127 else 127,
'blue': blue_color - 127 if blue_color > 127 else 127,
}
max_color = max(highlight_color.values())
name = ''
for _highlight_color in highlight_color:
if highlight_color[_highlight_color] == max_color:
name = str(_highlight_color)
if name == 'red':
return red_color, highlight_color['green'], highlight_color['blue']
elif name == 'green':
return highlight_color['red'], green_color, highlight_color['blue']
elif name == 'blue':
return highlight_color['red'], highlight_color['green'], blue_color
else:
return 0, 0, 0 # Error

View File

@ -1,70 +0,0 @@
from pathlib import Path
from typing import Dict, List, TypedDict
from msgspec import json as msgjson
from ...version import StarRail_version
MAP = Path(__file__).parent / 'data'
version = StarRail_version
avatarName2Element_fileName = f'avatarName2Element_mapping_{version}.json'
weaponHash2Name_fileName = f'weaponHash2Name_mapping_{version}.json'
weaponHash2Type_fileName = f'weaponHash2Type_mapping_{version}.json'
skillId2Name_fileName = f'skillId2Name_mapping_{version}.json'
talentId2Name_fileName = f'talentId2Name_mapping_{version}.json'
avatarId2Name_fileName = f'avatarId2Name_mapping_{version}.json'
avatarId2Star_fileName = f'avatarId2Star_mapping_{version}.json'
artifact2attr_fileName = f'artifact2attr_mapping_{version}.json'
enName2Id_fileName = f'enName2AvatarID_mapping_{version}.json'
icon2Name_fileName = f'icon2Name_mapping_{version}.json'
avatarName2Weapon_fileName = f'avatarName2Weapon_mapping_{version}.json'
class TS(TypedDict):
Name: Dict[str, str]
Icon: Dict[str, str]
with open(MAP / avatarId2Name_fileName, 'r', encoding='UTF-8') as f:
avatarId2Name = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / icon2Name_fileName, 'r', encoding='UTF-8') as f:
icon2Name = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / artifact2attr_fileName, 'r', encoding='UTF-8') as f:
artifact2attr = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / 'propId2Name_mapping.json', 'r', encoding='UTF-8') as f:
propId2Name = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / weaponHash2Name_fileName, 'r', encoding='UTF-8') as f:
weaponHash2Name = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / weaponHash2Type_fileName, 'r', encoding='UTF-8') as f:
weaponHash2Type = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / 'artifactId2Piece_mapping.json', 'r', encoding='UTF-8') as f:
artifactId2Piece = msgjson.decode(f.read(), type=Dict[str, List[str]])
with open(MAP / skillId2Name_fileName, 'r', encoding='UTF-8') as f:
skillId2Name = msgjson.decode(f.read(), type=TS)
with open(MAP / talentId2Name_fileName, 'r', encoding='UTF-8') as f:
talentId2Name = msgjson.decode(f.read(), type=TS)
with open(MAP / avatarName2Element_fileName, 'r', encoding='UTF-8') as f:
avatarName2Element = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / avatarName2Weapon_fileName, 'r', encoding='UTF-8') as f:
avatarName2Weapon = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / 'char_alias.json', 'r', encoding='UTF-8') as f:
alias_data = msgjson.decode(f.read(), type=Dict[str, List[str]])
with open(MAP / avatarId2Star_fileName, 'r', encoding='utf8') as f:
avatarId2Star_data = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / enName2Id_fileName, 'r', encoding='utf8') as f:
enName_to_avatarId_data = msgjson.decode(f.read(), type=Dict[str, str])

View File

@ -0,0 +1,25 @@
from pathlib import Path
from typing import Dict, TypedDict
from msgspec import json as msgjson
from ...version import StarRail_version
MAP = Path(__file__).parent / 'data'
version = StarRail_version
avatarId2Name_fileName = f'avatarId2Name_mapping_{version}.json'
EquipmentID2Name_fileName = f'EquipmentID2Name_mapping_{version}.json'
class TS(TypedDict):
Name: Dict[str, str]
Icon: Dict[str, str]
with open(MAP / avatarId2Name_fileName, 'r', encoding='UTF-8') as f:
avatarId2Name = msgjson.decode(f.read(), type=Dict[str, str])
with open(MAP / EquipmentID2Name_fileName, 'r', encoding='UTF-8') as f:
EquipmentID2Name = msgjson.decode(f.read(), type=Dict[str, str])

View File

@ -0,0 +1 @@
{"20000": "锋镝", "20001": "物穰", "20002": "天倾", "20003": "琥珀", "20004": "幽邃", "20005": "齐颂", "20006": "智库", "20007": "离弦", "20008": "嘉果", "20009": "乐圮", "20010": "戍御", "20011": "渊环", "20012": "轮契", "20013": "灵钥", "20014": "相抗", "20015": "蕃息", "20016": "俱殁", "20017": "开疆", "20018": "匿影", "20019": "调和", "20020": "睿见", "21000": "一场术后对话", "21001": "晚安与睡颜", "21002": "余生的第一天", "21003": "唯有沉默", "21004": "记忆中的模样", "21005": "鼹鼠党欢迎你", "21006": "「我」的诞生", "21007": "同一种心情", "21008": "猎物的视线", "21009": "朗道的选择", "21010": "论剑", "21011": "与行星相会", "21012": "秘密誓心", "21014": "此时恰好", "21015": "决心如汗珠般闪耀", "21016": "宇宙市场趋势", "21017": "点个关注吧!", "21021": "等价交换", "21028": "暖夜不会漫长", "23001": "于夜色中", "23003": "但战斗还未结束", "23004": "以世界之名", "23005": "制胜的瞬间", "23012": "如泥酣眠", "23013": "时节不居", "24000": "记一位星神的陨落", "24001": "星海巡航", "24002": "记忆的质料", "29000": "dev_测试白板", "21013": "别让世界静下来", "23000": "银河铁道之夜", "23010": "拂晓之前", "21019": "在蓝天下", "21020": "天才们的休憩", "21023": "我们是地火", "21024": "春水初生", "21022": "延长记号", "21026": "汪!散步时间!", "21027": "早餐的仪式感", "21029": "后会有期", "21030": "这就是我啦!", "21031": "重返幽冥", "21032": "镂月裁云之意", "21033": "无处可逃", "21034": "今日亦是和平的一日", "23002": "无可取代的东西", "21018": "舞!舞!舞!", "21025": "过往未来"}

View File

@ -1,9 +1,4 @@
from .GS_MAP_PATH import (
alias_data,
avatarId2Name,
avatarId2Star_data,
enName_to_avatarId_data,
)
from .SR_MAP_PATH import EquipmentID2Name, avatarId2Name
async def avatar_id_to_name(avatar_id: str) -> str:
@ -20,26 +15,15 @@ async def name_to_avatar_id(name: str) -> str:
return avatar_id
async def avatar_id_to_char_star(char_id: str) -> str:
char_star = avatarId2Star_data[str(char_id)]
return char_star
async def weapon_id_to_name(weapon_id: str) -> str:
weapon_name = EquipmentID2Name[weapon_id]
return weapon_name
async def alias_to_char_name(char_name: str) -> str:
for i in alias_data:
if (char_name in i) or (char_name in alias_data[i]):
return i
return char_name
async def enName_to_avatarId(en_name: str) -> str:
avatar_id = enName_to_avatarId_data[en_name]
return avatar_id
async def avatarId_to_enName(avatarId: str) -> str:
for name in enName_to_avatarId_data:
if enName_to_avatarId_data[name] == avatarId:
return name
else:
return 'Ayaka'
async def name_to_weapon_id(name: str) -> str:
weapon_id = ''
for i in EquipmentID2Name:
if EquipmentID2Name[i] == name:
weapon_id = i
break
return weapon_id

View File

@ -1,4 +1,5 @@
import sys
from pathlib import Path
from gsuid_core.data_store import get_res_path
@ -7,12 +8,22 @@ sys.path.append(str(MAIN_PATH))
CONFIG_PATH = MAIN_PATH / 'config.json'
RESOURCE_PATH = MAIN_PATH / 'resource'
PLAYER_PATH = MAIN_PATH / 'players'
CHAR_PATH = RESOURCE_PATH / 'chars'
CU_BG_PATH = MAIN_PATH / 'bg'
CHAR_ICON_PATH = RESOURCE_PATH / 'char_icon'
WEAPON_PATH = RESOURCE_PATH / 'weapons'
TEXT2D_PATH = Path(__file__).parent / 'texture2d'
def init_dir():
for i in [MAIN_PATH, RESOURCE_PATH, PLAYER_PATH, CHAR_PATH, WEAPON_PATH]:
for i in [
MAIN_PATH,
RESOURCE_PATH,
PLAYER_PATH,
CHAR_ICON_PATH,
WEAPON_PATH,
TEXT2D_PATH,
CU_BG_PATH,
]:
i.mkdir(parents=True, exist_ok=True)

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB