From ac13ab928060adc22d40b558ea22c8450cd9ed74 Mon Sep 17 00:00:00 2001 From: Art_Sakura <1754798088@qq.com> Date: Fri, 6 Jun 2025 10:56:22 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20=E5=B0=86=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E5=90=8C=E4=B8=80=E6=9B=B4=E6=96=B0=20?= =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E5=A4=8D=E5=81=B7=E8=8F=9C=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E6=AD=A3=E5=B8=B8=E8=AE=A1=E7=AE=97=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 197 +++++++++++++- __init__.py | 14 +- command.py | 289 ++++++++++---------- config.py | 95 ++++++- database/database.py | 20 +- database/plant.py | 20 +- database/user.py | 37 +-- database/userItem.py | 45 ++-- database/userPlant.py | 45 ++-- database/userSeed.py | 55 ++-- database/userSign.py | 115 +++++--- database/userSoil.py | 114 ++++---- database/userSteal.py | 44 +-- dbService.py | 6 +- event/event.py | 7 +- farm/farm.py | 613 +++++++++++++++++++++++++----------------- farm/shop.py | 92 ++++--- json.py | 2 +- request.py | 35 ++- tool.py | 138 ++++++++++ 20 files changed, 1284 insertions(+), 699 deletions(-) diff --git a/.gitignore b/.gitignore index 430cafa..c89499a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,198 @@ -__pycache__ +__pycache__/ ./config/sign_in.json + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the enitre vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Cursor +# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to +# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data +# refer to https://docs.cursor.com/context/ignore-files +.cursorignore +.cursorindexingignore diff --git a/__init__.py b/__init__.py index 51a39a0..c7bb7ee 100644 --- a/__init__.py +++ b/__init__.py @@ -39,7 +39,7 @@ __plugin_meta__ = PluginMetadata( """.strip(), extra=PluginExtraData( author="Art_Sakura", - version="1.4.2", + version="1.4.3", commands=[Command(command="我的农场")], menu_type="群内小游戏", configs=[ @@ -66,8 +66,8 @@ __plugin_meta__ = PluginMetadata( value="http://diuse.work", help="签到、交易行、活动等服务器地址", default_value="http://diuse.work", - ) - ] + ), + ], ).to_dict(), ) driver = get_driver() @@ -84,6 +84,7 @@ async def start(): await g_pDBService.init() + # 析构函数 @driver.on_shutdown async def shutdown(): @@ -92,12 +93,7 @@ async def shutdown(): await g_pDBService.cleanup() -@scheduler.scheduled_job( - trigger="cron", - hour=0, - minute=30, - id="signInFile" -) +@scheduler.scheduled_job(trigger="cron", hour=0, minute=30, id="signInFile") async def signInFile(): try: await g_pJsonManager.initSignInFile() diff --git a/command.py b/command.py index d5055a3..96c33bc 100644 --- a/command.py +++ b/command.py @@ -1,10 +1,20 @@ import inspect -from nonebot.adapters import Event, MessageTemplate +from nonebot.adapters import Event from nonebot.rule import to_me -from nonebot_plugin_alconna import (Alconna, AlconnaMatch, AlconnaQuery, Args, - Arparma, At, Match, MultiVar, Option, - Query, Subcommand, on_alconna, store_true) +from nonebot_plugin_alconna import ( + Alconna, + AlconnaQuery, + Args, + At, + Match, + MultiVar, + Option, + Query, + Subcommand, + on_alconna, + store_true, +) from nonebot_plugin_uninfo import Uninfo from nonebot_plugin_waiter import waiter @@ -12,24 +22,13 @@ from zhenxun.configs.config import BotConfig from zhenxun.services.log import logger from zhenxun.utils.message import MessageUtils -from .config import g_bSignStatus +from .config import g_bSignStatus, g_sTranslation from .dbService import g_pDBService from .farm.farm import g_pFarmManager from .farm.shop import g_pShopManager from .json import g_pJsonManager from .tool import g_pToolManager - -async def isRegisteredByUid(uid: str) -> bool: - result = await g_pDBService.user.isUserExist(uid) - - if not result: - await MessageUtils.build_message("尚未开通农场,快at我发送 开通农场 开通吧").send() - return False - - return True - - diuse_register = on_alconna( Alconna("开通农场"), priority=5, @@ -37,89 +36,40 @@ diuse_register = on_alconna( block=True, ) + @diuse_register.handle() async def handle_register(session: Uninfo): uid = str(session.user.id) user = await g_pDBService.user.getUserInfoByUid(uid) if user: - await MessageUtils.build_message("🎉 您已经开通农场啦~").send(reply_to=True) + await MessageUtils.build_message(g_sTranslation["register"]["repeat"]).send( + reply_to=True + ) return try: raw_name = str(session.user.name) - safe_name = sanitize_username(raw_name) + safe_name = g_pToolManager.sanitize_username(raw_name) # 初始化用户信息 success = await g_pDBService.user.initUserInfoByUid( - uid=uid, - name=safe_name, - exp=0, - point=500 + uid=uid, name=safe_name, exp=0, point=500 ) msg = ( - "✅ 农场开通成功!\n💼 初始资金:500农场币" + g_sTranslation["register"]["success"] if success - else "⚠️ 开通失败,请稍后再试" + else g_sTranslation["register"]["error"] ) logger.info(f"用户注册 {'成功' if success else '失败'}:{uid}") except Exception as e: - msg = "⚠️ 系统繁忙,请稍后再试" - logger.error(f"注册异常 | UID:{uid} | 错误:{str(e)}") + msg = g_sTranslation["register"]["error"] + logger.error(f"注册异常 | UID:{uid} | 错误:{e}") await MessageUtils.build_message(msg).send(reply_to=True) -def sanitize_username(username: str, max_length: int = 15) -> str: - """ - 安全处理用户名 - 功能: - 1. 移除首尾空白 - 2. 过滤危险字符 - 3. 转义单引号 - 4. 处理空值 - 5. 限制长度 - """ - # 处理空值 - if not username: - return "神秘农夫" - - # 基础清洗 - cleaned = username.strip() - - # 允许的字符白名单(可自定义扩展) - safe_chars = { - '_', '-', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', - '+', '=', '.', ',', '~', '·', ' ', - 'a','b','c','d','e','f','g','h','i','j','k','l','m', - 'n','o','p','q','r','s','t','u','v','w','x','y','z', - 'A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - '0','1','2','3','4','5','6','7','8','9', - } - # 添加常用中文字符(Unicode范围) - safe_chars.update(chr(c) for c in range(0x4E00, 0x9FFF+1)) - - # 过滤危险字符 - filtered = [ - c if c in safe_chars or 0x4E00 <= ord(c) <= 0x9FFF - else '' - for c in cleaned - ] - - # 合并处理结果 - safe_str = ''.join(filtered) - - # 转义单引号(双重保障) - escaped = safe_str.replace("'", "''") - - # 处理空结果 - if not escaped: - return "神秘农夫" - - # 长度限制 - return escaped[:max_length] diuse_farm = on_alconna( Alconna( @@ -137,25 +87,28 @@ diuse_farm = on_alconna( Subcommand("sell-plant", Args["name?", str]["num?", int], help_text="出售作物"), Subcommand("stealing", Args["target?", At], help_text="偷菜"), Subcommand("buy-point", Args["num?", int], help_text="购买农场币"), - #Subcommand("sell-point", Args["num?", int], help_text="转换金币") + # Subcommand("sell-point", Args["num?", int], help_text="转换金币") Subcommand("change-name", Args["name?", str], help_text="更改农场名"), Subcommand("sign-in", help_text="农场签到"), Subcommand("admin-up", Args["num?", int], help_text="农场下阶段"), ), priority=5, block=True, + use_cmd_start=True, ) + @diuse_farm.assign("$main") async def _(session: Uninfo): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return image = await g_pFarmManager.drawFarmByUid(uid) await MessageUtils.build_message(image).send(reply_to=True) + diuse_farm.shortcut( "农场详述", command="我的农场", @@ -163,16 +116,20 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("detail") async def _(session: Uninfo): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return info = await g_pFarmManager.drawDetailFarmByUid(uid) - await MessageUtils.alc_forward_msg([info], session.self_id, BotConfig.self_nickname).send() + await MessageUtils.alc_forward_msg( + [info], session.self_id, BotConfig.self_nickname + ).send() + diuse_farm.shortcut( "我的农场币", @@ -181,16 +138,20 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("my-point") async def _(session: Uninfo): uid = str(session.user.id) point = await g_pDBService.user.getUserPointByUid(uid) if point < 0: - await MessageUtils.build_message("尚未开通农场,快at我发送 开通农场 开通吧").send() + await MessageUtils.build_message(g_sTranslation["basic"]["notFarm"]).send() return False - await MessageUtils.build_message(f"你的当前农场币为: {point}").send(reply_to=True) + await MessageUtils.build_message( + g_sTranslation["basic"]["point"].format(point=point) + ).send(reply_to=True) + diuse_farm.shortcut( "种子商店(.*?)", @@ -199,11 +160,12 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("seed-shop") async def _(session: Uninfo, res: Match[tuple[str, ...]]): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return if res.result is inspect._empty: @@ -221,7 +183,12 @@ async def _(session: Uninfo, res: Match[tuple[str, ...]]): else: filterKey = first - if len(raw) >= 2 and raw[1] is not None and isinstance(raw[1], str) and raw[1].isdigit(): + if ( + len(raw) >= 2 + and raw[1] is not None + and isinstance(raw[1], str) + and raw[1].isdigit() + ): page = int(raw[1]) if filterKey is None: @@ -231,6 +198,7 @@ async def _(session: Uninfo, res: Match[tuple[str, ...]]): await MessageUtils.build_message(image).send() + diuse_farm.shortcut( "购买种子(?P.*?)", command="我的农场", @@ -238,21 +206,25 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("buy-seed") -async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", 1)): +async def _( + session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", 1) +): if not name.available: - await MessageUtils.build_message( - "请在指令后跟需要购买的种子名称" - ).finish(reply_to=True) + await MessageUtils.build_message(g_sTranslation["buySeed"]["notSeed"]).finish( + reply_to=True + ) uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return result = await g_pShopManager.buySeed(uid, name.result, num.result) await MessageUtils.build_message(result).send(reply_to=True) + diuse_farm.shortcut( "我的种子", command="我的农场", @@ -260,16 +232,18 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("my-seed") async def _(session: Uninfo): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return result = await g_pFarmManager.getUserSeedByUid(uid) await MessageUtils.build_message(result).send(reply_to=True) + diuse_farm.shortcut( "播种(?P.*?)", command="我的农场", @@ -277,16 +251,19 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("sowing") -async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1)): +async def _( + session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1) +): if not name.available: - await MessageUtils.build_message( - "请在指令后跟需要播种的种子名称" - ).finish(reply_to=True) + await MessageUtils.build_message(g_sTranslation["sowing"]["notSeed"]).finish( + reply_to=True + ) uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return result = await g_pFarmManager.sowing(uid, name.result, num.result) @@ -300,16 +277,18 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("harvest") async def _(session: Uninfo): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return result = await g_pFarmManager.harvest(uid) await MessageUtils.build_message(result).send(reply_to=True) + diuse_farm.shortcut( "铲除", command="我的农场", @@ -317,11 +296,12 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("eradicate") async def _(session: Uninfo): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return result = await g_pFarmManager.eradicate(uid) @@ -335,11 +315,12 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("my-plant") async def _(session: Uninfo): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return result = await g_pFarmManager.getUserPlantByUid(uid) @@ -352,15 +333,16 @@ reclamation = on_alconna( block=True, ) + @reclamation.handle() async def _(session: Uninfo): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return condition = await g_pFarmManager.reclamationCondition(uid) - condition += "\n 回复是将执行开垦" + condition += f"\n{g_sTranslation['reclamation']['confirm']}" await MessageUtils.build_message(condition).send(reply_to=True) @waiter(waits=["message"], keep_session=True) @@ -369,7 +351,9 @@ async def _(session: Uninfo): resp = await check.wait(timeout=60) if resp is None: - await MessageUtils.build_message("等待超时").send(reply_to=True) + await MessageUtils.build_message(g_sTranslation["reclamation"]["timeOut"]).send( + reply_to=True + ) return if not resp == "是": return @@ -377,6 +361,7 @@ async def _(session: Uninfo): res = await g_pFarmManager.reclamation(uid) await MessageUtils.build_message(res).send(reply_to=True) + diuse_farm.shortcut( "出售作物(?P.*?)", command="我的农场", @@ -384,16 +369,20 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("sell-plant") -async def _(session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1)): +async def _( + session: Uninfo, name: Match[str], num: Query[int] = AlconnaQuery("num", -1) +): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return result = await g_pShopManager.sellPlantByUid(uid, name.result, num.result) await MessageUtils.build_message(result).send(reply_to=True) + diuse_farm.shortcut( "偷菜", command="我的农场", @@ -401,26 +390,32 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("stealing") async def _(session: Uninfo, target: Match[At]): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return if not target.available: - await MessageUtils.build_message("请在指令后跟需要at的人").finish(reply_to=True) + await MessageUtils.build_message(g_sTranslation["stealing"]["noTarget"]).finish( + reply_to=True + ) tar = target.result result = await g_pDBService.user.isUserExist(tar.target) if not result: - await MessageUtils.build_message("目标尚未开通农场,快邀请ta开通吧").send() + await MessageUtils.build_message( + g_sTranslation["stealing"]["targetNotFarm"] + ).send() return None result = await g_pFarmManager.stealing(uid, tar.target) await MessageUtils.build_message(result).send(reply_to=True) + diuse_farm.shortcut( "购买农场币(.*?)", command="我的农场", @@ -428,16 +423,17 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("buy-point") async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)): if num.result <= 0: - await MessageUtils.build_message( - "请在指令后跟需要购买农场币的数量" - ).finish(reply_to=True) + await MessageUtils.build_message("请在指令后跟需要购买农场币的数量").finish( + reply_to=True + ) uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return result = await g_pFarmManager.buyPointByUid(uid, num.result) @@ -451,30 +447,38 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("change-name") async def _(session: Uninfo, name: Match[str]): if not name.available: - await MessageUtils.build_message( - "请在指令后跟需要更改的农场名" - ).finish(reply_to=True) + await MessageUtils.build_message(g_sTranslation["changeName"]["noName"]).finish( + reply_to=True + ) uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return - safeName = sanitize_username(name.result) + safeName = g_pToolManager.sanitize_username(name.result) if safeName == "神秘农夫": - await MessageUtils.build_message("农场名不支持特殊符号!").send(reply_to=True) + await MessageUtils.build_message(g_sTranslation["changeName"]["error"]).send( + reply_to=True + ) return result = await g_pDBService.user.updateUserNameByUid(uid, safeName) - if result == True: - await MessageUtils.build_message("更新农场名成功!").send(reply_to=True) + if result: + await MessageUtils.build_message(g_sTranslation["changeName"]["success"]).send( + reply_to=True + ) else: - await MessageUtils.build_message("更新农场名失败!").send(reply_to=True) + await MessageUtils.build_message(g_sTranslation["changeName"]["error1"]).send( + reply_to=True + ) + diuse_farm.shortcut( "农场签到", @@ -483,16 +487,17 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("sign-in") async def _(session: Uninfo): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return - #判断签到是否正常加载 + # 判断签到是否正常加载 if not g_bSignStatus: - await MessageUtils.build_message("签到功能异常!").send() + await MessageUtils.build_message(g_sTranslation["signIn"]["error"]).send() return @@ -500,39 +505,50 @@ async def _(session: Uninfo): message = "" status = await g_pDBService.userSign.sign(uid, toDay.strftime("%Y-%m-%d")) - #如果完成签到 + # 如果完成签到 if status == 1 or status == 2: - #获取签到总天数 - signDay = await g_pDBService.userSign.getUserSignCountByDate(uid, toDay.strftime("%Y-%m")) - exp, point = await g_pDBService.userSign.getUserSignRewardByDate(uid, toDay.strftime("%Y-%m-%d")) + # 获取签到总天数 + signDay = await g_pDBService.userSign.getUserSignCountByDate( + uid, toDay.strftime("%Y-%m") + ) + exp, point = await g_pDBService.userSign.getUserSignRewardByDate( + uid, toDay.strftime("%Y-%m-%d") + ) - message += f"签到成功!累计签到天数:{signDay}\n获得经验{exp},获得金币{point}" + message += g_sTranslation["signIn"]["success"].format( + day=signDay, exp=exp, num=point + ) - reward = g_pJsonManager.m_pSign['continuou'].get(f"{signDay}", None) + reward = g_pJsonManager.m_pSign["continuou"].get(f"{signDay}", None) if reward: - extraPoint = reward.get('point', 0) - extraExp = reward.get('exp', 0) + extraPoint = reward.get("point", 0) + extraExp = reward.get("exp", 0) - plant = reward.get('plant', {}) + plant = reward.get("plant", {}) - message += f"\n\n成功领取累计签到奖励:\n额外获得经验{extraExp},额外获得金币{extraPoint}" + message += g_sTranslation["signIn"]["grandTotal"].format( + exp=extraExp, num=extraPoint + ) - vipPoint = reward.get('vipPoint', 0) + vipPoint = reward.get("vipPoint", 0) if vipPoint > 0: - message += f",额外获得点券{vipPoint}" + message += g_sTranslation["signIn"]["grandTotal1"].format(num=vipPoint) if plant: for key, value in plant.items(): - message += f"\n获得{key}种子 * {value}" + message += g_sTranslation["signIn"]["grandTotal2"].format( + name=key, num=value + ) else: - message = "签到失败!未知错误" + message = g_sTranslation["signIn"]["error1"] await MessageUtils.build_message(message).send() # await MessageUtils.alc_forward_msg([info], session.self_id, BotConfig.self_nickname).send(reply_to=True) + diuse_farm.shortcut( "农场下阶段(.*?)", command="我的农场", @@ -540,11 +556,12 @@ diuse_farm.shortcut( prefix=True, ) + @diuse_farm.assign("admin-up") async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)): uid = str(session.user.id) - if not await isRegisteredByUid(uid): + if not await g_pToolManager.isRegisteredByUid(uid): return await g_pDBService.userSoil.nextPhase(uid, num.result) diff --git a/config.py b/config.py index a60649d..aed0345 100644 --- a/config.py +++ b/config.py @@ -2,15 +2,108 @@ from pathlib import Path from zhenxun.configs.path_config import DATA_PATH +# 签到状态 +g_bSignStatus = True + +# 是否处于Debug模式 g_bIsDebug = False +# 数据库文件目录 g_sDBPath = DATA_PATH / "farm_db" + +# 数据库文件路径 g_sDBFilePath = DATA_PATH / "farm_db/farm.db" +# 农场资源文件目录 g_sResourcePath = Path(__file__).resolve().parent / "resource" + +# 农场作物数据库 g_sPlantPath = g_sResourcePath / "db/plant.db" +# 农场配置文件目录 g_sConfigPath = Path(__file__).resolve().parent / "config" + +# 农场签到文件路径 g_sSignInPath = g_sConfigPath / "sign_in.json" -g_bSignStatus = True +# 农场同一文本 +g_sTranslation = { + "basic": { + "notFarm": "尚未开通农场,快at我发送 开通农场 开通吧 🌱🚜", + "point": "你的当前农场币为: {point} 🌾💰", + }, + "register": { + "success": "✅ 农场开通成功!\n💼 初始资金:{point}农场币 🥳🎉", + "repeat": "🎉 您已经开通农场啦~ 😄", + "error": "⚠️ 开通失败,请稍后再试 💔", + }, + "buySeed": { + "notSeed": "🌱 请在指令后跟需要购买的种子名称", + "notNum": "❗️ 请输入购买数量!", + "noLevel": "🔒 你的等级不够哦,努努力吧 💪", + "noPoint": "💰 你的农场币不够哦~ 快速速氪金吧!💸", + "success": "✅ 成功购买{name},花费{total}农场币,剩余{point}农场币 🌾", + "errorSql": "❌ 购买失败,执行数据库错误!🛑", + "error": "❌ 购买出错!请检查需购买的种子名称!🔍", + }, + "sowing": { + "notSeed": "🌱 请在指令后跟需要播种的种子名称", + "noSeed": "❌ 没有在你的仓库发现{name}种子,快去买点吧!🛒", + "noNum": "⚠️ 播种失败!仓库中的{name}种子数量不足,当前剩余{num}个种子 🍂", + "success": "✅ 播种{name}成功!仓库剩余{num}个种子 🌱", + "success2": "✅ 播种数量超出开垦土地数量,已将可播种土地成功播种{name}!仓库剩余{num}个种子 🌾", + "error": "❌ 播种失败,请稍后重试!⏳", + }, + "harvest": { + "append": "🌾 收获作物:{name},数量为:{num},经验为:{exp} ✨", + "exp": "✨ 累计获得经验:{exp} 📈", + "no": "🤷‍♂️ 没有可收获的作物哦~ 不要试图拔苗助长 🚫", + "error": "❌ 收获失败,请稍后重试!⏳", + }, + "eradicate": { + "success": "🗑️ 成功铲除荒废作物,累计获得经验:{exp} ✨", + "error": "❌ 没有可以铲除的作物 🚜", + }, + "reclamation": { + "confirm": "⚠️ 回复“是”将执行开垦 ⛏️", + "timeOut": "⏰ 等待开垦回复超时,请重试", + "perfect": "🌟 你已经开垦了全部土地 🎉", + "next": "🔜 下次开垦所需条件:等级:{level},农场币:{num} 💰", + "next2": "🔜 下次开垦所需条件:等级:{level},农场币:{num},物品:{item} 📦", + "nextLevel": "📈 当前用户等级{level},升级所需等级为{next} ⏳", + "noNum": "💰 当前用户农场币不足,升级所需农场币为{num} 💸", + "success": "✅ 开垦土地成功!🌱", + "error": "❌ 获取开垦土地条件失败!", + "error1": "❌ 执行开垦失败!", + "error2": "❌ 未知错误{e}💥", + }, + "sellPlant": { + "no": "🤷‍♀️ 你仓库没有可以出售的作物 🌾", + "success": "💰 成功出售所有作物,获得农场币:{point},当前农场币:{num} 🎉", + "success1": "💰 成功出售{name},获得农场币:{point},当前农场币:{num} 🥳", + "error": "❌ 出售作物{name}出错:仓库中不存在该作物 🚫", + "error1": "❌ 出售作物{name}出错:数量不足 ⚠️", + }, + "stealing": { + "noTarget": "🎯 请在指令后跟需要at的人 👤", + "targetNotFarm": "🚜 目标尚未开通农场,快邀请ta开通吧 😉", + "max": "❌ 你今天可偷次数到达上限啦,手下留情吧 🙏", + "info": "🤫 成功偷到作物:{name},数量为:{num} 🍒", + "noPlant": "🌱 目标没有作物可以被偷 🌾", + "repeat": "🚫 你已经偷过目标啦,请手下留情 🙏", + }, + "changeName": { + "noName": "✏️ 请在指令后跟需要更改的农场名", + "success": "✅ 更新农场名成功!🎉", + "error": "❌ 农场名不支持特殊符号!🚫", + "error1": "❌ 更新农场名失败!💔", + }, + "signIn": { + "success": "📝 签到成功!累计签到天数:{day}\n🎁 获得经验{exp},获得金币{num} 💰", + "grandTotal": "\n🎉 成功领取累计签到奖励:\n✨ 额外获得经验{exp},额外获得金币{num} 🥳", + "grandTotal1": ",额外获得点券{num} 🎫", + "grandTotal2": "\n🌱 获得{name}种子 * {num} 🌟", + "error": "❗️ 签到功能异常!", + "error1": "❌ 签到失败!未知错误 💔", + }, +} diff --git a/database/database.py b/database/database.py index 851bd2e..818eef8 100644 --- a/database/database.py +++ b/database/database.py @@ -1,4 +1,3 @@ -import math import os import re from contextlib import asynccontextmanager @@ -13,9 +12,9 @@ from ..config import g_sDBFilePath, g_sDBPath class CSqlManager: def __init__(self): - dbPath = Path(g_sDBPath) - if dbPath and not dbPath.exists(): - os.makedirs(dbPath, exist_ok=True) + dbPath = Path(g_sDBPath) + if dbPath and not dbPath.exists(): + os.makedirs(dbPath, exist_ok=True) @classmethod async def cleanup(cls): @@ -46,7 +45,7 @@ class CSqlManager: @classmethod async def getTableInfo(cls, tableName: str) -> list: - if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', tableName): + if not re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", tableName): raise ValueError(f"Illegal table name: {tableName}") try: cursor = await cls.m_pDB.execute(f'PRAGMA table_info("{tableName}")') @@ -70,7 +69,7 @@ class CSqlManager: """ info = await cls.getTableInfo(tableName) - existing = {col['name']: col['type'].upper() for col in info} + existing = {col["name"]: col["type"].upper() for col in info} desired = {k: v.upper() for k, v in columns.items() if k != "PRIMARY KEY"} primaryKey = columns.get("PRIMARY KEY", "") @@ -83,7 +82,9 @@ class CSqlManager: toAdd = [k for k in desired if k not in existing] toRemove = [k for k in existing if k not in desired] - typeMismatch = [k for k in desired if k in existing and existing[k] != desired[k]] + typeMismatch = [ + k for k in desired if k in existing and existing[k] != desired[k] + ] if toAdd and not toRemove and not typeMismatch: for col in toAdd: @@ -106,7 +107,9 @@ class CSqlManager: f'INSERT INTO "{tmpTable}" ({colsStr}) SELECT {colsStr} FROM "{tableName}";' ) await cls.m_pDB.execute(f'DROP TABLE "{tableName}";') - await cls.m_pDB.execute(f'ALTER TABLE "{tmpTable}" RENAME TO "{tableName}";') + await cls.m_pDB.execute( + f'ALTER TABLE "{tmpTable}" RENAME TO "{tableName}";' + ) return True @classmethod @@ -131,4 +134,5 @@ class CSqlManager: logger.warning(f"数据库语句执行出错: {command}", e=e) return False + g_pSqlManager = CSqlManager() diff --git a/database/plant.py b/database/plant.py index be2a0c4..c28a23a 100644 --- a/database/plant.py +++ b/database/plant.py @@ -1,8 +1,5 @@ -import ast import os -import re from contextlib import asynccontextmanager -from unittest import result import aiosqlite @@ -29,7 +26,9 @@ class CPlantManager: _ = os.path.exists(g_sPlantPath) if g_bIsDebug: - cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath.parent / "plant-test.db")) + cls.m_pDB = await aiosqlite.connect( + str(g_sPlantPath.parent / "plant-test.db") + ) else: cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath)) @@ -73,7 +72,6 @@ class CPlantManager: logger.warning(f"数据库语句执行出错: {command}", e=e) return False - @classmethod async def getPlantByName(cls, name: str) -> dict | None: """根据作物名称查询记录 @@ -113,7 +111,7 @@ class CPlantManager: if not row: return [] - phase = row[0].split(',') + phase = row[0].split(",") seen = set() result = [] @@ -149,9 +147,9 @@ class CPlantManager: if not row: return -1 - phase = row[0].split(',') + phase = row[0].split(",") - #去重 + # 去重 seen = set() result = [] for x in phase: @@ -184,7 +182,7 @@ class CPlantManager: if not row: return -1 - phase = row[0].split(',') + phase = row[0].split(",") again = phase[-1] - phase[3] / 60 / 60 return again @@ -241,7 +239,9 @@ class CPlantManager: async def listPlants(cls) -> list[dict]: """查询所有作物记录""" try: - async with cls.m_pDB.execute("SELECT * FROM plant ORDER BY level") as cursor: + async with cls.m_pDB.execute( + "SELECT * FROM plant ORDER BY level" + ) as cursor: rows = await cursor.fetchall() return [dict(r) for r in rows] except Exception as e: diff --git a/database/user.py b/database/user.py index 43ace85..a80276f 100644 --- a/database/user.py +++ b/database/user.py @@ -1,6 +1,4 @@ import math -from typing import List, Union -from unittest import result from zhenxun.services.log import logger @@ -13,19 +11,21 @@ class CUserDB(CSqlManager): async def initDB(cls): """初始化用户表结构,确保user表存在且字段完整""" userInfo = { - "uid": "TEXT PRIMARY KEY", #用户Uid - "name": "TEXT NOT NULL", #农场名称 - "exp": "INTEGER DEFAULT 0", #经验值 - "point": "INTEGER DEFAULT 0", #金币 - "vipPoint": "INTEGER DEFAULT 0", #点券 - "soil": "INTEGER DEFAULT 3", #解锁土地数量 - "stealTime": "TEXT DEFAULT ''", #偷菜时间字符串 - "stealCount": "INTEGER DEFAULT 0" #剩余偷菜次数 + "uid": "TEXT PRIMARY KEY", # 用户Uid + "name": "TEXT NOT NULL", # 农场名称 + "exp": "INTEGER DEFAULT 0", # 经验值 + "point": "INTEGER DEFAULT 0", # 金币 + "vipPoint": "INTEGER DEFAULT 0", # 点券 + "soil": "INTEGER DEFAULT 3", # 解锁土地数量 + "stealTime": "TEXT DEFAULT ''", # 偷菜时间字符串 + "stealCount": "INTEGER DEFAULT 0", # 剩余偷菜次数 } await cls.ensureTableSchema("user", userInfo) @classmethod - async def initUserInfoByUid(cls, uid: str, name: str = "", exp: int = 0, point: int = 500) -> Union[bool, str]: + async def initUserInfoByUid( + cls, uid: str, name: str = "", exp: int = 0, point: int = 500 + ) -> bool | str: """初始化用户信息,包含初始偷菜时间字符串与次数 Args: @@ -37,7 +37,7 @@ class CUserDB(CSqlManager): Returns: Union[bool, str]: False 表示失败,字符串表示成功信息 """ - nowStr = g_pToolManager.dateTime().date().today().strftime('%Y-%m-%d') + nowStr = g_pToolManager.dateTime().date().today().strftime("%Y-%m-%d") sql = ( f"INSERT INTO user (uid, name, exp, point, soil, stealTime, stealCount) " f"VALUES ({uid}, '{name}', {exp}, {point}, 3, '{nowStr}', 5)" @@ -51,7 +51,7 @@ class CUserDB(CSqlManager): return False @classmethod - async def getAllUsers(cls) -> List[str]: + async def getAllUsers(cls) -> list[str]: """获取所有用户UID列表 Returns: @@ -395,8 +395,7 @@ class CUserDB(CSqlManager): return "" try: async with cls.m_pDB.execute( - "SELECT stealTime FROM user WHERE uid = ?", (uid, - ) + "SELECT stealTime FROM user WHERE uid = ?", (uid,) ) as cursor: row = await cursor.fetchone() return row[0] if row and row[0] else "" @@ -451,11 +450,14 @@ class CUserDB(CSqlManager): return -1 @classmethod - async def updateStealCountByUid(cls, uid: str, stealCount: int) -> bool: + async def updateStealCountByUid( + cls, uid: str, stealTime: str, stealCount: int + ) -> bool: """根据用户Uid更新剩余偷菜次数 Args: uid (str): 用户Uid + stealTime (str): 偷菜日期 stealCount (int): 新剩余偷菜次数 Returns: @@ -467,7 +469,8 @@ class CUserDB(CSqlManager): try: async with cls._transaction(): await cls.m_pDB.execute( - "UPDATE user SET stealCount = ? WHERE uid = ?", (stealCount, uid) + "UPDATE user SET stealTime = ?, stealCount = ? WHERE uid = ?", + (stealTime, stealCount, uid), ) return True except Exception as e: diff --git a/database/userItem.py b/database/userItem.py index 8ae4d3f..b75cd58 100644 --- a/database/userItem.py +++ b/database/userItem.py @@ -1,5 +1,3 @@ -from typing import Optional - from zhenxun.services.log import logger from .database import CSqlManager @@ -9,16 +7,16 @@ class CUserItemDB(CSqlManager): @classmethod async def initDB(cls): userItem = { - "uid": "TEXT NOT NULL", #用户Uid - "item": "TEXT NOT NULL", #物品名称 - "count": "INTEGER NOT NULL DEFAULT 0", #数量 - "PRIMARY KEY": "(uid, item)" + "uid": "TEXT NOT NULL", # 用户Uid + "item": "TEXT NOT NULL", # 物品名称 + "count": "INTEGER NOT NULL DEFAULT 0", # 数量 + "PRIMARY KEY": "(uid, item)", } await cls.ensureTableSchema("userItem", userItem) @classmethod - async def getUserItemByName(cls, uid: str, item: str) -> Optional[int]: + async def getUserItemByName(cls, uid: str, item: str) -> int | None: """根据道具名称查询某一项数量 Args: @@ -32,13 +30,12 @@ class CUserItemDB(CSqlManager): return None try: async with cls.m_pDB.execute( - "SELECT count FROM userItem WHERE uid = ? AND item = ?", - (uid, item) + "SELECT count FROM userItem WHERE uid = ? AND item = ?", (uid, item) ) as cursor: row = await cursor.fetchone() return row[0] if row else None except Exception as e: - logger.warning(f"getUserItemByName查询失败!", e=e) + logger.warning("getUserItemByName查询失败!", e=e) return None @classmethod @@ -55,13 +52,12 @@ class CUserItemDB(CSqlManager): return {} try: cursor = await cls.m_pDB.execute( - "SELECT item, count FROM userItem WHERE uid = ?", - (uid,) + "SELECT item, count FROM userItem WHERE uid = ?", (uid,) ) rows = await cursor.fetchall() return {row["item"]: row["count"] for row in rows} except Exception as e: - logger.warning(f"getUserItemByUid查询失败!", e=e) + logger.warning("getUserItemByUid查询失败!", e=e) return {} @classmethod @@ -80,12 +76,11 @@ class CUserItemDB(CSqlManager): try: async with cls._transaction(): await cls.m_pDB.execute( - "DELETE FROM userItem WHERE uid = ? AND item = ?", - (uid, item) + "DELETE FROM userItem WHERE uid = ? AND item = ?", (uid, item) ) return True except Exception as e: - logger.warning(f"deleteUserItemByName失败!", e=e) + logger.warning("deleteUserItemByName失败!", e=e) return False @classmethod @@ -106,17 +101,16 @@ class CUserItemDB(CSqlManager): async with cls._transaction(): if count <= 0: await cls.m_pDB.execute( - "DELETE FROM userItem WHERE uid = ? AND item = ?", - (uid, item) + "DELETE FROM userItem WHERE uid = ? AND item = ?", (uid, item) ) else: await cls.m_pDB.execute( "UPDATE userItem SET count = ? WHERE uid = ? AND item = ?", - (count, uid, item) + (count, uid, item), ) return True except Exception as e: - logger.warning(f"updateUserItemByName失败!", e=e) + logger.warning("updateUserItemByName失败!", e=e) return False @classmethod @@ -136,8 +130,7 @@ class CUserItemDB(CSqlManager): try: async with cls._transaction(): async with cls.m_pDB.execute( - "SELECT count FROM userItem WHERE uid = ? AND item = ?", - (uid, item) + "SELECT count FROM userItem WHERE uid = ? AND item = ?", (uid, item) ) as cursor: row = await cursor.fetchone() @@ -146,20 +139,20 @@ class CUserItemDB(CSqlManager): if newCount <= 0: await cls.m_pDB.execute( "DELETE FROM userItem WHERE uid = ? AND item = ?", - (uid, item) + (uid, item), ) else: await cls.m_pDB.execute( "UPDATE userItem SET count = ? WHERE uid = ? AND item = ?", - (newCount, uid, item) + (newCount, uid, item), ) else: if count > 0: await cls.m_pDB.execute( "INSERT INTO userItem (uid, item, count) VALUES (?, ?, ?)", - (uid, item, count) + (uid, item, count), ) return True except Exception as e: - logger.warning(f"addUserItemByUid失败!", e=e) + logger.warning("addUserItemByUid失败!", e=e) return False diff --git a/database/userPlant.py b/database/userPlant.py index 9b6a9d8..4d403d8 100644 --- a/database/userPlant.py +++ b/database/userPlant.py @@ -1,5 +1,3 @@ -from typing import Dict, Optional - from zhenxun.services.log import logger from .database import CSqlManager @@ -9,10 +7,10 @@ class CUserPlantDB(CSqlManager): @classmethod async def initDB(cls): userPlant = { - "uid": "TEXT NOT NULL", #用户Uid - "plant": "TEXT NOT NULL", #作物名称 - "count": "INTEGER NOT NULL DEFAULT 0", #数量 - "PRIMARY KEY": "(uid, plant)" + "uid": "TEXT NOT NULL", # 用户Uid + "plant": "TEXT NOT NULL", # 作物名称 + "count": "INTEGER NOT NULL DEFAULT 0", # 数量 + "PRIMARY KEY": "(uid, plant)", } await cls.ensureTableSchema("userPlant", userPlant) @@ -31,33 +29,33 @@ class CUserPlantDB(CSqlManager): """ try: async with cls._transaction(): - #检查是否已存在该作物 + # 检查是否已存在该作物 async with cls.m_pDB.execute( "SELECT count FROM userPlant WHERE uid = ? AND plant = ?", - (uid, plant) + (uid, plant), ) as cursor: row = await cursor.fetchone() if row: - #如果作物已存在,则更新数量 + # 如果作物已存在,则更新数量 new_count = row[0] + count await cls.m_pDB.execute( "UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?", - (new_count, uid, plant) + (new_count, uid, plant), ) else: - #如果作物不存在,则插入新记录 + # 如果作物不存在,则插入新记录 await cls.m_pDB.execute( "INSERT INTO userPlant (uid, plant, count) VALUES (?, ?, ?)", - (uid, plant, count) + (uid, plant, count), ) return True except Exception as e: - logger.warning(f"addUserPlantByUid 失败!", e=e) + logger.warning("addUserPlantByUid 失败!", e=e) return False @classmethod - async def getUserPlantByUid(cls, uid: str) -> Dict[str, int]: + async def getUserPlantByUid(cls, uid: str) -> dict[str, int]: """根据用户uid获取全部作物信息 Args: @@ -67,14 +65,13 @@ class CUserPlantDB(CSqlManager): Dict[str, int]: 作物名称和数量 """ cursor = await cls.m_pDB.execute( - "SELECT plant, count FROM userPlant WHERE uid=?", - (uid,) + "SELECT plant, count FROM userPlant WHERE uid=?", (uid,) ) rows = await cursor.fetchall() return {row["plant"]: row["count"] for row in rows} @classmethod - async def getUserPlantByName(cls, uid: str, plant: str) -> Optional[int]: + async def getUserPlantByName(cls, uid: str, plant: str) -> int | None: """根据作物名称获取用户的作物数量 Args: @@ -86,13 +83,12 @@ class CUserPlantDB(CSqlManager): """ try: async with cls.m_pDB.execute( - "SELECT count FROM userPlant WHERE uid = ? AND plant = ?", - (uid, plant) + "SELECT count FROM userPlant WHERE uid = ? AND plant = ?", (uid, plant) ) as cursor: row = await cursor.fetchone() return row[0] if row else None except Exception as e: - logger.warning(f"getUserPlantByName 查询失败!", e=e) + logger.warning("getUserPlantByName 查询失败!", e=e) return None @classmethod @@ -114,11 +110,11 @@ class CUserPlantDB(CSqlManager): async with cls._transaction(): await cls.m_pDB.execute( "UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?", - (count, uid, plant) + (count, uid, plant), ) return True except Exception as e: - logger.warning(f"updateUserPlantByName失败!", e=e) + logger.warning("updateUserPlantByName失败!", e=e) return False @classmethod @@ -135,10 +131,9 @@ class CUserPlantDB(CSqlManager): try: async with cls._transaction(): await cls.m_pDB.execute( - "DELETE FROM userPlant WHERE uid = ? AND plant = ?", - (uid, plant) + "DELETE FROM userPlant WHERE uid = ? AND plant = ?", (uid, plant) ) return True except Exception as e: - logger.warning(f"deleteUserPlantByName 失败!", e=e) + logger.warning("deleteUserPlantByName 失败!", e=e) return False diff --git a/database/userSeed.py b/database/userSeed.py index 279c425..2ef7682 100644 --- a/database/userSeed.py +++ b/database/userSeed.py @@ -1,5 +1,3 @@ -from typing import Optional - from zhenxun.services.log import logger from .database import CSqlManager @@ -9,10 +7,10 @@ class CUserSeedDB(CSqlManager): @classmethod async def initDB(cls): userSeed = { - "uid": "TEXT NOT NULL", #用户Uid - "seed": "TEXT NOT NULL", #种子名称 - "count": "INTEGER NOT NULL DEFAULT 0", #数量 - "PRIMARY KEY": "(uid, seed)" + "uid": "TEXT NOT NULL", # 用户Uid + "seed": "TEXT NOT NULL", # 种子名称 + "count": "INTEGER NOT NULL DEFAULT 0", # 数量 + "PRIMARY KEY": "(uid, seed)", } await cls.ensureTableSchema("userSeed", userSeed) @@ -32,8 +30,7 @@ class CUserSeedDB(CSqlManager): try: async with cls._transaction(): async with cls.m_pDB.execute( - "SELECT count FROM userSeed WHERE uid = ? AND seed = ?", - (uid, seed) + "SELECT count FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed) ) as cursor: row = await cursor.fetchone() @@ -41,23 +38,22 @@ class CUserSeedDB(CSqlManager): newCount = row[0] + count await cls.m_pDB.execute( "UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?", - (newCount, uid, seed) + (newCount, uid, seed), ) else: newCount = count await cls.m_pDB.execute( "INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)", - (uid, seed, count) + (uid, seed, count), ) if newCount <= 0: await cls.m_pDB.execute( - "DELETE FROM userSeed WHERE uid = ? AND seed = ?", - (uid, seed) + "DELETE FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed) ) return True except Exception as e: - logger.warning(f"addUserSeedByUid 失败!", e=e) + logger.warning("addUserSeedByUid 失败!", e=e) return False @classmethod @@ -72,7 +68,7 @@ class CUserSeedDB(CSqlManager): else: await cls.m_pDB.execute( "INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)", - (uid, seed, newCount) + (uid, seed, newCount), ) if newCount <= 0: @@ -80,12 +76,11 @@ class CUserSeedDB(CSqlManager): return True except Exception as e: - logger.warning(f"_addUserSeedByUid 失败!", e=e) + logger.warning("_addUserSeedByUid 失败!", e=e) return False - @classmethod - async def getUserSeedByName(cls, uid: str, seed: str) -> Optional[int]: + async def getUserSeedByName(cls, uid: str, seed: str) -> int | None: """根据种子名称获取种子数量 Args: @@ -98,13 +93,12 @@ class CUserSeedDB(CSqlManager): try: async with cls.m_pDB.execute( - "SELECT count FROM userSeed WHERE uid = ? AND seed = ?", - (uid, seed) + "SELECT count FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed) ) as cursor: row = await cursor.fetchone() return row[0] if row else None except Exception as e: - logger.warning(f"getUserSeedByName 查询失败!", e=e) + logger.warning("getUserSeedByName 查询失败!", e=e) return None @classmethod @@ -119,8 +113,7 @@ class CUserSeedDB(CSqlManager): """ cursor = await cls.m_pDB.execute( - "SELECT seed, count FROM userSeed WHERE uid=?", - (uid,) + "SELECT seed, count FROM userSeed WHERE uid=?", (uid,) ) rows = await cursor.fetchall() return {row["seed"]: row["count"] for row in rows} @@ -144,11 +137,11 @@ class CUserSeedDB(CSqlManager): async with cls._transaction(): await cls.m_pDB.execute( "UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?", - (count, uid, seed) + (count, uid, seed), ) return True except Exception as e: - logger.warning(f"updateUserSeedByName失败!", e=e) + logger.warning("updateUserSeedByName失败!", e=e) return False @classmethod @@ -170,11 +163,11 @@ class CUserSeedDB(CSqlManager): async with cls._transaction(): await cls.m_pDB.execute( "UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?", - (count, uid, seed) + (count, uid, seed), ) return True except Exception as e: - logger.warning(f"updateUserSeedByName失败!", e=e) + logger.warning("updateUserSeedByName失败!", e=e) return False @classmethod @@ -191,12 +184,11 @@ class CUserSeedDB(CSqlManager): try: async with cls._transaction(): await cls.m_pDB.execute( - "DELETE FROM userSeed WHERE uid = ? AND seed = ?", - (uid, seed) + "DELETE FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed) ) return True except Exception as e: - logger.warning(f"deleteUserSeedByName 删除失败!", e=e) + logger.warning("deleteUserSeedByName 删除失败!", e=e) return False @classmethod @@ -212,10 +204,9 @@ class CUserSeedDB(CSqlManager): """ try: await cls.m_pDB.execute( - "DELETE FROM userSeed WHERE uid = ? AND seed = ?", - (uid, seed) + "DELETE FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed) ) return True except Exception as e: - logger.warning(f"deleteUserSeedByName 删除失败!", e=e) + logger.warning("deleteUserSeedByName 删除失败!", e=e) return False diff --git a/database/userSign.py b/database/userSign.py index c50fe32..ba42ec6 100644 --- a/database/userSign.py +++ b/database/userSign.py @@ -1,7 +1,6 @@ import calendar import random -from datetime import date, datetime, timedelta -from typing import Optional +from datetime import timedelta from zhenxun.services.log import logger from zhenxun.utils._build_image import BuildImage @@ -16,27 +15,27 @@ from .database import CSqlManager class CUserSignDB(CSqlManager): @classmethod async def initDB(cls): - #userSignLog 表结构,每条为一次签到事件 + # userSignLog 表结构,每条为一次签到事件 userSignLog = { - "uid": "TEXT NOT NULL", #用户ID - "signDate": "DATE NOT NULL", #签到日期 - "isSupplement": "TINYINT NOT NULL DEFAULT 0", #是否补签 - "exp": "INT NOT NULL DEFAULT 0", #当天签到经验 - "point": "INT NOT NULL DEFAULT 0", #当天签到金币 - "createdAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))",#创建时间 - "PRIMARY KEY": "(uid, signDate)" + "uid": "TEXT NOT NULL", # 用户ID + "signDate": "DATE NOT NULL", # 签到日期 + "isSupplement": "TINYINT NOT NULL DEFAULT 0", # 是否补签 + "exp": "INT NOT NULL DEFAULT 0", # 当天签到经验 + "point": "INT NOT NULL DEFAULT 0", # 当天签到金币 + "createdAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))", # 创建时间 + "PRIMARY KEY": "(uid, signDate)", } - #userSignSummary 表结构,每用户一行用于缓存签到状态 + # userSignSummary 表结构,每用户一行用于缓存签到状态 userSignSummary = { - "uid": "TEXT PRIMARY KEY NOT NULL", #用户ID - "totalSignDays": "INT NOT NULL DEFAULT 0", #累计签到天数 - "currentMonth": "CHAR(7) NOT NULL DEFAULT ''", #当前月份(如2025-05) - "monthSignDays": "INT NOT NULL DEFAULT 0", #本月签到次数 - "lastSignDate": "DATE DEFAULT NULL", #上次签到日期 - "continuousDays": "INT NOT NULL DEFAULT 0", #连续签到天数 - "supplementCount": "INT NOT NULL DEFAULT 0", #补签次数 - "updatedAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))"#更新时间 + "uid": "TEXT PRIMARY KEY NOT NULL", # 用户ID + "totalSignDays": "INT NOT NULL DEFAULT 0", # 累计签到天数 + "currentMonth": "CHAR(7) NOT NULL DEFAULT ''", # 当前月份(如2025-05) + "monthSignDays": "INT NOT NULL DEFAULT 0", # 本月签到次数 + "lastSignDate": "DATE DEFAULT NULL", # 上次签到日期 + "continuousDays": "INT NOT NULL DEFAULT 0", # 连续签到天数 + "supplementCount": "INT NOT NULL DEFAULT 0", # 补签次数 + "updatedAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))", # 更新时间 } await cls.ensureTableSchema("userSignLog", userSignLog) @@ -57,15 +56,15 @@ class CUserSignDB(CSqlManager): async with cls._transaction(): async with cls.m_pDB.execute( "SELECT exp, point FROM userSignLog WHERE uid=? AND signDate=?", - (uid, date) + (uid, date), ) as cursor: row = await cursor.fetchone() if row is None: return 0, 0 - exp = row['exp'] - point = row['point'] + exp = row["exp"] + point = row["point"] return exp, point except Exception as e: @@ -114,7 +113,7 @@ class CUserSignDB(CSqlManager): return False @classmethod - async def sign(cls, uid: str, signDate: str = '') -> int: + async def sign(cls, uid: str, signDate: str = "") -> int: """签到 Args: @@ -151,20 +150,35 @@ class CUserSignDB(CSqlManager): async with cls._transaction(): await cls.m_pDB.execute( "INSERT INTO userSignLog (uid, signDate, isSupplement, exp, point) VALUES (?, ?, ?, ?, ?)", - (uid, signDate, isSupplement, exp, point) + (uid, signDate, isSupplement, exp, point), ) - cursor = await cls.m_pDB.execute("SELECT * FROM userSignSummary WHERE uid=?", (uid,)) + cursor = await cls.m_pDB.execute( + "SELECT * FROM userSignSummary WHERE uid=?", (uid,) + ) row = await cursor.fetchone() currentMonth = signDate[:7] if row: - monthSignDays = row['monthSignDays'] + 1 if row['currentMonth'] == currentMonth else 1 - totalSignDays = row['totalSignDays'] - lastDate = row['lastSignDate'] - prevDate = (g_pToolManager.dateTime().strptime(signDate, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d") - continuousDays = row['continuousDays'] + 1 if lastDate == prevDate else 1 - supplementCount = row['supplementCount'] + 1 if isSupplement else row['supplementCount'] + monthSignDays = ( + row["monthSignDays"] + 1 + if row["currentMonth"] == currentMonth + else 1 + ) + totalSignDays = row["totalSignDays"] + lastDate = row["lastSignDate"] + prevDate = ( + g_pToolManager.dateTime().strptime(signDate, "%Y-%m-%d") + - timedelta(days=1) + ).strftime("%Y-%m-%d") + continuousDays = ( + row["continuousDays"] + 1 if lastDate == prevDate else 1 + ) + supplementCount = ( + row["supplementCount"] + 1 + if isSupplement + else row["supplementCount"] + ) await cls.m_pDB.execute( """ UPDATE userSignSummary @@ -176,7 +190,14 @@ class CUserSignDB(CSqlManager): supplementCount=? WHERE uid=? """, - (currentMonth, monthSignDays, signDate, continuousDays, supplementCount, uid) + ( + currentMonth, + monthSignDays, + signDate, + continuousDays, + supplementCount, + uid, + ), ) else: totalSignDays = 1 @@ -186,18 +207,26 @@ class CUserSignDB(CSqlManager): (uid, totalSignDays, currentMonth, monthSignDays, lastSignDate, continuousDays, supplementCount) VALUES (?, ?, ?, ?, ?, ?, ?) """, - (uid, 1, currentMonth, 1, signDate, 1, 1 if isSupplement else 0) + ( + uid, + 1, + currentMonth, + 1, + signDate, + 1, + 1 if isSupplement else 0, + ), ) - #计算累签奖励 - reward = g_pJsonManager.m_pSign['continuou'].get(f"{totalSignDays}", None) + # 计算累签奖励 + reward = g_pJsonManager.m_pSign["continuou"].get(f"{totalSignDays}", None) if reward: - point += reward.get('point', 0) - exp += reward.get('exp', 0) - vipPoint = reward.get('vipPoint', 0) + point += reward.get("point", 0) + exp += reward.get("exp", 0) + vipPoint = reward.get("vipPoint", 0) - plant = reward.get('plant', {}) + plant = reward.get("plant", {}) if plant: for key, value in plant.items(): @@ -206,7 +235,7 @@ class CUserSignDB(CSqlManager): if g_bIsDebug: exp += 9999 - #向数据库更新 + # 向数据库更新 currentExp = await g_pDBService.user.getUserExpByUid(uid) await g_pDBService.user.updateUserExpByUid(uid, currentExp + exp) @@ -215,7 +244,9 @@ class CUserSignDB(CSqlManager): if vipPoint > 0: currentVipPoint = await g_pDBService.user.getUserVipPointByUid(uid) - await g_pDBService.user.updateUserVipPointByUid(uid, currentVipPoint + vipPoint) + await g_pDBService.user.updateUserVipPointByUid( + uid, currentVipPoint + vipPoint + ) return 1 except Exception as e: @@ -224,7 +255,7 @@ class CUserSignDB(CSqlManager): @classmethod async def drawSignCalendarImage(cls, uid: str, year: int, month: int): - #绘制签到图,自动提取数据库中该用户该月的签到天数 + # 绘制签到图,自动提取数据库中该用户该月的签到天数 cellSize = 80 padding = 40 titleHeight = 80 diff --git a/database/userSoil.py b/database/userSoil.py index b8c5410..a2ca943 100644 --- a/database/userSoil.py +++ b/database/userSoil.py @@ -1,10 +1,7 @@ -from typing import Optional - from zhenxun.services.log import logger from ..config import g_bIsDebug from ..dbService import g_pDBService -from ..json import g_pJsonManager from ..tool import g_pToolManager from .database import CSqlManager @@ -14,17 +11,17 @@ class CUserSoilDB(CSqlManager): async def initDB(cls): userSoil = { "uid": "TEXT NOT NULL", - "soilIndex": "INTEGER NOT NULL", #地块索引从1开始 - "plantName": "TEXT DEFAULT ''", #作物名称 - "plantTime": "INTEGER DEFAULT 0", #播种时间 - "matureTime": "INTEGER DEFAULT 0", #成熟时间 - "soilLevel": "INTEGER DEFAULT 0", #土地等级 0=普通地,1=红土地,2=黑土地,3=金土地 - "wiltStatus": "INTEGER DEFAULT 0", #枯萎状态 0=未枯萎,1=枯萎 - "fertilizerStatus": "INTEGER DEFAULT 0",#施肥状态 0=未施肥,1=施肥 2=增肥 - "bugStatus": "INTEGER DEFAULT 0", #虫害状态 0=无虫害,1=有虫害 - "weedStatus": "INTEGER DEFAULT 0", #杂草状态 0=无杂草,1=有杂草 - "waterStatus": "INTEGER DEFAULT 0", #缺水状态 0=不缺水,1=缺水 - "harvestCount": "INTEGER DEFAULT 0", #收获次数 + "soilIndex": "INTEGER NOT NULL", # 地块索引从1开始 + "plantName": "TEXT DEFAULT ''", # 作物名称 + "plantTime": "INTEGER DEFAULT 0", # 播种时间 + "matureTime": "INTEGER DEFAULT 0", # 成熟时间 + "soilLevel": "INTEGER DEFAULT 0", # 土地等级 0=普通地,1=红土地,2=黑土地,3=金土地 + "wiltStatus": "INTEGER DEFAULT 0", # 枯萎状态 0=未枯萎,1=枯萎 + "fertilizerStatus": "INTEGER DEFAULT 0", # 施肥状态 0=未施肥,1=施肥 2=增肥 + "bugStatus": "INTEGER DEFAULT 0", # 虫害状态 0=无虫害,1=有虫害 + "weedStatus": "INTEGER DEFAULT 0", # 杂草状态 0=无杂草,1=有杂草 + "waterStatus": "INTEGER DEFAULT 0", # 缺水状态 0=不缺水,1=缺水 + "harvestCount": "INTEGER DEFAULT 0", # 收获次数 "PRIMARY KEY": "(uid, soilIndex)", } @@ -45,30 +42,30 @@ class CUserSoilDB(CSqlManager): if not soilInfo: return - plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName']) + plantInfo = await g_pDBService.plant.getPlantByName(soilInfo["plantName"]) if not plantInfo: return currentTime = g_pToolManager.dateTime().now().timestamp() - phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName']) + phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo["plantName"]) - if currentTime >= soilInfo['matureTime']: + if currentTime >= soilInfo["matureTime"]: return - elapsedTime = currentTime - soilInfo['plantTime'] + elapsedTime = currentTime - soilInfo["plantTime"] currentStage = currentStage = sum(1 for thr in phaseList if elapsedTime >= thr) - t = int(soilInfo['plantTime']) - phaseList[currentStage] - s = int(soilInfo['matureTime']) - phaseList[currentStage] + t = int(soilInfo["plantTime"]) - phaseList[currentStage] + s = int(soilInfo["matureTime"]) - phaseList[currentStage] - await cls.updateUserSoilFields(uid, soilIndex, - { - "plantTime": t, - "matureTime": s - }) + await cls.updateUserSoilFields( + uid, soilIndex, {"plantTime": t, "matureTime": s} + ) - logger.debug(f"当前阶段{currentStage}, 阶段时间{phaseList[currentStage]}, 播种时间{t}, 收获时间{s}") + logger.debug( + f"当前阶段{currentStage}, 阶段时间{phaseList[currentStage]}, 播种时间{t}, 收获时间{s}" + ) @classmethod async def getUserFarmByUid(cls, uid: str) -> dict: @@ -95,7 +92,7 @@ class CUserSoilDB(CSqlManager): Returns: bool: 如果旧表不存在则返回 False,否则迁移并删除后返回 True """ - #检查旧表是否存在 + # 检查旧表是否存在 cursor = await cls.m_pDB.execute( "SELECT name FROM sqlite_master WHERE type='table' AND name='soil'" ) @@ -184,31 +181,31 @@ class CUserSoilDB(CSqlManager): None """ await cls.m_pDB.execute( - """ + """ INSERT INTO userSoil (uid, soilIndex, plantName, plantTime, matureTime, soilLevel, wiltStatus, fertilizerStatus, bugStatus, weedStatus, waterStatus, harvestCount) VALUES (?,?,?,?,?,?,?,?,?,?,?,?) """, - ( - soilInfo["uid"], - soilInfo["soilIndex"], - soilInfo.get("plantName", ""), - soilInfo.get("plantTime", 0), - soilInfo.get("matureTime", 0), - soilInfo.get("soilLevel", 0), - soilInfo.get("wiltStatus", 0), - soilInfo.get("fertilizerStatus", 0), - soilInfo.get("bugStatus", 0), - soilInfo.get("weedStatus", 0), - soilInfo.get("waterStatus", 0), - soilInfo.get("harvestCount", 0), - ), + ( + soilInfo["uid"], + soilInfo["soilIndex"], + soilInfo.get("plantName", ""), + soilInfo.get("plantTime", 0), + soilInfo.get("matureTime", 0), + soilInfo.get("soilLevel", 0), + soilInfo.get("wiltStatus", 0), + soilInfo.get("fertilizerStatus", 0), + soilInfo.get("bugStatus", 0), + soilInfo.get("weedStatus", 0), + soilInfo.get("waterStatus", 0), + soilInfo.get("harvestCount", 0), + ), ) @classmethod - async def getUserSoil(cls, uid: str, soilIndex: int) -> Optional[dict]: + async def getUserSoil(cls, uid: str, soilIndex: int) -> dict | None: """获取指定用户某块土地的详细信息 Args: @@ -230,7 +227,7 @@ class CUserSoilDB(CSqlManager): return dict(zip(columns, row)) @classmethod - async def _getUserSoil(cls, uid: str, soilIndex: int) -> Optional[dict]: + async def _getUserSoil(cls, uid: str, soilIndex: int) -> dict | None: """获取指定用户某块土地的详细信息 Args: @@ -288,7 +285,9 @@ class CUserSoilDB(CSqlManager): ) @classmethod - async def updateUserSoilFields(cls, uid: str, soilIndex: int, updates: dict) -> bool: + async def updateUserSoilFields( + cls, uid: str, soilIndex: int, updates: dict + ) -> bool: """批量更新指定用户土地的多个字段 Args: @@ -299,11 +298,18 @@ class CUserSoilDB(CSqlManager): Returns: bool: 如果无可更新字段则返回 False,否则更新成功返回 True """ - #允许更新的列白名单 + # 允许更新的列白名单 allowedFields = { - "plantName", "plantTime", "matureTime", "soilLevel", - "wiltStatus", "fertilizerStatus", "bugStatus", - "weedStatus", "waterStatus", "harvestCount" + "plantName", + "plantTime", + "matureTime", + "soilLevel", + "wiltStatus", + "fertilizerStatus", + "bugStatus", + "weedStatus", + "waterStatus", + "harvestCount", } setClauses = [] values = [] @@ -316,7 +322,7 @@ class CUserSoilDB(CSqlManager): return False values.extend([uid, soilIndex]) - sql = f'UPDATE userSoil SET {", ".join(setClauses)} WHERE uid = ? AND soilIndex = ?' + sql = f"UPDATE userSoil SET {', '.join(setClauses)} WHERE uid = ? AND soilIndex = ?" try: async with cls._transaction(): @@ -386,12 +392,12 @@ class CUserSoilDB(CSqlManager): Returns: bool: 播种成功返回 True,否则返回 False """ - #校验土地区是否已种植 + # 校验土地区是否已种植 soilRecord = await cls.getUserSoil(uid, soilIndex) if soilRecord and soilRecord.get("plantName"): return False - #获取植物配置 + # 获取植物配置 plantCfg = await g_pDBService.plant.getPlantByName(plantName) if not plantCfg: logger.error(f"未知植物: {plantName}") @@ -417,12 +423,12 @@ class CUserSoilDB(CSqlManager): "bugStatus": prev.get("bugStatus", 0), "weedStatus": prev.get("weedStatus", 0), "waterStatus": prev.get("waterStatus", 0), - "harvestCount": 0 + "harvestCount": 0, } ) return True except Exception as e: - logger.error(f"播种失败!", e=e) + logger.error("播种失败!", e=e) return False @classmethod diff --git a/database/userSteal.py b/database/userSteal.py index 6dd04d4..57d21e3 100644 --- a/database/userSteal.py +++ b/database/userSteal.py @@ -7,17 +7,19 @@ class CUserStealDB(CSqlManager): @classmethod async def initDB(cls): userSteal = { - "uid": "TEXT NOT NULL", #被偷用户Uid - "soilIndex": "INTEGER NOT NULL", #被偷的地块索引 从1开始 - "stealerUid": "TEXT NOT NULL", #偷菜用户Uid - "stealCount": "INTEGER NOT NULL", #被偷数量 - "stealTime": "INTEGER NOT NULL", #被偷时间 - "PRIMARY KEY": "(uid, soilIndex, stealerUid)" + "uid": "TEXT NOT NULL", # 被偷用户Uid + "soilIndex": "INTEGER NOT NULL", # 被偷的地块索引 从1开始 + "stealerUid": "TEXT NOT NULL", # 偷菜用户Uid + "stealCount": "INTEGER NOT NULL", # 被偷数量 + "stealTime": "INTEGER NOT NULL", # 被偷时间 + "PRIMARY KEY": "(uid, soilIndex, stealerUid)", } await cls.ensureTableSchema("userSteal", userSteal) @classmethod - async def addStealRecord(cls, uid: str, soilIndex: int, stealerUid: str, stealCount: int, stealTime: int) -> bool: + async def addStealRecord( + cls, uid: str, soilIndex: int, stealerUid: str, stealCount: int, stealTime: int + ) -> bool: """添加偷菜记录 Args: @@ -34,7 +36,7 @@ class CUserStealDB(CSqlManager): async with cls._transaction(): await cls.m_pDB.execute( 'INSERT INTO "userSteal"(uid, soilIndex, stealerUid, stealCount, stealTime) VALUES(?, ?, ?, ?, ?);', - (uid, soilIndex, stealerUid, stealCount, stealTime) + (uid, soilIndex, stealerUid, stealCount, stealTime), ) return True except Exception as e: @@ -55,7 +57,7 @@ class CUserStealDB(CSqlManager): async with cls._transaction(): cursor = await cls.m_pDB.execute( 'SELECT soilIndex, stealerUid, stealCount, stealTime FROM "userSteal" WHERE uid=?;', - (uid,) + (uid,), ) rows = await cursor.fetchall() return [ @@ -64,7 +66,7 @@ class CUserStealDB(CSqlManager): "soilIndex": row[0], "stealerUid": row[1], "stealCount": row[2], - "stealTime": row[3] + "stealTime": row[3], } for row in rows ] @@ -87,7 +89,7 @@ class CUserStealDB(CSqlManager): async with cls._transaction(): cursor = await cls.m_pDB.execute( 'SELECT stealerUid, stealCount, stealTime FROM "userSteal" WHERE uid=? AND soilIndex=?;', - (uid, soilIndex) + (uid, soilIndex), ) rows = await cursor.fetchall() return [ @@ -96,7 +98,7 @@ class CUserStealDB(CSqlManager): "soilIndex": soilIndex, "stealerUid": row[0], "stealCount": row[1], - "stealTime": row[2] + "stealTime": row[2], } for row in rows ] @@ -119,10 +121,10 @@ class CUserStealDB(CSqlManager): async with cls._transaction(): cursor = await cls.m_pDB.execute( 'SELECT SUM(stealCount) FROM "userSteal" WHERE uid=? AND soilIndex=?;', - (uid, soilIndex) + (uid, soilIndex), ) row = await cursor.fetchone() - return row[0] or 0 # type: ignore + return row[0] or 0 # type: ignore except Exception as e: logger.warning("计算总偷菜数量失败", e=e) return 0 @@ -142,10 +144,10 @@ class CUserStealDB(CSqlManager): async with cls._transaction(): cursor = await cls.m_pDB.execute( 'SELECT COUNT(DISTINCT stealerUid) FROM "userSteal" WHERE uid=? AND soilIndex=?;', - (uid, soilIndex) + (uid, soilIndex), ) row = await cursor.fetchone() - return row[0] or 0 # type: ignore + return row[0] or 0 # type: ignore except Exception as e: logger.warning("计算偷菜者数量失败", e=e) return 0 @@ -166,7 +168,7 @@ class CUserStealDB(CSqlManager): async with cls._transaction(): cursor = await cls.m_pDB.execute( 'SELECT 1 FROM "userSteal" WHERE uid=? AND soilIndex=? AND stealerUid=? LIMIT 1;', - (uid, soilIndex, stealerUid) + (uid, soilIndex, stealerUid), ) row = await cursor.fetchone() return bool(row) @@ -175,7 +177,9 @@ class CUserStealDB(CSqlManager): return False @classmethod - async def updateStealRecord(cls, uid: str, soilIndex: int, stealerUid: str, stealCount: int, stealTime: int) -> bool: + async def updateStealRecord( + cls, uid: str, soilIndex: int, stealerUid: str, stealCount: int, stealTime: int + ) -> bool: """更新偷菜记录的数量和时间 Args: @@ -192,7 +196,7 @@ class CUserStealDB(CSqlManager): async with cls._transaction(): await cls.m_pDB.execute( 'UPDATE "userSteal" SET stealCount=?, stealTime=? WHERE uid=? AND soilIndex=? AND stealerUid=?;', - (stealCount, stealTime, uid, soilIndex, stealerUid) + (stealCount, stealTime, uid, soilIndex, stealerUid), ) return True except Exception as e: @@ -214,7 +218,7 @@ class CUserStealDB(CSqlManager): async with cls._transaction(): await cls.m_pDB.execute( 'DELETE FROM "userSteal" WHERE uid=? AND soilIndex=?;', - (uid, soilIndex) + (uid, soilIndex), ) return True except Exception as e: diff --git a/dbService.py b/dbService.py index c7b5927..d748d96 100644 --- a/dbService.py +++ b/dbService.py @@ -1,6 +1,3 @@ -from typing import Optional - - class CDBService: @classmethod async def init(cls): @@ -37,11 +34,12 @@ class CDBService: cls.userSign = CUserSignDB() await cls.userSign.initDB() - #迁移旧数据库 + # 迁移旧数据库 await cls.userSoil.migrateOldFarmData() @classmethod async def cleanup(cls): await cls.plant.cleanup() + g_pDBService = CDBService() diff --git a/event/event.py b/event/event.py index 4d468db..44c4ab9 100644 --- a/event/event.py +++ b/event/event.py @@ -6,8 +6,8 @@ from zhenxun.services.log import logger class Signal: def __init__(self): - self._slots = [] #绑定的槽函数列表 - self._onceSlots = [] #只触发一次的槽函数列表 + self._slots = [] # 绑定的槽函数列表 + self._onceSlots = [] # 只触发一次的槽函数列表 def connect(self, slot, priority=0): if callable(slot) and not any(s[0] == slot for s in self._slots): @@ -41,10 +41,8 @@ class Signal: logger.warning(f"事件槽 {slot.__name__} 触发异常: {e}") - class FarmEventManager: def __init__(self): - self.m_beforePlant = Signal() """播种前信号 @@ -100,4 +98,5 @@ class FarmEventManager: self.m_beforeSteal = Signal() self.m_afterSteal = Signal() + g_pEventManager = FarmEventManager() diff --git a/farm/farm.py b/farm/farm.py index a1bf1dd..acfb90c 100644 --- a/farm/farm.py +++ b/farm/farm.py @@ -1,7 +1,5 @@ -import asyncio import math import random -from typing import Dict, List, Tuple from zhenxun.configs.config import Config from zhenxun.models.user_console import UserConsole @@ -11,7 +9,7 @@ from zhenxun.utils.enum import GoldHandle from zhenxun.utils.image_utils import ImageTemplate from zhenxun.utils.platform import PlatformUtils -from ..config import g_bIsDebug, g_sResourcePath +from ..config import g_bIsDebug, g_sResourcePath, g_sTranslation from ..dbService import g_pDBService from ..event.event import g_pEventManager from ..json import g_pJsonManager @@ -29,16 +27,20 @@ class CFarmManager: pro = float(Config.get_config("zhenxun_plugin_farm", "兑换倍数")) tax = float(Config.get_config("zhenxun_plugin_farm", "手续费")) - #计算手续费 + # 计算手续费 fee = math.floor(num * tax) - #实际扣费金额 + # 实际扣费金额 deduction = num + fee if user.gold < deduction: return f"你的金币不足或不足承担手续费。当前手续费为{fee}" - await UserConsole.reduce_gold(uid, num, GoldHandle.PLUGIN , 'zhenxun_plugin_farm') # type: ignore - await UserConsole.reduce_gold(uid, fee, GoldHandle.PLUGIN , 'zhenxun_plugin_farm') # type: ignore + await UserConsole.reduce_gold( + uid, num, GoldHandle.PLUGIN, "zhenxun_plugin_farm" + ) # type: ignore + await UserConsole.reduce_gold( + uid, fee, GoldHandle.PLUGIN, "zhenxun_plugin_farm" + ) # type: ignore point = num * pro @@ -59,48 +61,59 @@ class CFarmManager: Returns: bytes: 返回绘制结果 """ - img = BuildImage(background = g_sResourcePath / "background/background.jpg") + img = BuildImage(background=g_sResourcePath / "background/background.jpg") - soilSize = g_pJsonManager.m_pSoil['size'] + soilSize = g_pJsonManager.m_pSoil["size"] - #TODO 缺少判断用户土地资源状况 - soil = BuildImage(background = g_sResourcePath / "soil/普通土地.png") + # TODO 缺少判断用户土地资源状况 + soil = BuildImage(background=g_sResourcePath / "soil/普通土地.png") await soil.resize(0, soilSize[0], soilSize[1]) - grass = BuildImage(background = g_sResourcePath / "soil/草土地.png") + grass = BuildImage(background=g_sResourcePath / "soil/草土地.png") await grass.resize(0, soilSize[0], soilSize[1]) - soilPos = g_pJsonManager.m_pSoil['soil'] + soilPos = g_pJsonManager.m_pSoil["soil"] userInfo = await g_pDBService.user.getUserInfoByUid(uid) - soilUnlock = int(userInfo['soil']) + soilUnlock = int(userInfo["soil"]) x = 0 y = 0 - isFirstExpansion = True #首次添加扩建图片 + isFirstExpansion = True # 首次添加扩建图片 isFirstRipe = True plant = None for index in range(0, 30): - x = soilPos[str(index + 1)]['x'] - y = soilPos[str(index + 1)]['y'] + x = soilPos[str(index + 1)]["x"] + y = soilPos[str(index + 1)]["y"] - #如果土地已经到达对应等级 + # 如果土地已经到达对应等级 if index < soilUnlock: await img.paste(soil, (x, y)) - isPlant, plant, isRipe, offsetX, offsetY = await cls.drawSoilPlant(uid, index + 1) + isPlant, plant, isRipe, offsetX, offsetY = await cls.drawSoilPlant( + uid, index + 1 + ) if isPlant: - await img.paste(plant, (x + soilSize[0] // 2 - plant.width // 2 + offsetX, - y + soilSize[1] // 2 - plant.height // 2 + offsetY)) + await img.paste( + plant, + ( + x + soilSize[0] // 2 - plant.width // 2 + offsetX, + y + soilSize[1] // 2 - plant.height // 2 + offsetY, + ), + ) - #1700 275 - #首次添加可收获图片 + # 1700 275 + # 首次添加可收获图片 if isRipe and isFirstRipe: - ripe = BuildImage(background = g_sResourcePath / "background/ripe.png") + ripe = BuildImage( + background=g_sResourcePath / "background/ripe.png" + ) - await img.paste(ripe, (x + soilSize[0] // 2 - ripe.width // 2, - y - ripe.height // 2)) + await img.paste( + ripe, + (x + soilSize[0] // 2 - ripe.width // 2, y - ripe.height // 2), + ) isFirstRipe = False else: @@ -109,54 +122,71 @@ class CFarmManager: if isFirstExpansion: isFirstExpansion = False - #首次添加扩建图片 - expansion = BuildImage(background = g_sResourcePath / "background/expansion.png") + # 首次添加扩建图片 + expansion = BuildImage( + background=g_sResourcePath / "background/expansion.png" + ) await expansion.resize(0, 69, 69) - await img.paste(expansion, (x + soilSize[0] // 2 - expansion.width // 2, - y + soilSize[1] // 2 - expansion.height)) + await img.paste( + expansion, + ( + x + soilSize[0] // 2 - expansion.width // 2, + y + soilSize[1] // 2 - expansion.height, + ), + ) - #左上角绘制用户信息 - #头像 + # 左上角绘制用户信息 + # 头像 image = await PlatformUtils.get_user_avatar(uid, "qq") if image: - avatar = BuildImage(background = image) + avatar = BuildImage(background=image) await img.paste(avatar, (125, 85)) - #头像框 - frame = BuildImage(background = g_sResourcePath / "background/frame.png") + # 头像框 + frame = BuildImage(background=g_sResourcePath / "background/frame.png") await img.paste(frame, (75, 44)) - #用户名 - nameImg = await BuildImage.build_text_image(userInfo['name'], size = 24, font_color = (77, 35, 4)) + # 用户名 + nameImg = await BuildImage.build_text_image( + userInfo["name"], size=24, font_color=(77, 35, 4) + ) await img.paste(nameImg, (300, 92)) - #经验值 + # 经验值 level = await g_pDBService.user.getUserLevelByUid(uid) beginX = 309 endX = 627 - #绘制宽度计算公式为 (当前经验值 / 经验值上限) * 宽度 + # 绘制宽度计算公式为 (当前经验值 / 经验值上限) * 宽度 width = int((level[2] / level[1]) * (endX - beginX)) await img.rectangle((beginX, 188, beginX + width, 222), (171, 194, 41)) - expImg = await BuildImage.build_text_image(f"{level[2]} / {level[1]}", size = 24, font_color = (102, 120, 19)) + expImg = await BuildImage.build_text_image( + f"{level[2]} / {level[1]}", size=24, font_color=(102, 120, 19) + ) await img.paste(expImg, (390, 193)) - #等级 - levelImg = await BuildImage.build_text_image(str(level[0]), size = 32, font_color = (214, 111, 1)) + # 等级 + levelImg = await BuildImage.build_text_image( + str(level[0]), size=32, font_color=(214, 111, 1) + ) await img.paste(levelImg, (660, 187)) - #金币 - pointImg = await BuildImage.build_text_image(str(userInfo['point']), size = 24, font_color = (253, 253, 253)) + # 金币 + pointImg = await BuildImage.build_text_image( + str(userInfo["point"]), size=24, font_color=(253, 253, 253) + ) await img.paste(pointImg, (330, 255)) - #点券 TODO - bondsImg = await BuildImage.build_text_image("0", size = 24, font_color = (253, 253, 253)) + # 点券 TODO + bondsImg = await BuildImage.build_text_image( + "0", size=24, font_color=(253, 253, 253) + ) await img.paste(bondsImg, (570, 255)) - #清晰度 + # 清晰度 definition = Config.get_config("zhenxun_plugin_farm", "绘制农场清晰度") if definition == "medium": await img.resize(0.6) @@ -196,9 +226,9 @@ class CFarmManager: if soilInfo: if soilInfo["soilLevel"] == 1: - iconPath = g_sResourcePath / f"soil/TODO.png" + iconPath = g_sResourcePath / "soil/TODO.png" else: - iconPath = g_sResourcePath / f"soil/普通土地.png" + iconPath = g_sResourcePath / "soil/普通土地.png" if iconPath.exists(): icon = (iconPath, 33, 33) @@ -211,27 +241,34 @@ class CFarmManager: totalNumber = "-" plantNumber = "-" else: - matureTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo.get("matureTime", 0))).strftime("%Y-%m-%d %H:%M:%S") + matureTime = ( + g_pToolManager.dateTime() + .fromtimestamp(int(soilInfo.get("matureTime", 0))) + .strftime("%Y-%m-%d %H:%M:%S") + ) soilStatus = await g_pDBService.userSoil.getUserSoilStatus(uid, i) - totalNumber = await g_pDBService.userSteal.getTotalStolenCount(uid, i) + totalNumber = await g_pDBService.userSteal.getTotalStolenCount( + uid, i + ) planInfo = await g_pDBService.plant.getPlantByName(plantName) if not planInfo: - plantNumber = f"None" + plantNumber = "None" else: plantNumber = f"{planInfo['harvest'] - totalNumber}" dataList.append( - [ - icon, - i, - plantName, - matureTime, - soilStatus, - totalNumber, - plantNumber, - ]) + [ + icon, + i, + plantName, + matureTime, + soilStatus, + totalNumber, + plantNumber, + ] + ) if len(dataList) >= 15: result = await ImageTemplate.table_page( @@ -258,7 +295,9 @@ class CFarmManager: return info @classmethod - async def drawSoilPlant(cls, uid: str, soilIndex: int) -> tuple[bool, BuildImage, bool, int, int]: + async def drawSoilPlant( + cls, uid: str, soilIndex: int + ) -> tuple[bool, BuildImage, bool, int, int]: """绘制植物资源 Args: @@ -273,54 +312,64 @@ class CFarmManager: soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex) if not soilInfo: - return False, None, False, 0, 0 #type: ignore + return False, None, False, 0, 0 # type: ignore - #是否枯萎 + # 是否枯萎 if int(soilInfo.get("wiltStatus", 0)) == 1: - plant = BuildImage(background = g_sResourcePath / f"plant/basic/9.png") + plant = BuildImage(background=g_sResourcePath / "plant/basic/9.png") await plant.resize(0, 150, 212) return True, plant, False, 0, 0 - #获取作物详细信息 - plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName']) + # 获取作物详细信息 + plantInfo = await g_pDBService.plant.getPlantByName(soilInfo["plantName"]) if not plantInfo: logger.error(f"绘制植物资源失败: {soilInfo['plantName']}") - return False, None, False, 0, 0 #type: ignore + return False, None, False, 0, 0 # type: ignore - offsetX = plantInfo.get('officX', 0) - offsetY = plantInfo.get('officY', 0) - offsetW = plantInfo.get('officW', 0) - offsetH = plantInfo.get('officH', 0) + offsetX = plantInfo.get("officX", 0) + offsetY = plantInfo.get("officY", 0) + offsetW = plantInfo.get("officW", 0) + offsetH = plantInfo.get("officH", 0) currentTime = g_pToolManager.dateTime().now().timestamp() - phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName']) + phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo["plantName"]) - #如果当前时间大于成熟时间 说明作物成熟 - if currentTime >= soilInfo['matureTime']: - plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{len(phaseList)}.png") + # 如果当前时间大于成熟时间 说明作物成熟 + if currentTime >= soilInfo["matureTime"]: + plant = BuildImage( + background=g_sResourcePath + / f"plant/{soilInfo['plantName']}/{len(phaseList)}.png" + ) return True, plant, True, offsetX, offsetY else: - #如果是多阶段作物 且没有成熟 #早期思路 多阶段作物 直接是倒数第二阶段图片 + # 如果是多阶段作物 且没有成熟 #早期思路 多阶段作物 直接是倒数第二阶段图片 # if soilInfo['harvestCount'] >= 1: # plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{plantInfo['phase'] - 1s}.png") # return True, plant, False, offsetX, offsetY - #如果没有成熟 则根据当前阶段进行绘制 - - elapsedTime = currentTime - soilInfo['plantTime'] - currentStage = sum(1 for thr in phaseList if elapsedTime >= thr) + # 如果没有成熟 则根据当前阶段进行绘制 + elapsedTime = currentTime - soilInfo["plantTime"] + currentStage = currentStage = sum( + 1 for thr in phaseList if elapsedTime >= thr + ) if currentStage <= 0: - if plantInfo['general'] == False: - plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/0.png") + if not plantInfo["general"]: + plant = BuildImage( + background=g_sResourcePath + / f"plant/{soilInfo['plantName']}/0.png" + ) else: - plant = BuildImage(background = g_sResourcePath / f"plant/basic/0.png") + plant = BuildImage(background=g_sResourcePath / "plant/basic/0.png") await plant.resize(0, 35 + offsetW, 58 + offsetH) else: - plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{currentStage}.png") + plant = BuildImage( + background=g_sResourcePath + / f"plant/{soilInfo['plantName']}/{currentStage}.png" + ) return True, plant, False, offsetX, offsetY @@ -336,10 +385,10 @@ class CFarmManager: "收获数量", "成熟时间(小时)", "收获次数", - "是否可以上架交易行" + "是否可以上架交易行", ] - #从数据库获取结构化数据 + # 从数据库获取结构化数据 seedRecords = await g_pDBService.userSeed.getUserSeedByUid(uid) or {} if not seedRecords: @@ -359,18 +408,20 @@ class CFarmManager: iconPath = g_sResourcePath / f"plant/{seedName}/icon.png" icon = (iconPath, 33, 33) if iconPath.exists() else "" - sellable = "可以" if plantInfo['sell'] else "不可以" + sellable = "可以" if plantInfo["sell"] else "不可以" - dataList.append([ - icon, - seedName, - count, - plantInfo['experience'], - plantInfo['harvest'], - plantInfo['time'], - plantInfo['crop'], - sellable - ]) + dataList.append( + [ + icon, + seedName, + count, + plantInfo["experience"], + plantInfo["harvest"], + plantInfo["time"], + plantInfo["crop"], + sellable, + ] + ) except KeyError: continue @@ -395,58 +446,61 @@ class CFarmManager: str: 返回结果 """ try: - #获取用户的种子数量 + # 获取用户的种子数量 count = await g_pDBService.userSeed.getUserSeedByName(uid, name) if count is None: - count = 0 #如果返回 None,则视为没有种子 + count = 0 # 如果返回 None,则视为没有种子 if count <= 0: - return f"没有在你的仓库发现{name}种子,快去买点吧!" + return g_sTranslation["sowing"]["noSeed"].format(name=name) - #如果播种数量超过仓库种子数量 + # 如果播种数量超过仓库种子数量 if count < num and num != -1: - return f"仓库中的{name}种子数量不足,当前剩余{count}个种子" + return g_sTranslation["sowing"]["noNum"].format(name=name, num=count) - #获取用户土地数量 + # 获取用户土地数量 soilNumber = await g_pDBService.user.getUserSoilByUid(uid) - #如果播种数量为 -1,表示播种所有可播种的土地 + # 如果播种数量为 -1,表示播种所有可播种的土地 if num == -1: num = count - #发送播种前信号 + # 发送播种前信号 await g_pEventManager.m_beforePlant.emit(uid=uid, name=name, num=num) - #记录是否成功播种 + # 记录是否成功播种 successCount = 0 for i in range(1, soilNumber + 1): if count > 0 and num > 0: - success = await g_pDBService.userSoil.sowingByPlantName(uid, i, name) + success = await g_pDBService.userSoil.sowingByPlantName( + uid, i, name + ) if success: - #更新种子数量 + # 更新种子数量 num -= 1 count -= 1 - #记录种子消耗数量 + # 记录种子消耗数量 successCount += 1 - #发送播种后信号 - await g_pEventManager.m_afterPlant.emit(uid=uid, name=name, soilIndex=i) + # 发送播种后信号 + await g_pEventManager.m_afterPlant.emit( + uid=uid, name=name, soilIndex=i + ) - - #确保用户仓库数量更新 + # 确保用户仓库数量更新 if successCount > 0: await g_pDBService.userSeed.updateUserSeedByName(uid, name, count) - #根据播种结果给出反馈 + # 根据播种结果给出反馈 if num == 0: - return f"播种{name}成功!仓库剩余{count}个种子" + return g_sTranslation["sowing"]["success"].format(name=name, num=count) else: - return f"播种数量超出开垦土地数量,已将可播种土地成功播种{name}!仓库剩余{count}个种子" + return g_sTranslation["sowing"]["success2"].format(name=name, num=count) except Exception as e: - logger.warning(f"播种操作失败!", e=e) - return "播种失败,请稍后重试!" + logger.warning("播种操作失败!", e=e) + return g_sTranslation["sowing"]["error"] @classmethod async def harvest(cls, uid: str) -> str: @@ -463,12 +517,12 @@ class CFarmManager: soilNumber = await g_pDBService.user.getUserSoilByUid(uid) - harvestRecords = [] #收获日志记录 - experience = 0 #总经验值 - harvestCount = 0 #成功收获数量 + harvestRecords = [] # 收获日志记录 + experience = 0 # 总经验值 + harvestCount = 0 # 成功收获数量 for i in range(1, soilNumber + 1): - #如果没有种植 + # 如果没有种植 if not await g_pDBService.userSoil.isSoilPlanted(uid, i): continue @@ -476,21 +530,25 @@ class CFarmManager: if not soilInfo: continue - #如果是枯萎状态 + # 如果是枯萎状态 if soilInfo.get("wiltStatus", 1) == 1: continue - plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName']) + plantInfo = await g_pDBService.plant.getPlantByName( + soilInfo["plantName"] + ) if not plantInfo: continue currentTime = g_pToolManager.dateTime().now() - matureTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['matureTime'])) + matureTime = g_pToolManager.dateTime().fromtimestamp( + int(soilInfo["matureTime"]) + ) if currentTime >= matureTime: - number = plantInfo['harvest'] + number = plantInfo["harvest"] - #处理偷菜扣除数量 + # 处理偷菜扣除数量 stealNum = await g_pDBService.userSteal.getTotalStolenCount(uid, i) number -= stealNum @@ -499,44 +557,67 @@ class CFarmManager: continue harvestCount += 1 - experience += plantInfo['experience'] + experience += plantInfo["experience"] - harvestRecords.append(f"收获作物:{soilInfo['plantName']},数量为:{number},经验为:{plantInfo['experience']}") + harvestRecords.append( + g_sTranslation["harvest"]["append"].format( + name=soilInfo["plantName"], + num=number, + exp=plantInfo["experience"], + ) + ) - await g_pDBService.userPlant.addUserPlantByUid(uid, soilInfo['plantName'], number) + await g_pDBService.userPlant.addUserPlantByUid( + uid, soilInfo["plantName"], number + ) - #如果到达收获次数上限 - if soilInfo['harvestCount'] + 1 >= plantInfo['crop']: - await g_pDBService.userSoil.updateUserSoil(uid, i, "wiltStatus", 1) + # 如果到达收获次数上限 + if soilInfo["harvestCount"] + 1 >= plantInfo["crop"]: + await g_pDBService.userSoil.updateUserSoil( + uid, i, "wiltStatus", 1 + ) else: - phase = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName']) - - ts, hc = int(currentTime.timestamp()), soilInfo["harvestCount"] + 1 - p1, p2, *rest = phase - - await g_pDBService.userSoil.updateUserSoilFields(uid, i, - { - "harvestCount": hc, - "plantTime": ts - p1 - p2, - "matureTime": ts + p2 + sum(rest), - } + phase = await g_pDBService.plant.getPlantPhaseByName( + soilInfo["plantName"] ) - await g_pEventManager.m_afterHarvest.emit(uid=uid, name=soilInfo['plantName'], num=number, soilIndex=i) + ts, hc = ( + int(currentTime.timestamp()), + soilInfo["harvestCount"] + 1, + ) + p1, p2, *rest = phase + + await g_pDBService.userSoil.updateUserSoilFields( + uid, + i, + { + "harvestCount": hc, + "plantTime": ts - p1 - p2, + "matureTime": ts + p2 + sum(rest), + }, + ) + + await g_pEventManager.m_afterHarvest.emit( + uid=uid, name=soilInfo["plantName"], num=number, soilIndex=i + ) if experience > 0: exp = await g_pDBService.user.getUserExpByUid(uid) await g_pDBService.user.updateUserExpByUid(uid, exp + experience) - harvestRecords.append(f"\t累计获得经验:{experience}") + harvestRecords.append( + g_sTranslation["harvest"]["exp"].format( + exp=experience, + ) + ) if harvestCount <= 0: - return "没有可收获的作物哦~ 不要试图拔苗助长" + return g_sTranslation["harvest"]["no"] else: return "\n".join(harvestRecords) except Exception as e: - logger.warning(f"收获操作失败!", e=e) - return "收获失败,请稍后重试!" + logger.warning("收获操作失败!", e=e) + return g_sTranslation["harvest"]["error"] @classmethod async def eradicate(cls, uid: str) -> str: @@ -554,38 +635,38 @@ class CFarmManager: experience = 0 for i in range(1, soilNumber + 1): - #如果没有种植 - if not await g_pDBService.userSoil.isSoilPlanted(uid, i): - continue + # 如果没有种植 + if not await g_pDBService.userSoil.isSoilPlanted(uid, i): + continue - soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i) - if not soilInfo: - continue + soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i) + if not soilInfo: + continue - #如果不是枯萎状态 - if soilInfo.get("wiltStatus", 0) == 0: - continue + # 如果不是枯萎状态 + if soilInfo.get("wiltStatus", 0) == 0: + continue - experience += 3 + experience += 3 - if g_bIsDebug: - experience += 999 + if g_bIsDebug: + experience += 999 - #批量更新数据库操作 - await g_pDBService.userSoil.deleteUserSoil(uid, i) + # 批量更新数据库操作 + await g_pDBService.userSoil.deleteUserSoil(uid, i) - #铲除作物会将偷菜记录清空 - await g_pDBService.userSteal.deleteStealRecord(uid, i) + # 铲除作物会将偷菜记录清空 + await g_pDBService.userSteal.deleteStealRecord(uid, i) - await g_pEventManager.m_afterEradicate.emit(uid=uid, soilIndex=i) + await g_pEventManager.m_afterEradicate.emit(uid=uid, soilIndex=i) if experience > 0: exp = await g_pDBService.user.getUserExpByUid(uid) await g_pDBService.user.updateUserExpByUid(uid, exp + experience) - return f"成功铲除荒废作物,累计获得经验:{experience}" + return g_sTranslation["eradicate"]["success"].format(exp=experience) else: - return "没有可以铲除的作物" + return g_sTranslation["eradicate"]["error"] @classmethod async def getUserPlantByUid(cls, uid: str) -> bytes: @@ -598,14 +679,7 @@ class CFarmManager: bytes: 返回图片 """ data_list = [] - column_name = [ - "-", - "作物名称", - "数量", - "单价", - "总价", - "是否可以上架交易行" - ] + column_name = ["-", "作物名称", "数量", "单价", "总价", "是否可以上架交易行"] plant = await g_pDBService.userPlant.getUserPlantByUid(uid) @@ -629,23 +703,14 @@ class CFarmManager: if icon_path.exists(): icon = (icon_path, 33, 33) - if plantInfo['sell'] == True: + if plantInfo["sell"] == True: sell = "可以" else: sell = "不可以" - number = int(count) * plantInfo['price'] + number = int(count) * plantInfo["price"] - data_list.append( - [ - icon, - name, - count, - plantInfo['price'], - number, - sell - ] - ) + data_list.append([icon, name, count, plantInfo["price"], number, sell]) result = await ImageTemplate.table_page( "作物仓库", @@ -667,30 +732,33 @@ class CFarmManager: Returns: str: 返回 """ - #用户信息 + # 用户信息 userInfo = await g_pDBService.user.getUserInfoByUid(uid) - stealTime = userInfo.get('stealTime', "") - stealCount = int(userInfo['stealCount']) + stealTime = userInfo.get("stealTime", "") + stealCount = int(userInfo["stealCount"]) if stealTime == "" or not stealTime: - stealTime = g_pToolManager.dateTime().date().today().strftime('%Y-%m-%d') + stealTime = g_pToolManager.dateTime().date().today().strftime("%Y-%m-%d") stealCount = 5 - elif g_pToolManager.dateTime().date().fromisoformat(stealTime) != g_pToolManager.dateTime().date().today(): - stealTime = g_pToolManager.dateTime().date().today().strftime('%Y-%m-%d') + elif ( + g_pToolManager.dateTime().date().fromisoformat(stealTime) + != g_pToolManager.dateTime().date().today() + ): + stealTime = g_pToolManager.dateTime().date().today().strftime("%Y-%m-%d") stealCount = 5 if stealCount <= 0: - return "你今天可偷次数到达上限啦,手下留情吧" + return g_sTranslation["stealing"]["max"] - #获取用户解锁地块数量 + # 获取用户解锁地块数量 soilNumber = await g_pDBService.user.getUserSoilByUid(target) - harvestRecords: List[str] = [] + harvestRecords: list[str] = [] isStealingNumber = 0 isStealingPlant = 0 for i in range(1, soilNumber + 1): - #如果没有种植 + # 如果没有种植 if not await g_pDBService.userSoil.isSoilPlanted(target, i): continue @@ -698,123 +766,162 @@ class CFarmManager: if not soilInfo: continue - #如果是枯萎状态 + # 如果是枯萎状态 if soilInfo.get("wiltStatus", 1) == 1: continue - #作物信息 - plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName']) + # 作物信息 + plantInfo = await g_pDBService.plant.getPlantByName(soilInfo["plantName"]) if not plantInfo: continue currentTime = g_pToolManager.dateTime().now() - matureTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['matureTime'])) + matureTime = g_pToolManager.dateTime().fromtimestamp( + int(soilInfo["matureTime"]) + ) if currentTime >= matureTime: - #如果偷过,则跳过该土地 + # 如果偷过,则跳过该土地 if await g_pDBService.userSteal.hasStealed(target, i, uid): + isStealingNumber += 1 continue - stealingNumber = plantInfo['harvest'] - await g_pDBService.userSteal.getTotalStolenCount(target, i) + stealingNumber = plantInfo[ + "harvest" + ] - await g_pDBService.userSteal.getTotalStolenCount(target, i) randomNumber = random.choice([1, 2]) randomNumber = min(randomNumber, stealingNumber) if randomNumber > 0: - await g_pDBService.userPlant.addUserPlantByUid(uid, soilInfo['plantName'], randomNumber) + await g_pDBService.userPlant.addUserPlantByUid( + uid, soilInfo["plantName"], randomNumber + ) - harvestRecords.append(f"成功偷到作物:{soilInfo['plantName']},数量为:{randomNumber}") + harvestRecords.append( + g_sTranslation["stealing"]["info"].format( + name=soilInfo["plantName"], num=randomNumber + ) + ) isStealingPlant += 1 - #如果将作物偷完,就直接更新状态 并记录用户偷取过 - if plantInfo['harvest'] - randomNumber + stealingNumber == 0: - #如果作物 是最后一阶段作物且偷完 则直接枯萎 - if soilInfo['harvestCount'] + 1 >= plantInfo['crop']: - await g_pDBService.userSoil.updateUserSoil(target, i, "wiltStatus", 1) + # 如果将作物偷完,就直接更新状态 并记录用户偷取过 + if plantInfo["harvest"] - randomNumber + stealingNumber == 0: + # 如果作物 是最后一阶段作物且偷完 则直接枯萎 + if soilInfo["harvestCount"] + 1 >= plantInfo["crop"]: + await g_pDBService.userSoil.updateUserSoil( + target, i, "wiltStatus", 1 + ) else: - phase = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName']) - - ts, hc = int(currentTime.timestamp()), soilInfo["harvestCount"] + 1 - p1, p2, *rest = phase - - await g_pDBService.userSoil.updateUserSoilFields(uid, i, - { - "harvestCount": hc, - "plantTime": ts - p1 - p2, - "matureTime": ts + p2 + sum(rest), - } + phase = await g_pDBService.plant.getPlantPhaseByName( + soilInfo["plantName"] ) - await g_pDBService.userSteal.addStealRecord(target, i, uid, randomNumber, int(g_pToolManager.dateTime().now().timestamp())) + ts, hc = ( + int(currentTime.timestamp()), + soilInfo["harvestCount"] + 1, + ) + p1, p2, *rest = phase + + await g_pDBService.userSoil.updateUserSoilFields( + uid, + i, + { + "harvestCount": hc, + "plantTime": ts - p1 - p2, + "matureTime": ts + p2 + sum(rest), + }, + ) + + await g_pDBService.userSteal.addStealRecord( + target, + i, + uid, + randomNumber, + int(g_pToolManager.dateTime().now().timestamp()), + ) else: - await g_pDBService.userSteal.addStealRecord(target, i, uid, randomNumber, int(g_pToolManager.dateTime().now().timestamp())) + await g_pDBService.userSteal.addStealRecord( + target, + i, + uid, + randomNumber, + int(g_pToolManager.dateTime().now().timestamp()), + ) if isStealingPlant <= 0 and isStealingNumber <= 0: - return "目标没有作物可以被偷" + return g_sTranslation["stealing"]["noPlant"] elif isStealingPlant <= 0 and isStealingNumber > 0: - return "你已经偷过目标啦,请手下留情" + return g_sTranslation["stealing"]["repeat"] else: stealCount -= 1 - await g_pDBService.user.updateStealCountByUid(uid, stealCount) + await g_pDBService.user.updateStealCountByUid(uid, stealTime, stealCount) return "\n".join(harvestRecords) @classmethod async def reclamationCondition(cls, uid: str) -> str: userInfo = await g_pDBService.user.getUserInfoByUid(uid) - rec = g_pJsonManager.m_pLevel['reclamation'] + rec = g_pJsonManager.m_pLevel["reclamation"] try: - if userInfo['soil'] >= 30: - return "你已经开垦了全部土地" + if userInfo["soil"] >= 30: + return g_sTranslation["reclamation"]["perfect"] rec = rec[f"{userInfo['soil'] + 1}"] - level = rec['level'] - point = rec['point'] - item = rec['item'] + level = rec["level"] + point = rec["point"] + item = rec["item"] str = "" if len(item) == 0: - str = f"下次开垦所需条件:等级:{level},农场币:{point}" + str = g_sTranslation["reclamation"]["next"].format( + level=level, num=point + ) else: - str = f"下次开垦所需条件:等级:{level},农场币:{point},物品:{item}" + str = g_sTranslation["reclamation"]["next2"].format( + level=level, num=point, item=item + ) return str - except Exception as e: - return "获取开垦土地条件失败!" + except Exception: + return g_sTranslation["reclamation"]["error"] @classmethod async def reclamation(cls, uid: str) -> str: userInfo = await g_pDBService.user.getUserInfoByUid(uid) level = await g_pDBService.user.getUserLevelByUid(uid) - rec = g_pJsonManager.m_pLevel['reclamation'] + rec = g_pJsonManager.m_pLevel["reclamation"] try: - if userInfo['soil'] >= 30: - return "你已经开垦了全部土地" + if userInfo["soil"] >= 30: + return g_sTranslation["reclamation"]["perfect"] rec = rec[f"{userInfo['soil'] + 1}"] - levelFileter = rec['level'] - point = rec['point'] - item = rec['item'] + levelFileter = rec["level"] + point = rec["point"] + item = rec["item"] if level[0] < levelFileter: - return f"当前用户等级{level[0]},升级所需等级为{levelFileter}" + return g_sTranslation["reclamation"]["nextLevel"].format( + level=level[0], next=levelFileter + ) - if userInfo['point'] < point: - return f"当前用户农场币不足,升级所需农场币为{point}" + if userInfo["point"] < point: + return g_sTranslation["reclamation"]["noNum"].format(num=point) - #TODO 缺少判断消耗的item - await g_pDBService.user.updateUserPointByUid(uid, userInfo['point'] - point) - await g_pDBService.user.updateUserSoilByUid(uid, userInfo['soil'] + 1) + # TODO 缺少判断消耗的item + await g_pDBService.user.updateUserPointByUid(uid, userInfo["point"] - point) + await g_pDBService.user.updateUserSoilByUid(uid, userInfo["soil"] + 1) + + return g_sTranslation["reclamation"]["success"] + except Exception: + return g_sTranslation["reclamation"]["error1"] - return "开垦土地成功!" - except Exception as e: - return "执行开垦失败!" g_pFarmManager = CFarmManager() diff --git a/farm/shop.py b/farm/shop.py index 1843b0d..4f1bb10 100644 --- a/farm/shop.py +++ b/farm/shop.py @@ -1,19 +1,15 @@ import math -import plistlib -from itertools import islice from zhenxun.services.log import logger -from zhenxun.utils._build_image import BuildImage from zhenxun.utils.image_utils import ImageTemplate -from ..config import g_sResourcePath +from ..config import g_sResourcePath, g_sTranslation from ..dbService import g_pDBService -from ..json import g_pJsonManager class CShopManager: @classmethod - async def getSeedShopImage(cls, filterKey: str|int = 1, num: int = 1) -> bytes: + async def getSeedShopImage(cls, filterKey: str | int = 1, num: int = 1) -> bytes: """获取商店页面 Args: @@ -44,7 +40,7 @@ class CShopManager: "收获数量", "成熟时间(小时)", "收获次数", - "是否可以上架交易行" + "是否可以上架交易行", ] # 查询所有可购买作物,并根据筛选关键字过滤 @@ -52,10 +48,10 @@ class CShopManager: filteredPlants = [] for plant in plants: # 跳过未解锁购买的种子 - if plant['isBuy'] == 0: + if plant["isBuy"] == 0: continue # 字符串筛选 - if filterStr and filterStr not in plant['name']: + if filterStr and filterStr not in plant["name"]: continue filteredPlants.append(plant) @@ -63,7 +59,7 @@ class CShopManager: totalCount = len(filteredPlants) pageCount = math.ceil(totalCount / 15) if totalCount else 1 startIndex = (page - 1) * 15 - pageItems = filteredPlants[startIndex: startIndex + 15] + pageItems = filteredPlants[startIndex : startIndex + 15] # 构建数据行 dataList = [] @@ -75,20 +71,22 @@ class CShopManager: icon = (iconPath, 33, 33) # 交易行标记 - sell = "可以" if plant['sell'] else "不可以" + sell = "可以" if plant["sell"] else "不可以" - dataList.append([ - icon, - plant['name'], # 种子名称 - plant['buy'], # 种子单价 - plant['level'], # 解锁等级 - plant['price'], # 果实单价 - plant['experience'], # 收获经验 - plant['harvest'], # 收获数量 - plant['time'], # 成熟时间(小时) - plant['crop'], # 收获次数 - sell # 是否可上架交易行 - ]) + dataList.append( + [ + icon, + plant["name"], # 种子名称 + plant["buy"], # 种子单价 + plant["level"], # 解锁等级 + plant["price"], # 果实单价 + plant["experience"], # 收获经验 + plant["harvest"], # 收获数量 + plant["time"], # 成熟时间(小时) + plant["crop"], # 收获次数 + sell, # 是否可上架交易行 + ] + ) # 页码标题 title = f"种子商店 页数: {page}/{pageCount}" @@ -102,7 +100,6 @@ class CShopManager: ) return result.pic2bytes() - @classmethod async def buySeed(cls, uid: str, name: str, num: int = 1) -> str: """购买种子 @@ -117,31 +114,35 @@ class CShopManager: """ if num <= 0: - return "请输入购买数量!" + return g_sTranslation["buySeed"]["notNum"] plantInfo = await g_pDBService.plant.getPlantByName(name) if not plantInfo: - return "购买出错!请检查需购买的种子名称!" + return g_sTranslation["buySeed"]["error"] level = await g_pDBService.user.getUserLevelByUid(uid) - if level[0] < int(plantInfo['level']): - return "你的等级不够哦,努努力吧" + if level[0] < int(plantInfo["level"]): + return g_sTranslation["buySeed"]["noLevel"] point = await g_pDBService.user.getUserPointByUid(uid) - total = int(plantInfo['buy']) * num + total = int(plantInfo["buy"]) * num - logger.debug(f"用户:{uid}购买{name},数量为{num}。用户农场币为{point},购买需要{total}") + logger.debug( + f"用户:{uid}购买{name},数量为{num}。用户农场币为{point},购买需要{total}" + ) if point < total: - return "你的农场币不够哦~ 快速速氪金吧!" + return g_sTranslation["buySeed"]["noPoint"] else: await g_pDBService.user.updateUserPointByUid(uid, point - total) if not await g_pDBService.userSeed.addUserSeedByUid(uid, name, num): - return "购买失败,执行数据库错误!" + return g_sTranslation["buySeed"]["errorSql"] - return f"成功购买{name},花费{total}农场币, 剩余{point - total}农场币" + return g_sTranslation["buySeed"]["success"].format( + name=name, total=total, point=point - total + ) @classmethod async def sellPlantByUid(cls, uid: str, name: str = "", num: int = 1) -> str: @@ -158,11 +159,11 @@ class CShopManager: plant = await g_pDBService.userPlant.getUserPlantByUid(uid) if not plant: - return "你仓库没有可以出售的作物" + return g_sTranslation["sellPlant"]["no"] point = 0 totalSold = 0 - isAll = (num == -1) + isAll = num == -1 if name == "": for plantName, count in plant.items(): @@ -170,16 +171,18 @@ class CShopManager: if not plantInfo: continue - point += plantInfo['price'] * count + point += plantInfo["price"] * count await g_pDBService.userPlant.updateUserPlantByName(uid, plantName, 0) else: if name not in plant: - return f"出售作物{name}出错:仓库中不存在该作物" + return g_sTranslation["sellPlant"]["error"].format(name=name) available = plant[name] sellAmount = available if isAll else min(available, num) if sellAmount <= 0: - return f"出售作物{name}出错:数量不足" - await g_pDBService.userPlant.updateUserPlantByName(uid, name, available - sellAmount) + return g_sTranslation["sellPlant"]["error1"].format(name=name) + await g_pDBService.userPlant.updateUserPlantByName( + uid, name, available - sellAmount + ) totalSold = sellAmount if name == "": @@ -189,7 +192,7 @@ class CShopManager: if not plantInfo: price = 0 else: - price = plantInfo['price'] + price = plantInfo["price"] totalPoint = totalSold * price @@ -197,8 +200,13 @@ class CShopManager: await g_pDBService.user.updateUserPointByUid(uid, currentPoint + totalPoint) if name == "": - return f"成功出售所有作物,获得农场币:{totalPoint},当前农场币:{currentPoint + totalPoint}" + return g_sTranslation["sellPlant"]["success"].format( + point=totalPoint, num=currentPoint + totalPoint + ) else: - return f"成功出售{name},获得农场币:{totalPoint},当前农场币:{currentPoint + totalPoint}" + return g_sTranslation["sellPlant"]["success1"].format( + name=name, point=totalPoint, num=currentPoint + totalPoint + ) + g_pShopManager = CShopManager() diff --git a/json.py b/json.py index ad68b1e..fb90a96 100644 --- a/json.py +++ b/json.py @@ -1,5 +1,4 @@ import json -from pathlib import Path from zhenxun.services.log import logger @@ -98,4 +97,5 @@ class CJsonManager: logger.warning(f"sign_in.json JSON格式错误: {e}") return False + g_pJsonManager = CJsonManager() diff --git a/request.py b/request.py index 91967dc..a74c2da 100644 --- a/request.py +++ b/request.py @@ -1,6 +1,5 @@ import json import os -from datetime import datetime import httpx @@ -15,12 +14,14 @@ class CRequestManager: m_sTokens = "xZ%?z5LtWV7H:0-Xnwp+bNRNQ-jbfrxG" @classmethod - async def download(cls, - url: str, - savePath: str, - fileName: str, - params: dict | None = None, - jsonData: dict | None = None) -> bool: + async def download( + cls, + url: str, + savePath: str, + fileName: str, + params: dict | None = None, + jsonData: dict | None = None, + ) -> bool: """下载文件到指定路径并覆盖已存在的文件 Args: @@ -51,7 +52,9 @@ class CRequestManager: f.write(response.content) return True else: - logger.warning(f"文件下载失败: HTTP {response.status_code} {response.text}") + logger.warning( + f"文件下载失败: HTTP {response.status_code} {response.text}" + ) return False except Exception as e: @@ -84,7 +87,9 @@ class CRequestManager: if response.status_code == 200: return response.json() else: - logger.warning(f"{name}请求失败: HTTP {response.status_code} {response.text}") + logger.warning( + f"{name}请求失败: HTTP {response.status_code} {response.text}" + ) return {} except httpx.RequestError as e: logger.warning(f"{name}请求异常", e=e) @@ -115,7 +120,9 @@ class CRequestManager: if response.status_code == 200: return response.json() else: - logger.warning(f"{name}请求失败: HTTP {response.status_code} {response.text}") + logger.warning( + f"{name}请求失败: HTTP {response.status_code} {response.text}" + ) return {} except httpx.RequestError as e: logger.warning(f"{name}请求异常", e=e) @@ -128,7 +135,7 @@ class CRequestManager: async def initSignInFile(cls) -> bool: if os.path.exists(g_sSignInPath): try: - with open(g_sSignInPath, "r", encoding="utf-8") as f: + with open(g_sSignInPath, encoding="utf-8") as f: content = f.read() sign = json.loads(content) @@ -141,8 +148,8 @@ class CRequestManager: else: logger.warning("真寻农场签到文件检查失败, 即将下载") return await cls.downloadSignInFile() - except json.JSONDecodeError as e: - logger.warning(f"真寻农场签到文件格式错误, 即将下载") + except json.JSONDecodeError: + logger.warning("真寻农场签到文件格式错误, 即将下载") return await cls.downloadSignInFile() else: return await cls.downloadSignInFile() @@ -156,7 +163,7 @@ class CRequestManager: path = str(g_sSignInPath.parent.resolve(strict=False)) yearMonth = g_pToolManager.dateTime().now().strftime("%Y%m") - await cls.download(url, path, "signTemp.json", jsonData={'date':yearMonth}) + await cls.download(url, path, "signTemp.json", jsonData={"date": yearMonth}) g_pToolManager.renameFile(f"{path}/signTemp.json", "sign_in.json") return True diff --git a/tool.py b/tool.py index 58df77a..ffd6190 100644 --- a/tool.py +++ b/tool.py @@ -3,9 +3,146 @@ from datetime import datetime from zoneinfo import ZoneInfo from zhenxun.services.log import logger +from zhenxun.utils.message import MessageUtils + +from .dbService import g_pDBService class CToolManager: + @classmethod + async def isRegisteredByUid(cls, uid: str) -> bool: + result = await g_pDBService.user.isUserExist(uid) + + if not result: + await MessageUtils.build_message( + "尚未开通农场,快at我发送 开通农场 开通吧" + ).send() + return False + + return True + + @classmethod + def sanitize_username(cls, username: str, max_length: int = 15) -> str: + """ + 安全处理用户名 + 功能: + 1. 移除首尾空白 + 2. 过滤危险字符 + 3. 转义单引号 + 4. 处理空值 + 5. 限制长度 + """ + # 处理空值 + if not username: + return "神秘农夫" + + # 基础清洗 + cleaned = username.strip() + + # 允许的字符白名单(可自定义扩展) + safe_chars = { + "_", + "-", + "!", + "@", + "#", + "$", + "%", + "^", + "&", + "*", + "(", + ")", + "+", + "=", + ".", + ",", + "~", + "·", + " ", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + } + # 添加常用中文字符(Unicode范围) + safe_chars.update(chr(c) for c in range(0x4E00, 0x9FFF + 1)) + + # 过滤危险字符 + filtered = [ + c if c in safe_chars or 0x4E00 <= ord(c) <= 0x9FFF else "" for c in cleaned + ] + + # 合并处理结果 + safe_str = "".join(filtered) + + # 转义单引号(双重保障) + escaped = safe_str.replace("'", "''") + + # 处理空结果 + if not escaped: + return "神秘农夫" + + # 长度限制 + return escaped[:max_length] @classmethod def renameFile(cls, currentFilePath: str, newFileName: str) -> bool: @@ -36,4 +173,5 @@ class CToolManager: tz = ZoneInfo("Asia/Shanghai") return datetime.now(tz) + g_pToolManager = CToolManager()