✨ 新增角色命座
的图片版 (#500)
@ -9,6 +9,11 @@ from .get_foods_pic import get_foods_wiki_img
|
||||
from .get_weapons_pic import get_weapons_wiki_img
|
||||
from ..genshinuid_config.gs_config import gsconfig
|
||||
from .get_artifacts_pic import get_artifacts_wiki_img
|
||||
from ..utils.map.name_covert import alias_to_char_name
|
||||
from .get_constellation_pic import (
|
||||
get_constellation_wiki_img,
|
||||
get_single_constellation_img,
|
||||
)
|
||||
from .get_wiki_text import (
|
||||
char_wiki,
|
||||
foods_wiki,
|
||||
@ -66,6 +71,7 @@ async def send_weapon(bot: Bot, ev: Event):
|
||||
@sv_wiki_text.on_prefix(('角色天赋', '查天赋'))
|
||||
async def send_talents(bot: Bot, ev: Event):
|
||||
name = ''.join(re.findall('[\u4e00-\u9fa5]', ev.text))
|
||||
name = await alias_to_char_name(name)
|
||||
num = re.findall(r'\d+', ev.text)
|
||||
if len(num) == 1:
|
||||
im = await talent_wiki(name, int(num[0]))
|
||||
@ -79,6 +85,7 @@ async def send_talents(bot: Bot, ev: Event):
|
||||
@sv_wiki_text.on_prefix(('角色介绍', '角色资料', '查角色'))
|
||||
async def send_char(bot: Bot, ev: Event):
|
||||
name = ''.join(re.findall('[\u4e00-\u9fa5]', ev.text))
|
||||
name = await alias_to_char_name(name)
|
||||
level = re.findall(r'\d+', ev.text)
|
||||
if len(level) == 1:
|
||||
im = await char_stats_wiki(name, int(level[0]))
|
||||
@ -89,7 +96,8 @@ async def send_char(bot: Bot, ev: Event):
|
||||
|
||||
@sv_wiki_text.on_prefix(('角色材料'))
|
||||
async def send_char_cost(bot: Bot, ev: Event):
|
||||
im = await char_costs_wiki(ev.text)
|
||||
name = await alias_to_char_name(ev.text)
|
||||
im = await char_costs_wiki(name)
|
||||
await bot.send(im)
|
||||
|
||||
|
||||
@ -104,18 +112,21 @@ async def send_polar(bot: Bot, ev: Event):
|
||||
m = ''.join(re.findall('[\u4e00-\u9fa5]', ev.text))
|
||||
num_re = re.findall(r'\d+', ev.text)
|
||||
|
||||
m = await alias_to_char_name(m)
|
||||
|
||||
if num_re:
|
||||
num = int(num_re[0])
|
||||
else:
|
||||
return
|
||||
'''
|
||||
if gsconfig.get_config('PicWiki').data:
|
||||
return await bot.send(await get_constellation_wiki_img(m))
|
||||
else:
|
||||
return await bot.send('请输入正确的命座数,例如 角色命座申鹤2!')
|
||||
'''
|
||||
|
||||
if num <= 0 or num > 6:
|
||||
return await bot.send('你家{}有{}命?'.format(m, num))
|
||||
im = await constellation_wiki(m, num)
|
||||
|
||||
if gsconfig.get_config('PicWiki').data:
|
||||
im = await get_single_constellation_img(m, num)
|
||||
else:
|
||||
im = await constellation_wiki(m, num)
|
||||
await bot.send(im)
|
||||
|
@ -170,7 +170,7 @@ async def draw_artifacts_wiki_img(data: Artifact) -> bytes:
|
||||
result_img.save(
|
||||
WIKI_REL_PATH / '{}.jpg'.format(data['name']),
|
||||
format='JPEG',
|
||||
quality=100,
|
||||
quality=95,
|
||||
subsampling=0,
|
||||
)
|
||||
return await convert_img(result_img)
|
||||
|
@ -1,26 +1,39 @@
|
||||
from typing import Dict, Tuple, Union
|
||||
from typing import Dict, List, Tuple, Union
|
||||
|
||||
import aiofiles
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
from .path import TEXT_PATH
|
||||
from ..utils.colors import first_color
|
||||
from ..utils.error_reply import get_error
|
||||
from ..utils.resource.download_url import download
|
||||
from ..utils.map.name_covert import name_to_avatar_id
|
||||
from ..utils.map.GS_MAP_PATH import avatarName2Element
|
||||
from ..utils.image.convert import str_lenth, convert_img
|
||||
from ..utils.fonts.genshin_fonts import gs_font_20, gs_font_28
|
||||
from ..gsuid_utils.api.minigg.request import get_constellation_info
|
||||
from ..utils.image.image_tools import get_color_bg, draw_pic_with_ring
|
||||
from ..gsuid_utils.api.minigg.request import (
|
||||
get_character_info,
|
||||
get_constellation_info,
|
||||
)
|
||||
from ..utils.resource.RESOURCE_PATH import (
|
||||
CHAR_PATH,
|
||||
ICON_PATH,
|
||||
CONSTELLATION_PATH,
|
||||
)
|
||||
from ..utils.fonts.genshin_fonts import (
|
||||
gs_font_18,
|
||||
gs_font_20,
|
||||
gs_font_28,
|
||||
gs_font_32,
|
||||
)
|
||||
from ..gsuid_utils.api.minigg.models import (
|
||||
Character,
|
||||
CharacterConstellation,
|
||||
CharacterConstellations,
|
||||
)
|
||||
from ..utils.image.image_tools import (
|
||||
get_star_png,
|
||||
get_simple_bg,
|
||||
get_unknown_png,
|
||||
draw_pic_with_ring,
|
||||
)
|
||||
|
||||
COLOR_MAP = {
|
||||
'Anemo': (0, 145, 137),
|
||||
@ -35,26 +48,47 @@ COLOR_MAP = {
|
||||
|
||||
async def get_constellation_wiki_img(name: str) -> Union[str, bytes]:
|
||||
data = await get_constellation_info(name)
|
||||
char_data = await get_character_info(name)
|
||||
if isinstance(data, int):
|
||||
return get_error(data)
|
||||
elif isinstance(char_data, int):
|
||||
return get_error(char_data)
|
||||
elif isinstance(char_data, List):
|
||||
return get_error(-400)
|
||||
else:
|
||||
pass
|
||||
'''
|
||||
full_name = data['name']
|
||||
path = CONSTELLATION_PATH / f'{full_name}.jpg'
|
||||
if path.exists():
|
||||
async with aiofiles.open(path, 'rb') as f:
|
||||
return await f.read()
|
||||
'''
|
||||
img = await draw_constellation_wiki_img(data)
|
||||
img = await draw_constellation_wiki_img(data, char_data)
|
||||
return img
|
||||
|
||||
|
||||
async def get_single_constellation_img(
|
||||
name: str, num: int
|
||||
) -> Union[str, bytes]:
|
||||
data = await get_constellation_info(name)
|
||||
if isinstance(data, int):
|
||||
return get_error(data)
|
||||
else:
|
||||
full_name = data['name']
|
||||
path = CONSTELLATION_PATH / f'{full_name}_{num}.jpg'
|
||||
if path.exists():
|
||||
async with aiofiles.open(path, 'rb') as f:
|
||||
return await f.read()
|
||||
con = data[f'c{num}']
|
||||
url = data['images'][f'c{num}']
|
||||
img = await draw_single_constellation(con, url, num, data['name'], True)
|
||||
return await convert_img(img)
|
||||
|
||||
|
||||
async def draw_single_constellation(
|
||||
data: CharacterConstellation,
|
||||
image: str,
|
||||
num: int,
|
||||
color: Tuple[int, int, int],
|
||||
char_name: str,
|
||||
is_single: bool = False,
|
||||
) -> Image.Image:
|
||||
# 计算长度
|
||||
effect = data['effect']
|
||||
@ -64,96 +98,114 @@ async def draw_single_constellation(
|
||||
else:
|
||||
effect = effect.replace('**', '」', 1)
|
||||
|
||||
effect = ' ' + effect.replace('\n', '\n ')
|
||||
effect = await str_lenth(effect, 20, 465)
|
||||
effect += '\n'
|
||||
img1 = Image.new('RGBA', (600, 1400))
|
||||
effect = await str_lenth(effect, 20, 420)
|
||||
|
||||
img1 = Image.new('RGBA', (1, 1))
|
||||
img1_draw = ImageDraw.Draw(img1)
|
||||
_, _, _, y1 = img1_draw.textbbox((0, 0), effect, gs_font_20)
|
||||
|
||||
y = 110 + y1
|
||||
img = Image.new('RGBA', (600, y))
|
||||
y = 90 + y1
|
||||
if is_single:
|
||||
bg = Image.open(TEXT_PATH / 'wiki_weapon_bg.jpg')
|
||||
img = await get_simple_bg(600, y, bg)
|
||||
else:
|
||||
img = Image.new('RGBA', (600, y))
|
||||
|
||||
img_draw = ImageDraw.Draw(img)
|
||||
|
||||
img_draw.rounded_rectangle(
|
||||
(45, 18, 555, y),
|
||||
fill=(255, 255, 255, 125),
|
||||
radius=20,
|
||||
)
|
||||
if is_single:
|
||||
img_test = Image.new('RGBA', (600, y))
|
||||
img_test_draw = ImageDraw.Draw(img_test)
|
||||
img_test_draw.rounded_rectangle(
|
||||
(28, 7, 572, 80 + y1),
|
||||
fill=(255, 255, 255, 60),
|
||||
radius=20,
|
||||
)
|
||||
img.paste(img_test, (0, 0), img_test)
|
||||
|
||||
icon_bg = Image.open(TEXT_PATH / 'ring_bg.png').resize((74, 74))
|
||||
img.paste(icon_bg, (38, 20), icon_bg)
|
||||
|
||||
icon_name = image.split('/')[-1]
|
||||
path = ICON_PATH / icon_name
|
||||
if not path.exists():
|
||||
await download(image, 8, icon_name)
|
||||
icon = Image.open(path).resize((51, 51))
|
||||
img.paste(icon, (60, 28), icon)
|
||||
icon = Image.open(path).resize((38, 38))
|
||||
img.paste(icon, (57, 37), icon)
|
||||
img_draw.text(
|
||||
(128, 52),
|
||||
f'{num}命 | {data["name"]}',
|
||||
color,
|
||||
(134, 40),
|
||||
f'{data["name"]}',
|
||||
(255, 206, 51),
|
||||
gs_font_28,
|
||||
'lm',
|
||||
)
|
||||
# line = '·' * 25 + '\n'
|
||||
# img_draw.text((300, 95), line, (243, 180, 133), gs_font_20, 'mm')
|
||||
img_draw.text((60, 95), effect, first_color, gs_font_20)
|
||||
# img_draw.text((300, 120 + y1), line, (243, 180, 133), gs_font_20, 'mm')
|
||||
img_draw.text((130, 60), effect, (230, 230, 230), gs_font_20)
|
||||
if is_single:
|
||||
img = img.convert('RGB')
|
||||
img.save(
|
||||
CONSTELLATION_PATH / f'{char_name}_{num}.jpg',
|
||||
format='JPEG',
|
||||
quality=95,
|
||||
subsampling=0,
|
||||
)
|
||||
return img
|
||||
|
||||
|
||||
async def draw_constellation_wiki_img(data: CharacterConstellations) -> bytes:
|
||||
async def draw_constellation_wiki_img(
|
||||
data: CharacterConstellations, char_data: Character
|
||||
) -> bytes:
|
||||
img_list: Dict[int, Tuple[Image.Image, int]] = {}
|
||||
element = avatarName2Element[data['name']]
|
||||
bg_color = COLOR_MAP[element]
|
||||
# element = avatarName2Element[data['name']]
|
||||
# bg_color = COLOR_MAP[element]
|
||||
|
||||
y = 0
|
||||
for i in range(1, 7):
|
||||
_img = await draw_single_constellation(
|
||||
data[f'c{i}'], data['images'][f'c{i}'], i, bg_color
|
||||
data[f'c{i}'], data['images'][f'c{i}'], i, char_data['name']
|
||||
)
|
||||
img_list[i] = (_img, _img.size[1])
|
||||
y += _img.size[1]
|
||||
title = Image.open(TEXT_PATH / 'con_title.png')
|
||||
|
||||
bg = Image.open(TEXT_PATH / 'wiki_weapon_bg.jpg')
|
||||
img = await get_simple_bg(600, 280 + y, bg)
|
||||
img_draw = ImageDraw.Draw(img)
|
||||
|
||||
desc = await str_lenth(char_data['description'], 18, 341)
|
||||
|
||||
avatar_id = await name_to_avatar_id(data['name'])
|
||||
char_img = Image.open(CHAR_PATH / f'{avatar_id}.png')
|
||||
icon = await draw_pic_with_ring(char_img, 210)
|
||||
title.paste(icon, (192, 44), icon)
|
||||
title_draw = ImageDraw.Draw(title)
|
||||
title_draw.text(
|
||||
(300, 296), f'{data["name"]}命座', bg_color, gs_font_28, 'mm'
|
||||
)
|
||||
'''
|
||||
overlay = Image.open(TEXT_PATH / 'wiki_grad_black.png').resize(
|
||||
(600, y + 400)
|
||||
)
|
||||
color_img = Image.new('RGBA', overlay.size, bg_color)
|
||||
img = ImageChops.difference(color_img, overlay)
|
||||
'''
|
||||
img = await get_color_bg(600, y + 400)
|
||||
icon = await draw_pic_with_ring(char_img, 148)
|
||||
img.paste(icon, (40, 77), icon)
|
||||
|
||||
'''
|
||||
gacha_img = Image.open(GACHA_IMG_PATH / f'{data["name"]}.png')
|
||||
gacha_img.putalpha(
|
||||
gacha_img.getchannel('A').point(
|
||||
lambda x: round(x * 0.2) if x > 0 else 0
|
||||
)
|
||||
img_draw.text((205, 161), desc, (230, 230, 230), gs_font_18)
|
||||
img_draw.text(
|
||||
(232, 102),
|
||||
f'{char_data["title"]}·{char_data["name"]}',
|
||||
(255, 255, 255),
|
||||
gs_font_32,
|
||||
'lm',
|
||||
)
|
||||
img.paste(gacha_img, (-724, 275), gacha_img)
|
||||
'''
|
||||
|
||||
temp = 365
|
||||
star_pic = get_star_png(char_data['rarity'])
|
||||
element_pic_path = TEXT_PATH / f'{char_data["element"]}.png'
|
||||
if element_pic_path.exists():
|
||||
element_pic = Image.open(element_pic_path).resize((36, 36))
|
||||
else:
|
||||
element_pic = get_unknown_png().resize((36, 36))
|
||||
img.paste(element_pic, (188, 81), element_pic)
|
||||
img.paste(star_pic, (201, 120), star_pic)
|
||||
|
||||
temp = 253
|
||||
for index in img_list:
|
||||
_img = img_list[index][0]
|
||||
img.paste(_img, (0, temp), _img)
|
||||
temp += img_list[index][1]
|
||||
|
||||
img.paste(title, (0, 0), title)
|
||||
|
||||
img = img.convert('RGB')
|
||||
img.save(
|
||||
CONSTELLATION_PATH / '{}.jpg'.format(data['name']),
|
||||
format='JPEG',
|
||||
quality=100,
|
||||
quality=95,
|
||||
subsampling=0,
|
||||
)
|
||||
return await convert_img(img)
|
||||
|
@ -123,7 +123,7 @@ async def draw_foods_wiki_img(data: Food):
|
||||
img.save(
|
||||
WIKI_FOOD_PATH / '{}.jpg'.format(data['name']),
|
||||
format='JPEG',
|
||||
quality=100,
|
||||
quality=95,
|
||||
subsampling=0,
|
||||
)
|
||||
return await convert_img(img)
|
||||
|
@ -214,7 +214,7 @@ async def draw_weapons_wiki_img(data: Weapon, stats: WeaponStats):
|
||||
img.save(
|
||||
WIKI_WEAPON_PATH / '{}.jpg'.format(data['name']),
|
||||
format='JPEG',
|
||||
quality=100,
|
||||
quality=95,
|
||||
subsampling=0,
|
||||
)
|
||||
return await convert_img(img)
|
||||
|
BIN
GenshinUID/genshinuid_wikitext/texture2D/ring_bg.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
GenshinUID/genshinuid_wikitext/texture2D/冰.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
GenshinUID/genshinuid_wikitext/texture2D/岩.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
GenshinUID/genshinuid_wikitext/texture2D/水.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
GenshinUID/genshinuid_wikitext/texture2D/火.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
GenshinUID/genshinuid_wikitext/texture2D/草.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
GenshinUID/genshinuid_wikitext/texture2D/雷.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
GenshinUID/genshinuid_wikitext/texture2D/风.png
Normal file
After Width: | Height: | Size: 10 KiB |
8
poetry.lock
generated
@ -619,14 +619,14 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6
|
||||
|
||||
[[package]]
|
||||
name = "fastapi-amis-admin"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
description = "FastAPI-Amis-Admin is a high-performance, efficient and easily extensible FastAPI admin framework. Inspired by Django-admin, and has as many powerful functions as Django-admin. "
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "fastapi_amis_admin-0.5.4-py3-none-any.whl", hash = "sha256:1cacef8681c35dbaa12a9ee7136161eca57b3f449cba18a885410ede00ced9d7"},
|
||||
{file = "fastapi_amis_admin-0.5.4.tar.gz", hash = "sha256:0d56c53a8766672b54de0e779b02c321bdd6fd09c754a1a99212bf5da24fc04f"},
|
||||
{file = "fastapi_amis_admin-0.5.5-py3-none-any.whl", hash = "sha256:8faeece0962a7db0f807e68c09fa45ed75e79ba55c9990e734de486ee6bbf4c1"},
|
||||
{file = "fastapi_amis_admin-0.5.5.tar.gz", hash = "sha256:b3c57f42fad800906cb39e0d1ea67d597747c09875ae74f20fda1d516e8b2580"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -639,7 +639,7 @@ sqlmodel = ">=0.0.7"
|
||||
[package.extras]
|
||||
all = ["fastapi-amis-admin[dev]"]
|
||||
cli = ["fastapi-amis-admin-cli (>=0.1.0,<0.2.0)"]
|
||||
dev = ["fastapi-amis-admin[test]", "pre-commit (>=2.20.0)"]
|
||||
dev = ["fastapi-amis-admin[test]", "pre-commit (>=2.20.0)", "ruff (>=0.0.261)"]
|
||||
standard = ["fastapi-amis-admin-cli (>=0.1.0,<0.2.0)", "uvicorn[standard] (>=0.19.0,<1.0)"]
|
||||
test = ["aiosqlite (>=0.15.0)", "fastapi-amis-admin[standard]", "httpx (>=0.23.0,<1.0)", "jinja2 (>=2.11.2,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-asyncio (>=0.17)", "requests (>=2.28.1)", "ujson (>=4.0.1)"]
|
||||
|
||||
|
@ -15,7 +15,7 @@ colorama==0.4.6 ; python_full_version >= "3.8.1" and python_version < "4.0" and
|
||||
dnspython==2.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0"
|
||||
email-validator==2.0.0.post1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
|
||||
et-xmlfile==1.1.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
|
||||
fastapi-amis-admin==0.5.4 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
|
||||
fastapi-amis-admin==0.5.5 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
|
||||
fastapi-user-auth==0.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
|
||||
fastapi==0.95.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
|
||||
frozenlist==1.3.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"
|
||||
|