修改优化开箱显示图片

This commit is contained in:
HibiKier 2023-04-01 01:50:34 +08:00
parent 4e35090ba6
commit 0c7c7f3987
16 changed files with 202 additions and 106 deletions

View File

@ -331,6 +331,11 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
## 更新 ## 更新
### 2023/4/1
* 修复开箱偶尔出现`未抽取到任何皮肤`
* 修改优化开箱显示图片
### 2023/3/28 ### 2023/3/28
* 补全注释`SCRIPT`中的sql语句 * 补全注释`SCRIPT`中的sql语句

View File

@ -235,7 +235,7 @@ async def _(event: MessageEvent, arg: Message = CommandArg(), cmd: str = OneComm
await update_case.finish(f"未登录, 已停止更新...") await update_case.finish(f"未登录, 已停止更新...")
rand = random.randint(300, 500) rand = random.randint(300, 500)
result = f"更新全部{type_}完成" result = f"更新全部{type_}完成"
if i < len(case_list): if i < len(case_list) - 1:
next_case = case_list[i + 1] next_case = case_list[i + 1]
result = f"将在 {rand} 秒后更新下一{type_}: {next_case}" result = f"将在 {rand} 秒后更新下一{type_}: {next_case}"
await update_case.send(f"{info}, {result}") await update_case.send(f"{info}, {result}")
@ -248,7 +248,9 @@ async def _(event: MessageEvent, arg: Message = CommandArg(), cmd: str = OneComm
else: else:
await update_case.send(f"开始{cmd}: {msg}, 请稍等") await update_case.send(f"开始{cmd}: {msg}, 请稍等")
try: try:
await update_case.send(await update_skin_data(msg, is_update_case_name), at_sender=True) await update_case.send(
await update_skin_data(msg, is_update_case_name), at_sender=True
)
except Exception as e: except Exception as e:
logger.error(f"{cmd}: {msg}", e=e) logger.error(f"{cmd}: {msg}", e=e)
await update_case.send(f"成功{cmd}: {msg} 发生错误: {type(e)}: {e}") await update_case.send(f"成功{cmd}: {msg} 发生错误: {type(e)}: {e}")

View File

@ -14,6 +14,56 @@ BASE_PATH = IMAGE_PATH / "csgo_cases"
ICON_PATH = IMAGE_PATH / "_icon" ICON_PATH = IMAGE_PATH / "_icon"
async def draw_card(skin: BuffSkin, rand: str) -> BuildImage:
"""构造抽取图片
Args:
skin (BuffSkin): BuffSkin
rand (str): 磨损
Returns:
BuildImage: BuildImage
"""
name = skin.name + "-" + skin.skin_name + "-" + skin.abrasion
file_path = BASE_PATH / cn2py(skin.case_name.split(",")[0]) / f"{cn2py(name)}.jpg"
if not file_path.exists():
logger.warning(f"皮肤图片: {name} 不存在", "开箱")
skin_bk = BuildImage(
460, 200, color=(25, 25, 25, 100), font_size=25, font="CJGaoDeGuo.otf"
)
if file_path.exists():
skin_image = BuildImage(205, 153, background=file_path)
await skin_bk.apaste(skin_image, (10, 30), alpha=True)
await skin_bk.aline((220, 10, 220, 180))
await skin_bk.atext((10, 10), skin.name, (255, 255, 255))
name_icon = BuildImage(20, 20, background=ICON_PATH / "name_white.png")
await skin_bk.apaste(name_icon, (240, 13), True)
await skin_bk.atext((265, 15), f"名称:", (255, 255, 255), font_size=20)
await skin_bk.atext(
(300, 9),
f"{skin.skin_name + ('(St)' if skin.is_stattrak else '')}",
(255, 255, 255),
)
tone_icon = BuildImage(20, 20, background=ICON_PATH / "tone_white.png")
await skin_bk.apaste(tone_icon, (240, 45), True)
await skin_bk.atext((265, 45), "品质:", (255, 255, 255), font_size=20)
await skin_bk.atext((300, 40), COLOR2NAME[skin.color][:2], COLOR2COLOR[skin.color])
type_icon = BuildImage(20, 20, background=ICON_PATH / "type_white.png")
await skin_bk.apaste(type_icon, (240, 73), True)
await skin_bk.atext((265, 75), "类型:", (255, 255, 255), font_size=20)
await skin_bk.atext((300, 70), skin.weapon_type, (255, 255, 255))
price_icon = BuildImage(20, 20, background=ICON_PATH / "price_white.png")
await skin_bk.apaste(price_icon, (240, 103), True)
await skin_bk.atext((265, 105), "价格:", (255, 255, 255), font_size=20)
await skin_bk.atext((300, 102), str(skin.sell_min_price), (0, 255, 98))
abrasion_icon = BuildImage(20, 20, background=ICON_PATH / "abrasion_white.png")
await skin_bk.apaste(abrasion_icon, (240, 133), True)
await skin_bk.atext((265, 135), "磨损:", (255, 255, 255), font_size=20)
await skin_bk.atext((300, 130), skin.abrasion, (255, 255, 255))
await skin_bk.atext((228, 165), f"({rand})", (255, 255, 255))
return skin_bk
async def generate_skin(skin: BuffSkin, update_count: int) -> Optional[BuildImage]: async def generate_skin(skin: BuffSkin, update_count: int) -> Optional[BuildImage]:
"""构造皮肤图片 """构造皮肤图片
@ -27,13 +77,13 @@ async def generate_skin(skin: BuffSkin, update_count: int) -> Optional[BuildImag
file_path = BASE_PATH / cn2py(skin.case_name.split(",")[0]) / f"{cn2py(name)}.jpg" file_path = BASE_PATH / cn2py(skin.case_name.split(",")[0]) / f"{cn2py(name)}.jpg"
if not file_path.exists(): if not file_path.exists():
logger.warning(f"皮肤图片: {name} 不存在", "查看武器箱") logger.warning(f"皮肤图片: {name} 不存在", "查看武器箱")
return None
if skin.color == "CASE": if skin.color == "CASE":
skin_img = BuildImage(200, 200, background=file_path)
case_bk = BuildImage( case_bk = BuildImage(
700, 200, color=(25, 25, 25, 100), font_size=25, font="CJGaoDeGuo.otf" 700, 200, color=(25, 25, 25, 100), font_size=25, font="CJGaoDeGuo.otf"
) )
await case_bk.apaste(skin_img, (10, 10), True) if file_path.exists():
skin_img = BuildImage(200, 200, background=file_path)
await case_bk.apaste(skin_img, (10, 10), True)
await case_bk.aline((250, 10, 250, 190)) await case_bk.aline((250, 10, 250, 190))
await case_bk.aline((280, 160, 660, 160)) await case_bk.aline((280, 160, 660, 160))
name_icon = BuildImage(30, 30, background=ICON_PATH / "box_white.png") name_icon = BuildImage(30, 30, background=ICON_PATH / "box_white.png")
@ -86,11 +136,12 @@ async def generate_skin(skin: BuffSkin, update_count: int) -> Optional[BuildImag
skin_bk = BuildImage( skin_bk = BuildImage(
235, 250, color=(25, 25, 25, 100), font_size=25, font="CJGaoDeGuo.otf" 235, 250, color=(25, 25, 25, 100), font_size=25, font="CJGaoDeGuo.otf"
) )
skin_image = BuildImage(205, 153, background=file_path) if file_path.exists():
skin_image = BuildImage(205, 153, background=file_path)
await skin_bk.apaste(skin_image, (10, 30), alpha=True)
update_count_icon = BuildImage( update_count_icon = BuildImage(
35, 35, background=ICON_PATH / "reload_white.png" 35, 35, background=ICON_PATH / "reload_white.png"
) )
await skin_bk.apaste(skin_image, (10, 30), alpha=True)
await skin_bk.aline((10, 180, 220, 180)) await skin_bk.aline((10, 180, 220, 180))
await skin_bk.atext((10, 10), skin.name, (255, 255, 255)) await skin_bk.atext((10, 10), skin.name, (255, 255, 255))
await skin_bk.apaste(update_count_icon, (140, 10), True) await skin_bk.apaste(update_count_icon, (140, 10), True)

View File

@ -72,7 +72,7 @@ class BuffSkin(Model):
) -> List["BuffSkin"]: # type: ignore ) -> List["BuffSkin"]: # type: ignore
query = cls query = cls
if case_name: if case_name:
query = query.filter(case_name=case_name) query = query.filter(case_name__contains=case_name)
query = query.filter(abrasion=abrasion, is_stattrak=is_stattrak, color=color) query = query.filter(abrasion=abrasion, is_stattrak=is_stattrak, color=color)
skin_list = await query.annotate(rand=Random()).limit(num) # type:ignore skin_list = await query.annotate(rand=Random()).limit(num) # type:ignore
num_ = num num_ = num

View File

@ -14,6 +14,7 @@ from utils.image_utils import BuildImage
from utils.message_builder import image from utils.message_builder import image
from utils.utils import cn2py from utils.utils import cn2py
from .build_image import draw_card
from .config import * from .config import *
from .models.open_cases_log import OpenCasesLog from .models.open_cases_log import OpenCasesLog
from .models.open_cases_user import OpenCasesUser from .models.open_cases_user import OpenCasesUser
@ -147,14 +148,11 @@ async def open_case(user_qq: int, group_id: int, case_name: str) -> Union[str, M
) )
logger.debug(f"添加 1 条开箱日志", "开箱", user_qq, group_id) logger.debug(f"添加 1 条开箱日志", "开箱", user_qq, group_id)
over_count = max_count - user.today_open_total over_count = max_count - user.today_open_total
img = await draw_card(skin, rand)
return ( return (
f"开启{case_name}武器箱.\n剩余开箱次数:{over_count}.\n" f"开启{case_name}武器箱.\n剩余开箱次数:{over_count}.\n"
+ image(img_path) + image(img)
+ "\n" + f"\n箱子单价:{case_price}\n花费:{17 + case_price:.2f}\n:{ridicule_result}"
+ f"皮肤:[{COLOR2NAME[skin.color]}]{skin.name}{'StatTrak™' if skin.is_stattrak else ''} | {skin.skin_name} ({skin.abrasion})\n"
f"磨损:{rand}\n"
f"价格:{price_result}\n箱子单价:{case_price}\n花费:{17 + case_price:.2f}\n"
f":{ridicule_result}"
) )
@ -191,12 +189,7 @@ async def open_multiple_case(
f"今天开箱次数不足{num}次噢,请单抽试试看(也许单抽运气更好?)" f"今天开箱次数不足{num}次噢,请单抽试试看(也许单抽运气更好?)"
f"\n剩余开箱次数:{max_count - user.today_open_total}" f"\n剩余开箱次数:{max_count - user.today_open_total}"
) )
if num < 5: logger.debug(f"尝试开启武器箱: {case_name}", "开箱", user_qq, group_id)
h = 270
elif num % 5 == 0:
h = 270 * int(num / 5)
else:
h = 270 * int(num / 5) + 270
case = cn2py(case_name) case = cn2py(case_name)
skin_count = {} skin_count = {}
img_list = [] img_list = []
@ -212,32 +205,19 @@ async def open_multiple_case(
case_price = 0 case_price = 0
if case_skin := await BuffSkin.get_or_none(case_name=case_name, color="CASE"): if case_skin := await BuffSkin.get_or_none(case_name=case_name, color="CASE"):
case_price = case_skin.sell_min_price case_price = case_skin.sell_min_price
cnt = 0 img_w, img_h = 0, 0
for skin, rand in skin_list: for skin, rand in skin_list:
img = await draw_card(skin, str(rand)[:11])
img_w, img_h = img.size
total_price += skin.sell_min_price total_price += skin.sell_min_price
rand = str(rand)[:11]
add_count(user, skin, case_price)
color_name = COLOR2CN[skin.color] color_name = COLOR2CN[skin.color]
if skin.is_stattrak:
color_name += "(暗金)"
if not skin_count.get(color_name): if not skin_count.get(color_name):
skin_count[color_name] = 0 skin_count[color_name] = 0
skin_count[color_name] += 1 skin_count[color_name] += 1
name = skin.name + "-" + skin.skin_name + "-" + skin.abrasion add_count(user, skin, case_price)
img_path = IMAGE_PATH / "csgo_cases" / case / f"{cn2py(name)}.jpg" img_list.append(img)
wImg = BuildImage(200, 270, 200, 200)
img = BuildImage(200, 200, background=img_path)
await wImg.apaste(img, (0, 0), True)
await wImg.atext(
(5, 200),
f"{skin.name}{'StatTrak™' if skin.is_stattrak else ''} | {skin.skin_name} ({skin.abrasion})",
)
cnt += 1
await wImg.atext((5, 220), f"磨损:{rand}")
await wImg.atext((5, 240), f"价格:{skin.sell_min_price}")
img_list.append(wImg)
logger.info( logger.info(
f"开启{case_name}武器箱获得 {skin.name}{'StatTrak™' if skin.is_stattrak else ''} | {skin.skin_name} ({skin.abrasion}) 磨损: [{rand}] 价格: {skin.sell_min_price}", f"开启{case_name}武器箱获得 {skin.name}{'StatTrak™' if skin.is_stattrak else ''} | {skin.skin_name} ({skin.abrasion}) 磨损: [{rand:.11f}] 价格: {skin.sell_min_price}",
"开箱", "开箱",
user_qq, user_qq,
group_id, group_id,
@ -261,16 +241,26 @@ async def open_multiple_case(
if log_list: if log_list:
await OpenCasesLog.bulk_create(log_list, 10) await OpenCasesLog.bulk_create(log_list, 10)
logger.debug(f"添加 {len(log_list)} 条开箱日志", "开箱", user_qq, group_id) logger.debug(f"添加 {len(log_list)} 条开箱日志", "开箱", user_qq, group_id)
markImg = BuildImage(1000, h, 200, 270) img_w += 10
img_h += 10
w = img_w * 5
if num < 5:
h = img_h - 10
w = img_w * num
elif not num % 5:
h = img_h * int(num / 5)
else:
h = img_h * int(num / 5) + img_h
markImg = BuildImage(w, h, img_w - 10, img_h - 10, 10)
for img in img_list: for img in img_list:
markImg.paste(img) markImg.paste(img, alpha=True)
over_count = max_count - user.today_open_total over_count = max_count - user.today_open_total
result = "" result = ""
for color_name in skin_count: for color_name in skin_count:
result += f"[{color_name}:{skin_count[color_name]}] " result += f"[{color_name}:{skin_count[color_name]}] "
return ( return (
f"开启{case_name}武器箱\n剩余开箱次数:{over_count}\n" f"开启{case_name}武器箱\n剩余开箱次数:{over_count}\n"
+ image(markImg.pic2bs4()) + image(markImg)
+ "\n" + "\n"
+ result[:-1] + result[:-1]
+ f"\n箱子单价:{case_price}\n总获取金额:{total_price:.2f}\n总花费:{(17 + case_price) * num:.2f}" + f"\n箱子单价:{case_price}\n总获取金额:{total_price:.2f}\n总花费:{(17 + case_price) * num:.2f}"

View File

@ -45,9 +45,19 @@ class CaseManager:
@classmethod @classmethod
async def reload(cls): async def reload(cls):
cls.CURRENT_CASES = ( cls.CURRENT_CASES = []
await BuffSkin.filter(case_name__not="未知武器箱").annotate().distinct().values_list("case_name", flat=True) # type: ignore case_list = await BuffSkin.filter(color="CASE").values_list(
"case_name", flat=True
) )
for case_name in (
await BuffSkin.filter(case_name__not="未知武器箱")
.annotate()
.distinct()
.values_list("case_name", flat=True)
):
for name in case_name.split(","): # type: ignore
if name not in cls.CURRENT_CASES and name in case_list:
cls.CURRENT_CASES.append(name)
async def update_skin_data(name: str, is_update_case_name: bool = False) -> str: async def update_skin_data(name: str, is_update_case_name: bool = False) -> str:

View File

@ -1,4 +1,5 @@
from pydantic.error_wrappers import ValidationError from pydantic.error_wrappers import ValidationError
from services.log import logger from services.log import logger
from utils.manager import group_manager from utils.manager import group_manager
from utils.utils import get_bot from utils.utils import get_bot
@ -7,8 +8,8 @@ from ..auth import Depends, User, token_to_user
from ..config import * from ..config import *
@app.get("/webui/group") @router.get("/group", dependencies=[token_to_user()])
async def _(user: User = Depends(token_to_user)) -> Result: async def _() -> Result:
""" """
获取群信息 获取群信息
""" """
@ -47,8 +48,8 @@ async def _(user: User = Depends(token_to_user)) -> Result:
return Result(code=200, data=group_list_result) return Result(code=200, data=group_list_result)
@app.post("/webui/group") @router.post("/group", dependencies=[token_to_user()])
async def _(group: GroupResult, user: User = Depends(token_to_user)) -> Result: async def _(group: GroupResult) -> Result:
""" """
修改群信息 修改群信息
""" """
@ -58,4 +59,4 @@ async def _(group: GroupResult, user: User = Depends(token_to_user)) -> Result:
group_manager.turn_on_group_bot_status(group_id) group_manager.turn_on_group_bot_status(group_id)
else: else:
group_manager.shutdown_group_bot_status(group_id) group_manager.shutdown_group_bot_status(group_id)
return Result(code=200, data="修改成功!") return Result(data="修改成功!")

View File

@ -1,9 +1,14 @@
from pydantic import ValidationError from pydantic import ValidationError
from configs.config import Config from configs.config import Config
from services.log import logger from services.log import logger
from utils.manager import (plugins2block_manager, plugins2cd_manager, from utils.manager import (
plugins2count_manager, plugins2settings_manager, plugins2block_manager,
plugins_manager) plugins2cd_manager,
plugins2count_manager,
plugins2settings_manager,
plugins_manager,
)
from utils.utils import get_matchers from utils.utils import get_matchers
from ..auth import Depends, User, token_to_user from ..auth import Depends, User, token_to_user
@ -12,8 +17,8 @@ from ..config import *
plugin_name_list = None plugin_name_list = None
@app.get("/webui/plugins") @router.get("/plugins", dependencies=[token_to_user()])
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result: def _(type_: Optional[str]) -> Result:
""" """
获取插件列表 获取插件列表
:param type_: 类型 normal, superuser, hidden, admin :param type_: 类型 normal, superuser, hidden, admin
@ -101,7 +106,7 @@ def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
return Result(code=200, data=plugin_list) return Result(code=200, data=plugin_list)
@app.post("/webui/plugins") @router.post("/plugins", dependencies=[token_to_user()])
def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result: def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
""" """
修改插件信息 修改插件信息
@ -126,7 +131,9 @@ def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
) or isinstance(c.default_value, float): ) or isinstance(c.default_value, float):
c.value = float(c.value) c.value = float(c.value)
elif isinstance(c.value, str) and ( elif isinstance(c.value, str) and (
isinstance(Config.get_config(plugin.model, c.key, c.value), (list, tuple)) isinstance(
Config.get_config(plugin.model, c.key, c.value), (list, tuple)
)
or isinstance(c.default_value, (list, tuple)) or isinstance(c.default_value, (list, tuple))
): ):
default_value = Config.get_config(plugin.model, c.key, c.value) default_value = Config.get_config(plugin.model, c.key, c.value)
@ -161,7 +168,9 @@ def _(plugin: Plugin, user: User = Depends(token_to_user)) -> Result:
) )
for key in plugins2settings_manager.keys(): for key in plugins2settings_manager.keys():
if isinstance(plugins2settings_manager[key].cmd, str): if isinstance(plugins2settings_manager[key].cmd, str):
plugins2settings_manager[key].cmd = plugins2settings_manager[key].cmd.split(',') plugins2settings_manager[key].cmd = plugins2settings_manager[key].cmd.split(
","
)
plugins2settings_manager.save() plugins2settings_manager.save()
plugins_manager.save() plugins_manager.save()
return Result(code=200, data="修改成功!") return Result(code=200, data="修改成功!")

View File

@ -6,8 +6,8 @@ from ..auth import Depends, User, token_to_user
from ..config import * from ..config import *
@app.get("/webui/request") @router.get("/webui/request", dependencies=[token_to_user()])
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result: def _(type_: Optional[str]) -> Result:
req_data = requests_manager.get_data() req_data = requests_manager.get_data()
req_list = [] req_list = []
if type_ in ["group", "private"]: if type_ in ["group", "private"]:
@ -19,8 +19,8 @@ def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
return Result(code=200, data=req_list) return Result(code=200, data=req_list)
@app.delete("/webui/request") @router.delete("/webui/request", dependencies=[token_to_user()])
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result: def _(type_: Optional[str]) -> Result:
""" """
清空请求 清空请求
:param type_: 类型 :param type_: 类型
@ -29,8 +29,8 @@ def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
return Result(code=200) return Result(code=200)
@app.post("/webui/request") @router.post("/webui/request", dependencies=[token_to_user()])
async def _(parma: RequestParma, user: User = Depends(token_to_user)) -> Result: async def _(parma: RequestParma) -> Result:
""" """
操作请求 操作请求
:param parma: 参数 :param parma: 参数

View File

@ -4,6 +4,7 @@ from pathlib import Path
import psutil import psutil
import ujson as json import ujson as json
from configs.path_config import ( from configs.path_config import (
DATA_PATH, DATA_PATH,
FONT_PATH, FONT_PATH,
@ -28,21 +29,21 @@ memory_data = {"data": []}
disk_data = {"data": []} disk_data = {"data": []}
@app.get("/webui/system") @router.get("/system", dependencies=[token_to_user()])
async def _() -> Result: async def _() -> Result:
return await get_system_data() return await get_system_data()
@app.get("/webui/system/status") @router.get("/webui/system/status", dependencies=[token_to_user()])
async def _(user: User = Depends(token_to_user)) -> Result: async def _() -> Result:
return Result( return Result(
code=200, code=200,
data=await asyncio.get_event_loop().run_in_executor(None, _get_system_status), data=await asyncio.get_event_loop().run_in_executor(None, _get_system_status),
) )
@app.get("/webui/system/disk") @router.get("/webui/system/disk", dependencies=[token_to_user()])
async def _(type_: Optional[str] = None, user: User = Depends(token_to_user)) -> Result: async def _(type_: Optional[str] = None) -> Result:
return Result( return Result(
code=200, code=200,
data=await asyncio.get_event_loop().run_in_executor( data=await asyncio.get_event_loop().run_in_executor(
@ -51,8 +52,8 @@ async def _(type_: Optional[str] = None, user: User = Depends(token_to_user)) ->
) )
@app.get("/webui/system/statusList") @router.get("/webui/system/statusList", dependencies=[token_to_user()])
async def _(user: User = Depends(token_to_user)) -> Result: async def _() -> Result:
global cpu_data, memory_data, disk_data global cpu_data, memory_data, disk_data
await asyncio.get_event_loop().run_in_executor(None, _get_system_status) await asyncio.get_event_loop().run_in_executor(None, _get_system_status)
cpu_rst = cpu_data["data"][-10:] if len(cpu_data["data"]) > 10 else cpu_data["data"] cpu_rst = cpu_data["data"][-10:] if len(cpu_data["data"]) > 10 else cpu_data["data"]
@ -74,7 +75,7 @@ async def _(user: User = Depends(token_to_user)) -> Result:
) )
async def get_system_data(user: User = Depends(token_to_user)): async def get_system_data():
""" """
说明: 说明:
获取系统信息资源文件大小网络状态等 获取系统信息资源文件大小网络状态等
@ -105,7 +106,7 @@ async def get_system_data(user: User = Depends(token_to_user)):
) )
def _get_system_status(user: User = Depends(token_to_user)) -> SystemStatus: def _get_system_status() -> SystemStatus:
""" """
说明: 说明:
获取系统信息等 获取系统信息等
@ -123,7 +124,7 @@ def _get_system_status(user: User = Depends(token_to_user)) -> SystemStatus:
def _get_system_disk( def _get_system_disk(
type_: Optional[str], user: User = Depends(token_to_user) type_: Optional[str],
) -> Union[SystemFolderSize, Dict[str, Union[float, datetime]]]: ) -> Union[SystemFolderSize, Dict[str, Union[float, datetime]]]:
""" """
说明: 说明:

View File

@ -1,16 +1,18 @@
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
from configs.path_config import DATA_PATH
from typing import Optional from typing import Optional
from starlette import status
import nonebot
from fastapi import Depends, HTTPException from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from configs.config import Config
from jose import JWTError, jwt from jose import JWTError, jwt
import nonebot from pydantic import BaseModel
from starlette import status
from ..config import Result from configs.config import Config
from configs.path_config import DATA_PATH
from ..config import Result, router
app = nonebot.get_app() app = nonebot.get_app()
@ -19,14 +21,14 @@ SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256" ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30 ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="webui/login") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/login")
token_file = DATA_PATH / "web_ui" / "token.json" token_file = DATA_PATH / "web_ui" / "token.json"
token_file.parent.mkdir(parents=True, exist_ok=True) token_file.parent.mkdir(parents=True, exist_ok=True)
token_data = {"token": []} token_data = {"token": []}
if token_file.exists(): if token_file.exists():
token_data = json.load(open(token_file, 'r', encoding='utf8')) token_data = json.load(open(token_file, "r", encoding="utf8"))
class User(BaseModel): class User(BaseModel):
@ -39,11 +41,6 @@ class Token(BaseModel):
token_type: str token_type: str
# USER_LIST = [
# User(username="admin", password="123")
# ]
def get_user(uname: str) -> Optional[User]: def get_user(uname: str) -> Optional[User]:
username = Config.get_config("web-ui", "username") username = Config.get_config("web-ui", "username")
password = Config.get_config("web-ui", "password") password = Config.get_config("web-ui", "password")
@ -59,24 +56,26 @@ form_exception = HTTPException(
def create_token(user: User, expires_delta: Optional[timedelta] = None): def create_token(user: User, expires_delta: Optional[timedelta] = None):
expire = datetime.utcnow() + expires_delta or timedelta(minutes=15) expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
return jwt.encode( return jwt.encode(
claims={"sub": user.username, "exp": expire}, claims={"sub": user.username, "exp": expire},
key=SECRET_KEY, key=SECRET_KEY,
algorithm=ALGORITHM algorithm=ALGORITHM,
) )
@app.post("/webui/login") @router.post("/login")
async def login_get_token(form_data: OAuth2PasswordRequestForm = Depends()): async def login_get_token(form_data: OAuth2PasswordRequestForm = Depends()):
user: User = get_user(form_data.username) user = get_user(form_data.username)
if not user or user.password != form_data.password: if not user or user.password != form_data.password:
raise form_exception raise form_exception
access_token = create_token(user=user, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)) access_token = create_token(
user=user, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
)
token_data["token"].append(access_token) token_data["token"].append(access_token)
if len(token_data["token"]) > 3: if len(token_data["token"]) > 3:
token_data["token"] = token_data["token"][1:] token_data["token"] = token_data["token"][1:]
with open(token_file, 'w', encoding="utf8") as f: with open(token_file, "w", encoding="utf8") as f:
json.dump(token_data, f, ensure_ascii=False, indent=4) json.dump(token_data, f, ensure_ascii=False, indent=4)
return {"access_token": access_token, "token_type": "bearer"} return {"access_token": access_token, "token_type": "bearer"}
@ -88,20 +87,21 @@ credentials_exception = HTTPException(
) )
@app.post("/webui/auth") @app.post("/auth")
def token_to_user(token: str = Depends(oauth2_scheme)): def token_to_user(token: str = Depends(oauth2_scheme)):
if token not in token_data["token"]: if token not in token_data["token"]:
try: try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username, expire = payload.get("sub"), payload.get("exp") username, expire = payload.get("sub"), payload.get("exp")
user = get_user(username) user = get_user(username) # type: ignore
if user is None: if user is None:
raise JWTError raise JWTError
except JWTError: except JWTError:
return Result(code=401) return Result(code=401)
return Result(code=200, data="ok") return Result(code=200, info="登录成功")
if __name__ == '__main__': if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8080) uvicorn.run(app, host="127.0.0.1", port=8080)

