from .config import ( SIGN_RESOURCE_PATH, SIGN_TODAY_CARD_PATH, SIGN_BORDER_PATH, SIGN_BACKGROUND_PATH, lik2level, lik2relation, level2attitude, weekdays, ) from models.sign_group_user import SignGroupUser from models.group_member_info import GroupInfoUser from nonebot.adapters.onebot.v11 import MessageSegment from configs.config import Config from utils.utils import get_user_avatar from utils.image_utils import BuildImage from utils.message_builder import image from configs.config import NICKNAME from pathlib import Path from datetime import datetime from typing import Optional, List from nonebot import Driver from io import BytesIO import asyncio import random import nonebot import os driver: Driver = nonebot.get_driver() @driver.on_startup async def init_image(): SIGN_RESOURCE_PATH.mkdir(parents=True, exist_ok=True) SIGN_TODAY_CARD_PATH.mkdir(exist_ok=True, parents=True) await GroupInfoUser.add_member_info(114514, 114514, "", datetime.min, 0) _u = await GroupInfoUser.get_member_info(114514, 114514) if _u.uid is None: await _u.update(uid=0).apply() generate_progress_bar_pic() clear_sign_data_pic() async def get_card( user: "SignGroupUser", nickname: str, add_impression: Optional[float], gold: Optional[int], gift: str, is_double: bool = False, is_card_view: bool = False, ) -> MessageSegment: user_id = user.user_qq date = datetime.now().date() _type = "view" if is_card_view else "sign" card_file = ( Path(SIGN_TODAY_CARD_PATH) / f"{user_id}_{user.group_id}_{_type}_{date}.png" ) if card_file.exists(): return image(f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card") else: if add_impression == -1: card_file = ( Path(SIGN_TODAY_CARD_PATH) / f"{user_id}_{user.group_id}_view_{date}.png" ) if card_file.exists(): return image( f"{user_id}_{user.group_id}_view_{date}.png", "sign/today_card", ) is_card_view = True ava = BytesIO(await get_user_avatar(user_id)) uid = await GroupInfoUser.get_group_member_uid(user.user_qq, user.group_id) impression_list = None if is_card_view: _, impression_list, _ = await SignGroupUser.get_all_impression( user.group_id ) return await asyncio.get_event_loop().run_in_executor( None, _generate_card, user, nickname, user_id, add_impression, gold, gift, uid, ava, impression_list, is_double, is_card_view, ) def _generate_card( user: "SignGroupUser", nickname: str, user_id: int, impression: Optional[float], gold: Optional[int], gift: str, uid: str, ava_bytes: BytesIO, impression_list: List[float], is_double: bool = False, is_card_view: bool = False, ) -> MessageSegment: ava_bk = BuildImage(140, 140, is_alpha=True) ava_border = BuildImage( 140, 140, background=SIGN_BORDER_PATH / "ava_border_01.png", ) ava = BuildImage(102, 102, background=ava_bytes) ava.circle() ava_bk.paste(ava, center_type="center") ava_bk.paste(ava_border, alpha=True, center_type="center") info_img = BuildImage(250, 150, color=(255, 255, 255, 0), font_size=15) level, next_impression, previous_impression = get_level_and_next_impression( user.impression ) interpolation = next_impression - user.impression if level == "9": level = "8" interpolation = 0 info_img.text((0, 0), f"· 好感度等级:{level} [{lik2relation[level]}]") info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}") info_img.text((0, 40), f"· 距离升级还差 {interpolation:.2f} 好感度") bar_bk = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png") bar = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar.png") bar_bk.paste( bar, ( -int( 220 * ( (next_impression - user.impression) / (next_impression - previous_impression) ) ), 0, ), True, ) font_size = 30 if "好感度双倍加持卡" in gift: font_size = 20 gift_border = BuildImage( 270, 100, background=SIGN_BORDER_PATH / "gift_border_02.png", font_size=font_size, ) gift_border.text((0, 0), gift, center_type="center") bk = BuildImage( 876, 424, background=SIGN_BACKGROUND_PATH / random.choice(os.listdir(SIGN_BACKGROUND_PATH)), font_size=25, ) A = BuildImage(876, 274, background=SIGN_RESOURCE_PATH / "white.png") line = BuildImage(2, 180, color="black") A.transparent(2) A.paste(ava_bk, (25, 80), True) A.paste(line, (200, 70)) nickname_img = BuildImage( 0, 0, plain_text=nickname, color=(255, 255, 255, 0), font_size=50, font_color=(255, 255, 255), ) if uid: uid = f"{uid}".rjust(12, "0") uid = uid[:4] + " " + uid[4:8] + " " + uid[8:] else: uid = "XXXX XXXX XXXX" uid_img = BuildImage( 0, 0, plain_text=f"UID: {uid}", color=(255, 255, 255, 0), font_size=30, font_color=(255, 255, 255), ) sign_day_img = BuildImage( 0, 0, plain_text=f"{user.checkin_count}", color=(255, 255, 255, 0), font_size=40, font_color=(211, 64, 33), ) lik_text1_img = BuildImage( 0, 0, plain_text="当前", color=(255, 255, 255, 0), font_size=20 ) lik_text2_img = BuildImage( 0, 0, plain_text=f"好感度:{user.impression:.2f}", color=(255, 255, 255, 0), font_size=30, ) watermark = BuildImage( 0, 0, plain_text=f"{NICKNAME}@{datetime.now().year}", color=(255, 255, 255, 0), font_size=15, font_color=(155, 155, 155), ) today_data = BuildImage(300, 300, color=(255, 255, 255, 0), font_size=20) if is_card_view: today_sign_text_img = BuildImage( 0, 0, plain_text="", color=(255, 255, 255, 0), font_size=30 ) if impression_list: impression_list.sort(reverse=True) index = impression_list.index(user.impression) rank_img = BuildImage( 0, 0, plain_text=f"* 此群好感排名第 {index + 1} 位", color=(255, 255, 255, 0), font_size=30, ) A.paste(rank_img, ((A.w - rank_img.w - 10), 20), True) today_data.text( (0, 0), f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}", ) today_data.text((0, 25), f"总金币:{gold}") default_setu_prob = ( Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100 ) today_data.text( (0, 50), f"色图概率:{(default_setu_prob + user.impression if user.impression < 100 else 100):.2f}%", ) today_data.text((0, 75), f"开箱次数:{(20 + int(user.impression / 3))}") _type = "view" else: A.paste(gift_border, (570, 140), True) today_sign_text_img = BuildImage( 0, 0, plain_text="今日签到", color=(255, 255, 255, 0), font_size=30 ) if is_double: today_data.text((0, 0), f"好感度 + {impression / 2:.2f} × 2") else: today_data.text((0, 0), f"好感度 + {impression:.2f}") today_data.text((0, 25), f"金币 + {gold}") _type = "sign" current_date = datetime.now() current_datetime_str = current_date.strftime("%Y-%m-%d %a %H:%M:%S") data = current_date.date() data_img = BuildImage( 0, 0, plain_text=f"时间:{current_datetime_str}", color=(255, 255, 255, 0), font_size=20, ) bk.paste(nickname_img, (30, 15), True) bk.paste(uid_img, (30, 85), True) bk.paste(A, (0, 150), alpha=True) bk.text((30, 167), "Accumulative check-in for") _x = bk.getsize("Accumulative check-in for")[0] + sign_day_img.w + 45 bk.paste(sign_day_img, (346, 158), True) bk.text((_x, 167), "days") bk.paste(data_img, (220, 370), True) bk.paste(lik_text1_img, (220, 240), True) bk.paste(lik_text2_img, (262, 234), True) bk.paste(bar_bk, (225, 275), True) bk.paste(info_img, (220, 305), True) bk.paste(today_sign_text_img, (550, 180), True) bk.paste(today_data, (580, 220), True) bk.paste(watermark, (15, 400), True) bk.save(SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png") return image(f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card") def generate_progress_bar_pic(): bg_2 = (254, 1, 254) bg_1 = (0, 245, 246) bk = BuildImage(1000, 50, is_alpha=True) img_x = BuildImage(50, 50, color=bg_2) img_x.circle() img_x.crop((25, 0, 50, 50)) img_y = BuildImage(50, 50, color=bg_1) img_y.circle() img_y.crop((0, 0, 25, 50)) A = BuildImage(950, 50) width, height = A.size step_r = (bg_2[0] - bg_1[0]) / width step_g = (bg_2[1] - bg_1[1]) / width step_b = (bg_2[2] - bg_1[2]) / width for y in range(0, width): bg_r = round(bg_1[0] + step_r * y) bg_g = round(bg_1[1] + step_g * y) bg_b = round(bg_1[2] + step_b * y) for x in range(0, height): A.point((y, x), fill=(bg_r, bg_g, bg_b)) bk.paste(img_y, (0, 0), True) bk.paste(A, (25, 0)) bk.paste(img_x, (975, 0), True) bk.save(SIGN_RESOURCE_PATH / "bar.png") A = BuildImage(950, 50) bk = BuildImage(1000, 50, is_alpha=True) img_x = BuildImage(50, 50) img_x.circle() img_x.crop((25, 0, 50, 50)) img_y = BuildImage(50, 50) img_y.circle() img_y.crop((0, 0, 25, 50)) bk.paste(img_y, (0, 0), True) bk.paste(A, (25, 0)) bk.paste(img_x, (975, 0), True) bk.save(SIGN_RESOURCE_PATH / "bar_white.png") def get_level_and_next_impression(impression: float): if impression == 0: return lik2level[10], 10, 0 keys = list(lik2level.keys()) for i in range(len(keys)): if impression > keys[i]: return lik2level[keys[i]], keys[i - 1], keys[i] return lik2level[10], 10, 0 def clear_sign_data_pic(): date = datetime.now().date() for file in os.listdir(SIGN_TODAY_CARD_PATH): if str(date) not in file: os.remove(SIGN_TODAY_CARD_PATH / file)