Skip to content

Commit

Permalink
✨ 新增抽卡成就功能
Browse files Browse the repository at this point in the history
  • Loading branch information
monsterxcn committed Dec 30, 2022
1 parent aca878e commit 0f27711
Show file tree
Hide file tree
Showing 7 changed files with 581 additions and 11 deletions.
Binary file added data/gachalogs/HYWH-85W.ttf
Binary file not shown.
Binary file added data/gachalogs/achieve-detail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/gachalogs/achieve-nodetail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 20 additions & 4 deletions nonebot_plugin_gachalogs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

from .__meta__ import SAFE_GROUP
from .data_export import gnrtGachaFile
from .data_render import gnrtGachaInfo
from .data_render import gnrtGachaInfo, gnrtGachaArchieve
from .data_source import (
checkAuthKey,
configHelper,
Expand All @@ -36,15 +36,16 @@ async def _OFFLINE_FILE(bot: "rBot", event: "rEvent") -> bool:
if isinstance(event, NoticeEvent):
if event.notice_type == "offline_file": # type: ignore
if hasattr(event, "user_id") and hasattr(event, "file"):
if any(
str(event.file.get("name", "")).lower().endswith(t) # type: ignore
for t in ["json", "xlsx"]
filename = str(event.file.get("name", "")).lower() # type: ignore
if any(filename.endswith(t) for t in ["json", "xlsx"]) or (
filename.startswith("gachalogs-") and filename.endswith(".json.bak")
):
return True
return False


mainMatcher = on_command("抽卡记录", aliases={"ckjl"}, priority=5)
aMatcher = on_command("抽卡成就", aliases={"ckcj"}, priority=5)
eMatcher = on_command("抽卡记录导出", aliases={"logexp", "ckjldc"}, priority=5)
dMatcher = on_command("抽卡记录删除", aliases={"logdel", "ckjlsc"}, priority=5)
fMatcher = on_notice(rule=Rule(_OFFLINE_FILE))
Expand Down Expand Up @@ -169,6 +170,21 @@ async def mainGot(bot: Bot, event: MessageEvent, state: T_State):
await mainMatcher.finish(MessageSegment.image(imgB64))


@aMatcher.handle()
async def gachaAchievement(bot: Bot, event: MessageEvent, state: T_State):
qq = event.get_user_id()
# 读取配置数据
cfg = await configHelper(qq)
if cfg.get("error"):
await aMatcher.finish(cfg["error"], at_sender=True)
# 生成抽卡成就
uid, logs = await logsHelper(cfg["logs"])
if not logs:
await aMatcher.finish()
imgB64 = await gnrtGachaArchieve(logs, uid)
await aMatcher.finish(MessageSegment.image(imgB64))


@eMatcher.handle()
async def gachaExport(bot: Bot, event: MessageEvent, state: T_State):
qq = event.get_user_id()
Expand Down
34 changes: 32 additions & 2 deletions nonebot_plugin_gachalogs/__meta__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,36 @@
for chunk in r.iter_bytes():
f.write(chunk)

# 成就绘图资源
ACHIEVE_FONT = (
(Path(cfg.gachalogs_achieve_font))
if hasattr(cfg, "gachalogs_achieve_font")
else (LOCAL_DIR / "HYWH-85W.ttf")
)
if not ACHIEVE_FONT.exists():
with stream(
"GET", "https://cdn.monsterx.cn/bot/gachalogs/HYWH-85W.ttf", verify=False
) as r:
with open(ACHIEVE_FONT, "wb") as f:
for chunk in r.iter_bytes():
f.write(chunk)
ACHIEVE_BG = LOCAL_DIR / "achieve-nodetail.png"
if not ACHIEVE_BG.exists():
with stream(
"GET", "https://cdn.monsterx.cn/bot/gachalogs/achieve-nodetail.png", verify=False
) as r:
with open(ACHIEVE_BG, "wb") as f:
for chunk in r.iter_bytes():
f.write(chunk)
ACHIEVE_BG_DETAIL = LOCAL_DIR / "achieve-detail.png"
if not ACHIEVE_BG_DETAIL.exists():
with stream(
"GET", "https://cdn.monsterx.cn/bot/gachalogs/achieve-detail.png", verify=False
) as r:
with open(ACHIEVE_BG_DETAIL, "wb") as f:
for chunk in r.iter_bytes():
f.write(chunk)

# 卡池信息
_pools = LOCAL_DIR / "GachaEvent.json"
if (not _pools.exists()) or (
Expand Down Expand Up @@ -87,6 +117,6 @@
POOL_API = "https://webstatic.mihoyo.com/hk4e/gacha_info/cn_gf01/gacha/list.json"
AUTHKEY_API = "https://api-takumi.mihoyo.com/binding/api/genAuthKey"
# 米游社请求验证
CLIENT_SALT = "dWCcD2FsOUXEstC5f9xubswZxEeoBOTc"
CLIENT_VERSION = "2.28.1"
CLIENT_SALT = "fdv0fY9My9eA7MR0NpjGP9RjueFvjUSQ" # "dWCcD2FsOUXEstC5f9xubswZxEeoBOTc"
CLIENT_VERSION = "2.40.1" # "2.28.1"
CLIENT_TYPE = "2"
130 changes: 125 additions & 5 deletions nonebot_plugin_gachalogs/data_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from datetime import datetime
from io import BytesIO
from math import floor
from time import localtime, strftime
from time import localtime, strftime, time
from typing import Dict, List, Literal, Tuple

import matplotlib.pyplot as plt
Expand All @@ -11,7 +11,16 @@
from PIL import Image, ImageDraw, ImageFont
from pytz import timezone

from .__meta__ import GACHA_TYPE, PIE_FONT, PIL_FONT, POOL_INFO
from .__meta__ import (
GACHA_TYPE,
PIE_FONT,
PIL_FONT,
POOL_INFO,
ACHIEVE_FONT,
ACHIEVE_BG,
ACHIEVE_BG_DETAIL,
)
from .gacha_achieve import calcAchievement


def percent(a: int, b: int, rt: Literal["pct", "rgb"] = "pct") -> str:
Expand Down Expand Up @@ -62,22 +71,22 @@ def getPoolTag(num: int) -> Tuple[str, str, str]:
if num >= 72:
return "非", "#6c6c6c", "#505a6d"
if num >= 68:
return "", "#b8b8b8", "#9a9fa8"
return "", "#b8b8b8", "#9a9fa8"
if num >= 60:
return "平", "#a0bb77", "#9ed052"
if num >= 54:
return "吉", "#aa96c7", "#9d78d2"
return "欧", "#e4b95b", "#e4b44d"


def fs(size: int) -> ImageFont.FreeTypeFont:
def fs(size: int, achieve: bool = False) -> ImageFont.FreeTypeFont:
"""
Pillow 绘制字体设置
* ``param size: int`` 字体大小
- ``return: ImageFont.FreeTypeFont`` Pillow 字体对象
"""
return ImageFont.truetype(str(PIL_FONT), size=size)
return ImageFont.truetype(str(ACHIEVE_FONT if achieve else PIL_FONT), size=size)


async def colorfulFive(
Expand Down Expand Up @@ -538,3 +547,114 @@ async def gnrtGachaInfo(rawData: Dict, uid: str) -> bytes:
buf = BytesIO()
resultImg.save(buf, format="PNG")
return buf.getvalue()


async def gnrtGachaArchieve(rawData: Dict, uid: str) -> bytes:
"""
抽卡成就图片生成,通过 pillow 绘制图片
* ``param rawData: Dict`` 抽卡记录数据
* ``param uid: str`` 用户 UID
- ``return: bytes`` 图片字节数据
"""
achievements = await calcAchievement(rawData)
result = Image.new("RGBA", (720, 110 * len(achievements) + 10 + 100), "#f9f9f9")
drawer = ImageDraw.Draw(result)

# 标题
title = f"UID{uid} 抽卡成就"
drawer.text(
(int((720 - fs(36, True).getlength(title)) / 2), 20),
title,
font=fs(36, True),
fill="black",
stroke_width=1,
stroke_fill="grey",
)
# TODO: 使用绘制成就时的最后一抽的时间
timeStr = strftime("%Y-%m-%d %H:%M:%S", localtime(int(time())))
drawer.text(
(int((720 - fs(18, True).getlength(timeStr)) / 2), 70),
timeStr,
font=fs(18, True),
fill="#808080",
)

bgPure = Image.open(ACHIEVE_BG)
bgDetail = Image.open(ACHIEVE_BG_DETAIL)
for aIdx, achievement in enumerate(achievements):
startHeight = 110 * (aIdx + 1)
bg = bgDetail if achievement.get("value") else bgPure
result.paste(bg, (10, startHeight), bg)
# 名称
drawer.text(
(115, startHeight + 18),
achievement["title"],
font=fs(22, True),
fill="#585757",
)
# 描述
multilineText, tmpText, tmpLength = [], "", 0
maxLength = 445 if achievement.get("value") else 565
for s in achievement["info"]:
length = fs(16, True).getlength(s)
if tmpLength + length <= maxLength:
tmpText += s
tmpLength += length
else:
multilineText.append(tmpText)
tmpText = s
tmpLength = length
if tmpText.strip():
multilineText.append(tmpText)
multilineText = [s.strip() for s in multilineText if s.strip()]
spacing = (0.3 if len(multilineText) < 3 else 0.1) * 16
drawer.multiline_text(
(
125,
startHeight
+ 100
- 18
- 16 * len(multilineText)
- spacing * (len(multilineText) - 1),
),
"\n".join(multilineText),
font=fs(16, True),
fill="#988B81",
spacing=spacing,
align="left",
)
# 详情
if achievement.get("value"):
drawer.text(
(
int(582 + (128 - fs(20, True).getlength(achievement["value"])) / 2),
int(
startHeight
+ (100 - fs(20, True).getbbox(achievement["value"])[-1]) / 2
),
),
achievement["value"],
font=fs(20, True),
fill="#988B81",
)
drawer.text(
(
int(
582
+ (128 - fs(15, True).getlength(achievement["achievedTime"])) / 2
),
int(
startHeight
+ 76
+ (20 - fs(15, True).getbbox(achievement["value"])[-1]) / 2
),
),
achievement["achievedTime"],
font=fs(15, True),
fill="#988B81",
)

buf = BytesIO()
result.save(buf, format="PNG")
return buf.getvalue()
Loading

0 comments on commit 0f27711

Please sign in to comment.