View File

@ -1,9 +1,10 @@
from typing import Optional, List, Any, Union, Dict
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
from datetime import datetime from datetime import datetime
import nonebot from typing import Any, Dict, List, Optional, Union
import nonebot
from fastapi import APIRouter
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
app = nonebot.get_app() app = nonebot.get_app()
@ -17,11 +18,14 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
router = APIRouter(tags=["api"])
class CdLimit(BaseModel): class CdLimit(BaseModel):
""" """
Cd 限制 Cd 限制
""" """
cd: int cd: int
status: bool status: bool
check_type: str check_type: str
@ -33,6 +37,7 @@ class BlockLimit(BaseModel):
""" """
Block限制 Block限制
""" """
status: bool status: bool
check_type: str check_type: str
limit_type: str limit_type: str
@ -43,6 +48,7 @@ class CountLimit(BaseModel):
""" """
Count限制 Count限制
""" """
max_count: int max_count: int
status: bool status: bool
limit_type: str limit_type: str
@ -53,6 +59,7 @@ class PluginManager(BaseModel):
""" """
插件信息 插件信息
""" """
plugin_name: str # 插件名称 plugin_name: str # 插件名称
status: Optional[bool] # 插件状态 status: Optional[bool] # 插件状态
error: Optional[bool] # 加载状态 error: Optional[bool] # 加载状态
@ -65,6 +72,7 @@ class PluginSettings(BaseModel):
""" """
插件基本设置 插件基本设置
""" """
level: Optional[int] # 群权限等级 level: Optional[int] # 群权限等级
default_status: Optional[bool] # 默认开关 default_status: Optional[bool] # 默认开关
limit_superuser: Optional[bool] # 是否限制超级用户 limit_superuser: Optional[bool] # 是否限制超级用户
@ -77,6 +85,7 @@ class PluginConfig(BaseModel):
""" """
插件配置项 插件配置项
""" """
id: int id: int
key: str key: str
value: Optional[Any] value: Optional[Any]
@ -88,6 +97,7 @@ class Plugin(BaseModel):
""" """
插件 插件
""" """
model: str # 模块 model: str # 模块
plugin_settings: Optional[PluginSettings] plugin_settings: Optional[PluginSettings]
plugin_manager: Optional[PluginManager] plugin_manager: Optional[PluginManager]
@ -101,6 +111,7 @@ class Group(BaseModel):
""" """
群组信息 群组信息
""" """
group_id: int group_id: int
group_name: str group_name: str
member_count: int member_count: int
@ -111,6 +122,7 @@ class Task(BaseModel):
""" """
被动技能 被动技能
""" """
name: str name: str
nameZh: str nameZh: str
status: bool status: bool
@ -120,6 +132,7 @@ class GroupResult(BaseModel):
""" """
群组返回数据 群组返回数据
""" """
group: Group group: Group
level: int level: int
status: bool status: bool
@ -131,6 +144,7 @@ class RequestResult(BaseModel):
""" """
好友/群组请求管理 好友/群组请求管理
""" """
oid: str oid: str
id: int id: int
flag: str flag: str
@ -148,6 +162,7 @@ class RequestParma(BaseModel):
""" """
操作请求接收数据 操作请求接收数据
""" """
id: int id: int
handle: str handle: str
type: str type: str
@ -157,6 +172,7 @@ class SystemStatus(BaseModel):
""" """
系统状态 系统状态
""" """
cpu: int cpu: int
memory: int memory: int
disk: int disk: int
@ -167,6 +183,7 @@ class SystemNetwork(BaseModel):
""" """
系统网络状态 系统网络状态
""" """
baidu: int baidu: int
google: int google: int
@ -175,6 +192,7 @@ class SystemFolderSize(BaseModel):
""" """
资源文件占比 资源文件占比
""" """
font_dir_size: float font_dir_size: float
image_dir_size: float image_dir_size: float
text_dir_size: float text_dir_size: float
@ -189,6 +207,7 @@ class SystemStatusList(BaseModel):
""" """
状态记录 状态记录
""" """
cpu_data: List[Dict[str, Union[float, str]]] cpu_data: List[Dict[str, Union[float, str]]]
memory_data: List[Dict[str, Union[float, str]]] memory_data: List[Dict[str, Union[float, str]]]
disk_data: List[Dict[str, Union[float, str]]] disk_data: List[Dict[str, Union[float, str]]]
@ -198,6 +217,7 @@ class SystemResult(BaseModel):
""" """
系统api返回 系统api返回
""" """
status: SystemStatus status: SystemStatus
network: SystemNetwork network: SystemNetwork
disk: SystemFolderSize disk: SystemFolderSize
@ -208,5 +228,7 @@ class Result(BaseModel):
""" """
总体返回 总体返回
""" """
code: int
data: Any code: int = 200
info: str = "操作成功"
data: Any = None

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -160,6 +160,7 @@ class BuildImage:
h: int, h: int,
paste_image_width: int = 0, paste_image_width: int = 0,
paste_image_height: int = 0, paste_image_height: int = 0,
paste_space: int = 0,
color: Union[str, Tuple[int, int, int], Tuple[int, int, int, int]] = None, color: Union[str, Tuple[int, int, int], Tuple[int, int, int, int]] = None,
image_mode: ModeType = "RGBA", image_mode: ModeType = "RGBA",
font_size: int = 10, font_size: int = 10,
@ -177,6 +178,7 @@ class BuildImage:
:param h: 自定义图片的高度h=0时为图片原本高度 :param h: 自定义图片的高度h=0时为图片原本高度
:param paste_image_width: 当图片做为背景图时设置贴图的宽度用于贴图自动换行 :param paste_image_width: 当图片做为背景图时设置贴图的宽度用于贴图自动换行
:param paste_image_height: 当图片做为背景图时设置贴图的高度用于贴图自动换行 :param paste_image_height: 当图片做为背景图时设置贴图的高度用于贴图自动换行
:param paste_space: 自动贴图间隔
:param color: 生成图片的颜色 :param color: 生成图片的颜色
:param image_mode: 图片的类型 :param image_mode: 图片的类型
:param font_size: 文字大小 :param font_size: 文字大小
@ -190,6 +192,7 @@ class BuildImage:
self.h = int(h) self.h = int(h)
self.paste_image_width = int(paste_image_width) self.paste_image_width = int(paste_image_width)
self.paste_image_height = int(paste_image_height) self.paste_image_height = int(paste_image_height)
self.paste_space = int(paste_space)
self._current_w = 0 self._current_w = 0
self._current_h = 0 self._current_h = 0
self.uid = uuid.uuid1() self.uid = uuid.uuid1()
@ -277,7 +280,9 @@ class BuildImage:
:param center_type: 居中类型可能的值 center: 完全居中by_width: 水平居中by_height: 垂直居中 :param center_type: 居中类型可能的值 center: 完全居中by_width: 水平居中by_height: 垂直居中
:param allow_negative: 允许使用负数作为坐标且不超出图片范围从右侧开始计算 :param allow_negative: 允许使用负数作为坐标且不超出图片范围从右侧开始计算
""" """
await self.loop.run_in_executor(None, self.paste, img, pos, alpha, center_type, allow_negative) await self.loop.run_in_executor(
None, self.paste, img, pos, alpha, center_type, allow_negative
)
def paste( def paste(
self, self,
@ -324,7 +329,7 @@ class BuildImage:
img = img.markImg img = img.markImg
if self._current_w >= self.w: if self._current_w >= self.w:
self._current_w = 0 self._current_w = 0
self._current_h += self.paste_image_height self._current_h += self.paste_image_height + self.paste_space
if not pos: if not pos:
pos = (self._current_w, self._current_h) pos = (self._current_w, self._current_h)
if alpha: if alpha:
@ -335,7 +340,7 @@ class BuildImage:
self.markImg.paste(img, pos, img) self.markImg.paste(img, pos, img)
else: else:
self.markImg.paste(img, pos) self.markImg.paste(img, pos)
self._current_w += self.paste_image_width self._current_w += self.paste_image_width + self.paste_space
@classmethod @classmethod
def get_text_size(cls, msg: str, font: str, font_size: int) -> Tuple[int, int]: def get_text_size(cls, msg: str, font: str, font_size: int) -> Tuple[int, int]:
@ -469,7 +474,7 @@ class BuildImage:
"center_type must be 'center', 'by_width' or 'by_height'" "center_type must be 'center', 'by_width' or 'by_height'"
) )
w, h = self.w, self.h w, h = self.w, self.h
longgest_text = '' longgest_text = ""
sentence = text.split("\n") sentence = text.split("\n")
for x in sentence: for x in sentence:
longgest_text = x if len(x) > len(longgest_text) else longgest_text longgest_text = x if len(x) > len(longgest_text) else longgest_text