mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 06:12:53 +08:00
修改优化开箱显示图片
This commit is contained in:
parent
4e35090ba6
commit
0c7c7f3987
@ -331,6 +331,11 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
|
||||
|
||||
## 更新
|
||||
|
||||
### 2023/4/1
|
||||
|
||||
* 修复开箱偶尔出现`未抽取到任何皮肤`
|
||||
* 修改优化开箱显示图片
|
||||
|
||||
### 2023/3/28
|
||||
|
||||
* 补全注释`SCRIPT`中的sql语句
|
||||
|
||||
@ -235,7 +235,7 @@ async def _(event: MessageEvent, arg: Message = CommandArg(), cmd: str = OneComm
|
||||
await update_case.finish(f"未登录, 已停止更新...")
|
||||
rand = random.randint(300, 500)
|
||||
result = f"更新全部{type_}完成"
|
||||
if i < len(case_list):
|
||||
if i < len(case_list) - 1:
|
||||
next_case = case_list[i + 1]
|
||||
result = f"将在 {rand} 秒后更新下一{type_}: {next_case}"
|
||||
await update_case.send(f"{info}, {result}")
|
||||
@ -248,7 +248,9 @@ async def _(event: MessageEvent, arg: Message = CommandArg(), cmd: str = OneComm
|
||||
else:
|
||||
await update_case.send(f"开始{cmd}: {msg}, 请稍等")
|
||||
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:
|
||||
logger.error(f"{cmd}: {msg}", e=e)
|
||||
await update_case.send(f"成功{cmd}: {msg} 发生错误: {type(e)}: {e}")
|
||||
|
||||
@ -14,6 +14,56 @@ BASE_PATH = IMAGE_PATH / "csgo_cases"
|
||||
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]:
|
||||
"""构造皮肤图片
|
||||
|
||||
@ -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"
|
||||
if not file_path.exists():
|
||||
logger.warning(f"皮肤图片: {name} 不存在", "查看武器箱")
|
||||
return None
|
||||
if skin.color == "CASE":
|
||||
skin_img = BuildImage(200, 200, background=file_path)
|
||||
case_bk = BuildImage(
|
||||
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((280, 160, 660, 160))
|
||||
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(
|
||||
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(
|
||||
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.atext((10, 10), skin.name, (255, 255, 255))
|
||||
await skin_bk.apaste(update_count_icon, (140, 10), True)
|
||||
|
||||
@ -72,7 +72,7 @@ class BuffSkin(Model):
|
||||
) -> List["BuffSkin"]: # type: ignore
|
||||
query = cls
|
||||
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)
|
||||
skin_list = await query.annotate(rand=Random()).limit(num) # type:ignore
|
||||
num_ = num
|
||||
|
||||
@ -14,6 +14,7 @@ from utils.image_utils import BuildImage
|
||||
from utils.message_builder import image
|
||||
from utils.utils import cn2py
|
||||
|
||||
from .build_image import draw_card
|
||||
from .config import *
|
||||
from .models.open_cases_log import OpenCasesLog
|
||||
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)
|
||||
over_count = max_count - user.today_open_total
|
||||
img = await draw_card(skin, rand)
|
||||
return (
|
||||
f"开启{case_name}武器箱.\n剩余开箱次数:{over_count}.\n"
|
||||
+ image(img_path)
|
||||
+ "\n"
|
||||
+ 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}"
|
||||
+ image(img)
|
||||
+ f"\n箱子单价:{case_price}\n花费:{17 + case_price:.2f}\n:{ridicule_result}"
|
||||
)
|
||||
|
||||
|
||||
@ -191,12 +189,7 @@ async def open_multiple_case(
|
||||
f"今天开箱次数不足{num}次噢,请单抽试试看(也许单抽运气更好?)"
|
||||
f"\n剩余开箱次数:{max_count - user.today_open_total}"
|
||||
)
|
||||
if num < 5:
|
||||
h = 270
|
||||
elif num % 5 == 0:
|
||||
h = 270 * int(num / 5)
|
||||
else:
|
||||
h = 270 * int(num / 5) + 270
|
||||
logger.debug(f"尝试开启武器箱: {case_name}", "开箱", user_qq, group_id)
|
||||
case = cn2py(case_name)
|
||||
skin_count = {}
|
||||
img_list = []
|
||||
@ -212,32 +205,19 @@ async def open_multiple_case(
|
||||
case_price = 0
|
||||
if case_skin := await BuffSkin.get_or_none(case_name=case_name, color="CASE"):
|
||||
case_price = case_skin.sell_min_price
|
||||
cnt = 0
|
||||
img_w, img_h = 0, 0
|
||||
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
|
||||
rand = str(rand)[:11]
|
||||
add_count(user, skin, case_price)
|
||||
color_name = COLOR2CN[skin.color]
|
||||
if skin.is_stattrak:
|
||||
color_name += "(暗金)"
|
||||
if not skin_count.get(color_name):
|
||||
skin_count[color_name] = 0
|
||||
skin_count[color_name] += 1
|
||||
name = skin.name + "-" + skin.skin_name + "-" + skin.abrasion
|
||||
img_path = IMAGE_PATH / "csgo_cases" / case / f"{cn2py(name)}.jpg"
|
||||
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)
|
||||
add_count(user, skin, case_price)
|
||||
img_list.append(img)
|
||||
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,
|
||||
group_id,
|
||||
@ -261,16 +241,26 @@ async def open_multiple_case(
|
||||
if log_list:
|
||||
await OpenCasesLog.bulk_create(log_list, 10)
|
||||
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:
|
||||
markImg.paste(img)
|
||||
markImg.paste(img, alpha=True)
|
||||
over_count = max_count - user.today_open_total
|
||||
result = ""
|
||||
for color_name in skin_count:
|
||||
result += f"[{color_name}:{skin_count[color_name]}] "
|
||||
return (
|
||||
f"开启{case_name}武器箱\n剩余开箱次数:{over_count}\n"
|
||||
+ image(markImg.pic2bs4())
|
||||
+ image(markImg)
|
||||
+ "\n"
|
||||
+ result[:-1]
|
||||
+ f"\n箱子单价:{case_price}\n总获取金额:{total_price:.2f}\n总花费:{(17 + case_price) * num:.2f}"
|
||||
|
||||
@ -45,9 +45,19 @@ class CaseManager:
|
||||
|
||||
@classmethod
|
||||
async def reload(cls):
|
||||
cls.CURRENT_CASES = (
|
||||
await BuffSkin.filter(case_name__not="未知武器箱").annotate().distinct().values_list("case_name", flat=True) # type: ignore
|
||||
cls.CURRENT_CASES = []
|
||||
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:
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
from pydantic.error_wrappers import ValidationError
|
||||
|
||||
from services.log import logger
|
||||
from utils.manager import group_manager
|
||||
from utils.utils import get_bot
|
||||
@ -7,8 +8,8 @@ from ..auth import Depends, User, token_to_user
|
||||
from ..config import *
|
||||
|
||||
|
||||
@app.get("/webui/group")
|
||||
async def _(user: User = Depends(token_to_user)) -> Result:
|
||||
@router.get("/group", dependencies=[token_to_user()])
|
||||
async def _() -> Result:
|
||||
"""
|
||||
获取群信息
|
||||
"""
|
||||
@ -47,8 +48,8 @@ async def _(user: User = Depends(token_to_user)) -> Result:
|
||||
return Result(code=200, data=group_list_result)
|
||||
|
||||
|
||||
@app.post("/webui/group")
|
||||
async def _(group: GroupResult, user: User = Depends(token_to_user)) -> Result:
|
||||
@router.post("/group", dependencies=[token_to_user()])
|
||||
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)
|
||||
else:
|
||||
group_manager.shutdown_group_bot_status(group_id)
|
||||
return Result(code=200, data="修改成功!")
|
||||
return Result(data="修改成功!")
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
from pydantic import ValidationError
|
||||
|
||||
from configs.config import Config
|
||||
from services.log import logger
|
||||
from utils.manager import (plugins2block_manager, plugins2cd_manager,
|
||||
plugins2count_manager, plugins2settings_manager,
|
||||
plugins_manager)
|
||||
from utils.manager import (
|
||||
plugins2block_manager,
|
||||
plugins2cd_manager,
|
||||
plugins2count_manager,
|
||||
plugins2settings_manager,
|
||||
plugins_manager,
|
||||
)
|
||||
from utils.utils import get_matchers
|
||||
|
||||
from ..auth import Depends, User, token_to_user
|
||||
@ -12,8 +17,8 @@ from ..config import *
|
||||
plugin_name_list = None
|
||||
|
||||
|
||||
@app.get("/webui/plugins")
|
||||
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
|
||||
@router.get("/plugins", dependencies=[token_to_user()])
|
||||
def _(type_: Optional[str]) -> Result:
|
||||
"""
|
||||
获取插件列表
|
||||
: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)
|
||||
|
||||
|
||||
@app.post("/webui/plugins")
|
||||
@router.post("/plugins", dependencies=[token_to_user()])
|
||||
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):
|
||||
c.value = float(c.value)
|
||||
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))
|
||||
):
|
||||
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():
|
||||
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()
|
||||
plugins_manager.save()
|
||||
return Result(code=200, data="修改成功!")
|
||||
|
||||
@ -6,8 +6,8 @@ from ..auth import Depends, User, token_to_user
|
||||
from ..config import *
|
||||
|
||||
|
||||
@app.get("/webui/request")
|
||||
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
|
||||
@router.get("/webui/request", dependencies=[token_to_user()])
|
||||
def _(type_: Optional[str]) -> Result:
|
||||
req_data = requests_manager.get_data()
|
||||
req_list = []
|
||||
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)
|
||||
|
||||
|
||||
@app.delete("/webui/request")
|
||||
def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
|
||||
@router.delete("/webui/request", dependencies=[token_to_user()])
|
||||
def _(type_: Optional[str]) -> Result:
|
||||
"""
|
||||
清空请求
|
||||
:param type_: 类型
|
||||
@ -29,8 +29,8 @@ def _(type_: Optional[str], user: User = Depends(token_to_user)) -> Result:
|
||||
return Result(code=200)
|
||||
|
||||
|
||||
@app.post("/webui/request")
|
||||
async def _(parma: RequestParma, user: User = Depends(token_to_user)) -> Result:
|
||||
@router.post("/webui/request", dependencies=[token_to_user()])
|
||||
async def _(parma: RequestParma) -> Result:
|
||||
"""
|
||||
操作请求
|
||||
:param parma: 参数
|
||||
|
||||
@ -4,6 +4,7 @@ from pathlib import Path
|
||||
|
||||
import psutil
|
||||
import ujson as json
|
||||
|
||||
from configs.path_config import (
|
||||
DATA_PATH,
|
||||
FONT_PATH,
|
||||
@ -28,21 +29,21 @@ memory_data = {"data": []}
|
||||
disk_data = {"data": []}
|
||||
|
||||
|
||||
@app.get("/webui/system")
|
||||
@router.get("/system", dependencies=[token_to_user()])
|
||||
async def _() -> Result:
|
||||
return await get_system_data()
|
||||
|
||||
|
||||
@app.get("/webui/system/status")
|
||||
async def _(user: User = Depends(token_to_user)) -> Result:
|
||||
@router.get("/webui/system/status", dependencies=[token_to_user()])
|
||||
async def _() -> Result:
|
||||
return Result(
|
||||
code=200,
|
||||
data=await asyncio.get_event_loop().run_in_executor(None, _get_system_status),
|
||||
)
|
||||
|
||||
|
||||
@app.get("/webui/system/disk")
|
||||
async def _(type_: Optional[str] = None, user: User = Depends(token_to_user)) -> Result:
|
||||
@router.get("/webui/system/disk", dependencies=[token_to_user()])
|
||||
async def _(type_: Optional[str] = None) -> Result:
|
||||
return Result(
|
||||
code=200,
|
||||
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")
|
||||
async def _(user: User = Depends(token_to_user)) -> Result:
|
||||
@router.get("/webui/system/statusList", dependencies=[token_to_user()])
|
||||
async def _() -> Result:
|
||||
global cpu_data, memory_data, disk_data
|
||||
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"]
|
||||
@ -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(
|
||||
type_: Optional[str], user: User = Depends(token_to_user)
|
||||
type_: Optional[str],
|
||||
) -> Union[SystemFolderSize, Dict[str, Union[float, datetime]]]:
|
||||
"""
|
||||
说明:
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from configs.path_config import DATA_PATH
|
||||
from typing import Optional
|
||||
from starlette import status
|
||||
|
||||
import nonebot
|
||||
from fastapi import Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from pydantic import BaseModel
|
||||
from configs.config import Config
|
||||
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()
|
||||
|
||||
@ -19,14 +21,14 @@ SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
|
||||
ALGORITHM = "HS256"
|
||||
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.parent.mkdir(parents=True, exist_ok=True)
|
||||
token_data = {"token": []}
|
||||
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):
|
||||
@ -39,11 +41,6 @@ class Token(BaseModel):
|
||||
token_type: str
|
||||
|
||||
|
||||
# USER_LIST = [
|
||||
# User(username="admin", password="123")
|
||||
# ]
|
||||
|
||||
|
||||
def get_user(uname: str) -> Optional[User]:
|
||||
username = Config.get_config("web-ui", "username")
|
||||
password = Config.get_config("web-ui", "password")
|
||||
@ -59,24 +56,26 @@ form_exception = HTTPException(
|
||||
|
||||
|
||||
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(
|
||||
claims={"sub": user.username, "exp": expire},
|
||||
key=SECRET_KEY,
|
||||
algorithm=ALGORITHM
|
||||
algorithm=ALGORITHM,
|
||||
)
|
||||
|
||||
|
||||
@app.post("/webui/login")
|
||||
@router.post("/login")
|
||||
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:
|
||||
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)
|
||||
if len(token_data["token"]) > 3:
|
||||
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)
|
||||
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)):
|
||||
if token not in token_data["token"]:
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username, expire = payload.get("sub"), payload.get("exp")
|
||||
user = get_user(username)
|
||||
user = get_user(username) # type: ignore
|
||||
if user is None:
|
||||
raise JWTError
|
||||
except JWTError:
|
||||
return Result(code=401)
|
||||
return Result(code=200, data="ok")
|
||||
return Result(code=200, info="登录成功")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(app, host="127.0.0.1", port=8080)
|
||||
|
||||
@ -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
|
||||
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()
|
||||
|
||||
@ -17,11 +18,14 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
router = APIRouter(tags=["api"])
|
||||
|
||||
|
||||
class CdLimit(BaseModel):
|
||||
"""
|
||||
Cd 限制
|
||||
"""
|
||||
|
||||
cd: int
|
||||
status: bool
|
||||
check_type: str
|
||||
@ -33,6 +37,7 @@ class BlockLimit(BaseModel):
|
||||
"""
|
||||
Block限制
|
||||
"""
|
||||
|
||||
status: bool
|
||||
check_type: str
|
||||
limit_type: str
|
||||
@ -43,6 +48,7 @@ class CountLimit(BaseModel):
|
||||
"""
|
||||
Count限制
|
||||
"""
|
||||
|
||||
max_count: int
|
||||
status: bool
|
||||
limit_type: str
|
||||
@ -53,6 +59,7 @@ class PluginManager(BaseModel):
|
||||
"""
|
||||
插件信息
|
||||
"""
|
||||
|
||||
plugin_name: str # 插件名称
|
||||
status: Optional[bool] # 插件状态
|
||||
error: Optional[bool] # 加载状态
|
||||
@ -65,6 +72,7 @@ class PluginSettings(BaseModel):
|
||||
"""
|
||||
插件基本设置
|
||||
"""
|
||||
|
||||
level: Optional[int] # 群权限等级
|
||||
default_status: Optional[bool] # 默认开关
|
||||
limit_superuser: Optional[bool] # 是否限制超级用户
|
||||
@ -77,6 +85,7 @@ class PluginConfig(BaseModel):
|
||||
"""
|
||||
插件配置项
|
||||
"""
|
||||
|
||||
id: int
|
||||
key: str
|
||||
value: Optional[Any]
|
||||
@ -88,6 +97,7 @@ class Plugin(BaseModel):
|
||||
"""
|
||||
插件
|
||||
"""
|
||||
|
||||
model: str # 模块
|
||||
plugin_settings: Optional[PluginSettings]
|
||||
plugin_manager: Optional[PluginManager]
|
||||
@ -101,6 +111,7 @@ class Group(BaseModel):
|
||||
"""
|
||||
群组信息
|
||||
"""
|
||||
|
||||
group_id: int
|
||||
group_name: str
|
||||
member_count: int
|
||||
@ -111,6 +122,7 @@ class Task(BaseModel):
|
||||
"""
|
||||
被动技能
|
||||
"""
|
||||
|
||||
name: str
|
||||
nameZh: str
|
||||
status: bool
|
||||
@ -120,6 +132,7 @@ class GroupResult(BaseModel):
|
||||
"""
|
||||
群组返回数据
|
||||
"""
|
||||
|
||||
group: Group
|
||||
level: int
|
||||
status: bool
|
||||
@ -131,6 +144,7 @@ class RequestResult(BaseModel):
|
||||
"""
|
||||
好友/群组请求管理
|
||||
"""
|
||||
|
||||
oid: str
|
||||
id: int
|
||||
flag: str
|
||||
@ -148,6 +162,7 @@ class RequestParma(BaseModel):
|
||||
"""
|
||||
操作请求接收数据
|
||||
"""
|
||||
|
||||
id: int
|
||||
handle: str
|
||||
type: str
|
||||
@ -157,6 +172,7 @@ class SystemStatus(BaseModel):
|
||||
"""
|
||||
系统状态
|
||||
"""
|
||||
|
||||
cpu: int
|
||||
memory: int
|
||||
disk: int
|
||||
@ -167,6 +183,7 @@ class SystemNetwork(BaseModel):
|
||||
"""
|
||||
系统网络状态
|
||||
"""
|
||||
|
||||
baidu: int
|
||||
google: int
|
||||
|
||||
@ -175,6 +192,7 @@ class SystemFolderSize(BaseModel):
|
||||
"""
|
||||
资源文件占比
|
||||
"""
|
||||
|
||||
font_dir_size: float
|
||||
image_dir_size: float
|
||||
text_dir_size: float
|
||||
@ -189,6 +207,7 @@ class SystemStatusList(BaseModel):
|
||||
"""
|
||||
状态记录
|
||||
"""
|
||||
|
||||
cpu_data: List[Dict[str, Union[float, str]]]
|
||||
memory_data: List[Dict[str, Union[float, str]]]
|
||||
disk_data: List[Dict[str, Union[float, str]]]
|
||||
@ -198,6 +217,7 @@ class SystemResult(BaseModel):
|
||||
"""
|
||||
系统api返回
|
||||
"""
|
||||
|
||||
status: SystemStatus
|
||||
network: SystemNetwork
|
||||
disk: SystemFolderSize
|
||||
@ -208,5 +228,7 @@ class Result(BaseModel):
|
||||
"""
|
||||
总体返回
|
||||
"""
|
||||
code: int
|
||||
data: Any
|
||||
|
||||
code: int = 200
|
||||
info: str = "操作成功"
|
||||
data: Any = None
|
||||
|
||||
BIN
resources/image/_icon/abrasion_white.png
Normal file
BIN
resources/image/_icon/abrasion_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
resources/image/_icon/name_white.png
Normal file
BIN
resources/image/_icon/name_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
resources/image/_icon/tone_white.png
Normal file
BIN
resources/image/_icon/tone_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@ -160,6 +160,7 @@ class BuildImage:
|
||||
h: int,
|
||||
paste_image_width: int = 0,
|
||||
paste_image_height: int = 0,
|
||||
paste_space: int = 0,
|
||||
color: Union[str, Tuple[int, int, int], Tuple[int, int, int, int]] = None,
|
||||
image_mode: ModeType = "RGBA",
|
||||
font_size: int = 10,
|
||||
@ -177,6 +178,7 @@ class BuildImage:
|
||||
:param h: 自定义图片的高度,h=0时为图片原本高度
|
||||
:param paste_image_width: 当图片做为背景图时,设置贴图的宽度,用于贴图自动换行
|
||||
:param paste_image_height: 当图片做为背景图时,设置贴图的高度,用于贴图自动换行
|
||||
:param paste_space: 自动贴图间隔
|
||||
:param color: 生成图片的颜色
|
||||
:param image_mode: 图片的类型
|
||||
:param font_size: 文字大小
|
||||
@ -190,6 +192,7 @@ class BuildImage:
|
||||
self.h = int(h)
|
||||
self.paste_image_width = int(paste_image_width)
|
||||
self.paste_image_height = int(paste_image_height)
|
||||
self.paste_space = int(paste_space)
|
||||
self._current_w = 0
|
||||
self._current_h = 0
|
||||
self.uid = uuid.uuid1()
|
||||
@ -277,7 +280,9 @@ class BuildImage:
|
||||
:param center_type: 居中类型,可能的值 center: 完全居中,by_width: 水平居中,by_height: 垂直居中
|
||||
: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(
|
||||
self,
|
||||
@ -324,7 +329,7 @@ class BuildImage:
|
||||
img = img.markImg
|
||||
if self._current_w >= self.w:
|
||||
self._current_w = 0
|
||||
self._current_h += self.paste_image_height
|
||||
self._current_h += self.paste_image_height + self.paste_space
|
||||
if not pos:
|
||||
pos = (self._current_w, self._current_h)
|
||||
if alpha:
|
||||
@ -335,7 +340,7 @@ class BuildImage:
|
||||
self.markImg.paste(img, pos, img)
|
||||
else:
|
||||
self.markImg.paste(img, pos)
|
||||
self._current_w += self.paste_image_width
|
||||
self._current_w += self.paste_image_width + self.paste_space
|
||||
|
||||
@classmethod
|
||||
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'"
|
||||
)
|
||||
w, h = self.w, self.h
|
||||
longgest_text = ''
|
||||
longgest_text = ""
|
||||
sentence = text.split("\n")
|
||||
for x in sentence:
|
||||
longgest_text = x if len(x) > len(longgest_text) else longgest_text
|
||||
|
||||
Loading…
Reference in New Issue
Block a user