🎨 将文本内容同一更新

🐛 修复偷菜无法正常计算的BUG
This commit is contained in:
Art_Sakura 2025-06-06 10:56:22 +08:00
parent ba2ecf19fc
commit ac13ab9280
20 changed files with 1284 additions and 699 deletions

197
.gitignore vendored
View File

@ -1,3 +1,198 @@
__pycache__ __pycache__/
./config/sign_in.json ./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

View File

@ -39,7 +39,7 @@ __plugin_meta__ = PluginMetadata(
""".strip(), """.strip(),
extra=PluginExtraData( extra=PluginExtraData(
author="Art_Sakura", author="Art_Sakura",
version="1.4.2", version="1.4.3",
commands=[Command(command="我的农场")], commands=[Command(command="我的农场")],
menu_type="群内小游戏", menu_type="群内小游戏",
configs=[ configs=[
@ -66,8 +66,8 @@ __plugin_meta__ = PluginMetadata(
value="http://diuse.work", value="http://diuse.work",
help="签到、交易行、活动等服务器地址", help="签到、交易行、活动等服务器地址",
default_value="http://diuse.work", default_value="http://diuse.work",
) ),
] ],
).to_dict(), ).to_dict(),
) )
driver = get_driver() driver = get_driver()
@ -84,6 +84,7 @@ async def start():
await g_pDBService.init() await g_pDBService.init()
# 析构函数 # 析构函数
@driver.on_shutdown @driver.on_shutdown
async def shutdown(): async def shutdown():
@ -92,12 +93,7 @@ async def shutdown():
await g_pDBService.cleanup() await g_pDBService.cleanup()
@scheduler.scheduled_job( @scheduler.scheduled_job(trigger="cron", hour=0, minute=30, id="signInFile")
trigger="cron",
hour=0,
minute=30,
id="signInFile"
)
async def signInFile(): async def signInFile():
try: try:
await g_pJsonManager.initSignInFile() await g_pJsonManager.initSignInFile()

View File

@ -1,10 +1,20 @@
import inspect import inspect
from nonebot.adapters import Event, MessageTemplate from nonebot.adapters import Event
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot_plugin_alconna import (Alconna, AlconnaMatch, AlconnaQuery, Args, from nonebot_plugin_alconna import (
Arparma, At, Match, MultiVar, Option, Alconna,
Query, Subcommand, on_alconna, store_true) AlconnaQuery,
Args,
At,
Match,
MultiVar,
Option,
Query,
Subcommand,
on_alconna,
store_true,
)
from nonebot_plugin_uninfo import Uninfo from nonebot_plugin_uninfo import Uninfo
from nonebot_plugin_waiter import waiter from nonebot_plugin_waiter import waiter
@ -12,24 +22,13 @@ from zhenxun.configs.config import BotConfig
from zhenxun.services.log import logger from zhenxun.services.log import logger
from zhenxun.utils.message import MessageUtils from zhenxun.utils.message import MessageUtils
from .config import g_bSignStatus from .config import g_bSignStatus, g_sTranslation
from .dbService import g_pDBService from .dbService import g_pDBService
from .farm.farm import g_pFarmManager from .farm.farm import g_pFarmManager
from .farm.shop import g_pShopManager from .farm.shop import g_pShopManager
from .json import g_pJsonManager from .json import g_pJsonManager
from .tool import g_pToolManager 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( diuse_register = on_alconna(
Alconna("开通农场"), Alconna("开通农场"),
priority=5, priority=5,
@ -37,89 +36,40 @@ diuse_register = on_alconna(
block=True, block=True,
) )
@diuse_register.handle() @diuse_register.handle()
async def handle_register(session: Uninfo): async def handle_register(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
user = await g_pDBService.user.getUserInfoByUid(uid) user = await g_pDBService.user.getUserInfoByUid(uid)
if user: if user:
await MessageUtils.build_message("🎉 您已经开通农场啦~").send(reply_to=True) await MessageUtils.build_message(g_sTranslation["register"]["repeat"]).send(
reply_to=True
)
return return
try: try:
raw_name = str(session.user.name) 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( success = await g_pDBService.user.initUserInfoByUid(
uid=uid, uid=uid, name=safe_name, exp=0, point=500
name=safe_name,
exp=0,
point=500
) )
msg = ( msg = (
"✅ 农场开通成功!\n💼 初始资金500农场币" g_sTranslation["register"]["success"]
if success if success
else "⚠️ 开通失败,请稍后再试" else g_sTranslation["register"]["error"]
) )
logger.info(f"用户注册 {'成功' if success else '失败'}{uid}") logger.info(f"用户注册 {'成功' if success else '失败'}{uid}")
except Exception as e: except Exception as e:
msg = "⚠️ 系统繁忙,请稍后再试" msg = g_sTranslation["register"]["error"]
logger.error(f"注册异常 | UID:{uid} | 错误:{str(e)}") logger.error(f"注册异常 | UID:{uid} | 错误:{e}")
await MessageUtils.build_message(msg).send(reply_to=True) 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( diuse_farm = on_alconna(
Alconna( Alconna(
@ -137,25 +87,28 @@ diuse_farm = on_alconna(
Subcommand("sell-plant", Args["name?", str]["num?", int], help_text="出售作物"), Subcommand("sell-plant", Args["name?", str]["num?", int], help_text="出售作物"),
Subcommand("stealing", Args["target?", At], help_text="偷菜"), Subcommand("stealing", Args["target?", At], help_text="偷菜"),
Subcommand("buy-point", Args["num?", int], 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("change-name", Args["name?", str], help_text="更改农场名"),
Subcommand("sign-in", help_text="农场签到"), Subcommand("sign-in", help_text="农场签到"),
Subcommand("admin-up", Args["num?", int], help_text="农场下阶段"), Subcommand("admin-up", Args["num?", int], help_text="农场下阶段"),
), ),
priority=5, priority=5,
block=True, block=True,
use_cmd_start=True,
) )
@diuse_farm.assign("$main") @diuse_farm.assign("$main")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
image = await g_pFarmManager.drawFarmByUid(uid) image = await g_pFarmManager.drawFarmByUid(uid)
await MessageUtils.build_message(image).send(reply_to=True) await MessageUtils.build_message(image).send(reply_to=True)
diuse_farm.shortcut( diuse_farm.shortcut(
"农场详述", "农场详述",
command="我的农场", command="我的农场",
@ -163,16 +116,20 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("detail") @diuse_farm.assign("detail")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
info = await g_pFarmManager.drawDetailFarmByUid(uid) 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( diuse_farm.shortcut(
"我的农场币", "我的农场币",
@ -181,16 +138,20 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("my-point") @diuse_farm.assign("my-point")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
point = await g_pDBService.user.getUserPointByUid(uid) point = await g_pDBService.user.getUserPointByUid(uid)
if point < 0: if point < 0:
await MessageUtils.build_message("尚未开通农场快at我发送 开通农场 开通吧").send() await MessageUtils.build_message(g_sTranslation["basic"]["notFarm"]).send()
return False 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( diuse_farm.shortcut(
"种子商店(.*?)", "种子商店(.*?)",
@ -199,11 +160,12 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("seed-shop") @diuse_farm.assign("seed-shop")
async def _(session: Uninfo, res: Match[tuple[str, ...]]): async def _(session: Uninfo, res: Match[tuple[str, ...]]):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
if res.result is inspect._empty: if res.result is inspect._empty:
@ -221,7 +183,12 @@ async def _(session: Uninfo, res: Match[tuple[str, ...]]):
else: else:
filterKey = first 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]) page = int(raw[1])
if filterKey is None: if filterKey is None:
@ -231,6 +198,7 @@ async def _(session: Uninfo, res: Match[tuple[str, ...]]):
await MessageUtils.build_message(image).send() await MessageUtils.build_message(image).send()
diuse_farm.shortcut( diuse_farm.shortcut(
"购买种子(?P<name>.*?)", "购买种子(?P<name>.*?)",
command="我的农场", command="我的农场",
@ -238,21 +206,25 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("buy-seed") @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: if not name.available:
await MessageUtils.build_message( await MessageUtils.build_message(g_sTranslation["buySeed"]["notSeed"]).finish(
"请在指令后跟需要购买的种子名称" reply_to=True
).finish(reply_to=True) )
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
result = await g_pShopManager.buySeed(uid, name.result, num.result) result = await g_pShopManager.buySeed(uid, name.result, num.result)
await MessageUtils.build_message(result).send(reply_to=True) await MessageUtils.build_message(result).send(reply_to=True)
diuse_farm.shortcut( diuse_farm.shortcut(
"我的种子", "我的种子",
command="我的农场", command="我的农场",
@ -260,16 +232,18 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("my-seed") @diuse_farm.assign("my-seed")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
result = await g_pFarmManager.getUserSeedByUid(uid) result = await g_pFarmManager.getUserSeedByUid(uid)
await MessageUtils.build_message(result).send(reply_to=True) await MessageUtils.build_message(result).send(reply_to=True)
diuse_farm.shortcut( diuse_farm.shortcut(
"播种(?P<name>.*?)", "播种(?P<name>.*?)",
command="我的农场", command="我的农场",
@ -277,16 +251,19 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("sowing") @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: if not name.available:
await MessageUtils.build_message( await MessageUtils.build_message(g_sTranslation["sowing"]["notSeed"]).finish(
"请在指令后跟需要播种的种子名称" reply_to=True
).finish(reply_to=True) )
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
result = await g_pFarmManager.sowing(uid, name.result, num.result) result = await g_pFarmManager.sowing(uid, name.result, num.result)
@ -300,16 +277,18 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("harvest") @diuse_farm.assign("harvest")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
result = await g_pFarmManager.harvest(uid) result = await g_pFarmManager.harvest(uid)
await MessageUtils.build_message(result).send(reply_to=True) await MessageUtils.build_message(result).send(reply_to=True)
diuse_farm.shortcut( diuse_farm.shortcut(
"铲除", "铲除",
command="我的农场", command="我的农场",
@ -317,11 +296,12 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("eradicate") @diuse_farm.assign("eradicate")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
result = await g_pFarmManager.eradicate(uid) result = await g_pFarmManager.eradicate(uid)
@ -335,11 +315,12 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("my-plant") @diuse_farm.assign("my-plant")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
result = await g_pFarmManager.getUserPlantByUid(uid) result = await g_pFarmManager.getUserPlantByUid(uid)
@ -352,15 +333,16 @@ reclamation = on_alconna(
block=True, block=True,
) )
@reclamation.handle() @reclamation.handle()
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
condition = await g_pFarmManager.reclamationCondition(uid) condition = await g_pFarmManager.reclamationCondition(uid)
condition += "\n 回复是将执行开垦" condition += f"\n{g_sTranslation['reclamation']['confirm']}"
await MessageUtils.build_message(condition).send(reply_to=True) await MessageUtils.build_message(condition).send(reply_to=True)
@waiter(waits=["message"], keep_session=True) @waiter(waits=["message"], keep_session=True)
@ -369,7 +351,9 @@ async def _(session: Uninfo):
resp = await check.wait(timeout=60) resp = await check.wait(timeout=60)
if resp is None: 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 return
if not resp == "": if not resp == "":
return return
@ -377,6 +361,7 @@ async def _(session: Uninfo):
res = await g_pFarmManager.reclamation(uid) res = await g_pFarmManager.reclamation(uid)
await MessageUtils.build_message(res).send(reply_to=True) await MessageUtils.build_message(res).send(reply_to=True)
diuse_farm.shortcut( diuse_farm.shortcut(
"出售作物(?P<name>.*?)", "出售作物(?P<name>.*?)",
command="我的农场", command="我的农场",
@ -384,16 +369,20 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("sell-plant") @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) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
result = await g_pShopManager.sellPlantByUid(uid, name.result, num.result) result = await g_pShopManager.sellPlantByUid(uid, name.result, num.result)
await MessageUtils.build_message(result).send(reply_to=True) await MessageUtils.build_message(result).send(reply_to=True)
diuse_farm.shortcut( diuse_farm.shortcut(
"偷菜", "偷菜",
command="我的农场", command="我的农场",
@ -401,26 +390,32 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("stealing") @diuse_farm.assign("stealing")
async def _(session: Uninfo, target: Match[At]): async def _(session: Uninfo, target: Match[At]):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
if not target.available: 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 tar = target.result
result = await g_pDBService.user.isUserExist(tar.target) result = await g_pDBService.user.isUserExist(tar.target)
if not result: if not result:
await MessageUtils.build_message("目标尚未开通农场快邀请ta开通吧").send() await MessageUtils.build_message(
g_sTranslation["stealing"]["targetNotFarm"]
).send()
return None return None
result = await g_pFarmManager.stealing(uid, tar.target) result = await g_pFarmManager.stealing(uid, tar.target)
await MessageUtils.build_message(result).send(reply_to=True) await MessageUtils.build_message(result).send(reply_to=True)
diuse_farm.shortcut( diuse_farm.shortcut(
"购买农场币(.*?)", "购买农场币(.*?)",
command="我的农场", command="我的农场",
@ -428,16 +423,17 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("buy-point") @diuse_farm.assign("buy-point")
async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)): async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)):
if num.result <= 0: if num.result <= 0:
await MessageUtils.build_message( await MessageUtils.build_message("请在指令后跟需要购买农场币的数量").finish(
"请在指令后跟需要购买农场币的数量" reply_to=True
).finish(reply_to=True) )
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
result = await g_pFarmManager.buyPointByUid(uid, num.result) result = await g_pFarmManager.buyPointByUid(uid, num.result)
@ -451,30 +447,38 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("change-name") @diuse_farm.assign("change-name")
async def _(session: Uninfo, name: Match[str]): async def _(session: Uninfo, name: Match[str]):
if not name.available: if not name.available:
await MessageUtils.build_message( await MessageUtils.build_message(g_sTranslation["changeName"]["noName"]).finish(
"请在指令后跟需要更改的农场名" reply_to=True
).finish(reply_to=True) )
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
safeName = sanitize_username(name.result) safeName = g_pToolManager.sanitize_username(name.result)
if safeName == "神秘农夫": if safeName == "神秘农夫":
await MessageUtils.build_message("农场名不支持特殊符号!").send(reply_to=True) await MessageUtils.build_message(g_sTranslation["changeName"]["error"]).send(
reply_to=True
)
return return
result = await g_pDBService.user.updateUserNameByUid(uid, safeName) result = await g_pDBService.user.updateUserNameByUid(uid, safeName)
if result == True: if result:
await MessageUtils.build_message("更新农场名成功!").send(reply_to=True) await MessageUtils.build_message(g_sTranslation["changeName"]["success"]).send(
reply_to=True
)
else: else:
await MessageUtils.build_message("更新农场名失败!").send(reply_to=True) await MessageUtils.build_message(g_sTranslation["changeName"]["error1"]).send(
reply_to=True
)
diuse_farm.shortcut( diuse_farm.shortcut(
"农场签到", "农场签到",
@ -483,16 +487,17 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("sign-in") @diuse_farm.assign("sign-in")
async def _(session: Uninfo): async def _(session: Uninfo):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
#判断签到是否正常加载 # 判断签到是否正常加载
if not g_bSignStatus: if not g_bSignStatus:
await MessageUtils.build_message("签到功能异常!").send() await MessageUtils.build_message(g_sTranslation["signIn"]["error"]).send()
return return
@ -500,39 +505,50 @@ async def _(session: Uninfo):
message = "" message = ""
status = await g_pDBService.userSign.sign(uid, toDay.strftime("%Y-%m-%d")) status = await g_pDBService.userSign.sign(uid, toDay.strftime("%Y-%m-%d"))
#如果完成签到 # 如果完成签到
if status == 1 or status == 2: if status == 1 or status == 2:
#获取签到总天数 # 获取签到总天数
signDay = await g_pDBService.userSign.getUserSignCountByDate(uid, toDay.strftime("%Y-%m")) signDay = await g_pDBService.userSign.getUserSignCountByDate(
exp, point = await g_pDBService.userSign.getUserSignRewardByDate(uid, toDay.strftime("%Y-%m-%d")) 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: if reward:
extraPoint = reward.get('point', 0) extraPoint = reward.get("point", 0)
extraExp = reward.get('exp', 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: if vipPoint > 0:
message += f",额外获得点券{vipPoint}" message += g_sTranslation["signIn"]["grandTotal1"].format(num=vipPoint)
if plant: if plant:
for key, value in plant.items(): for key, value in plant.items():
message += f"\n获得{key}种子 * {value}" message += g_sTranslation["signIn"]["grandTotal2"].format(
name=key, num=value
)
else: else:
message = "签到失败!未知错误" message = g_sTranslation["signIn"]["error1"]
await MessageUtils.build_message(message).send() await MessageUtils.build_message(message).send()
# await MessageUtils.alc_forward_msg([info], session.self_id, BotConfig.self_nickname).send(reply_to=True) # await MessageUtils.alc_forward_msg([info], session.self_id, BotConfig.self_nickname).send(reply_to=True)
diuse_farm.shortcut( diuse_farm.shortcut(
"农场下阶段(.*?)", "农场下阶段(.*?)",
command="我的农场", command="我的农场",
@ -540,11 +556,12 @@ diuse_farm.shortcut(
prefix=True, prefix=True,
) )
@diuse_farm.assign("admin-up") @diuse_farm.assign("admin-up")
async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)): async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)):
uid = str(session.user.id) uid = str(session.user.id)
if not await isRegisteredByUid(uid): if not await g_pToolManager.isRegisteredByUid(uid):
return return
await g_pDBService.userSoil.nextPhase(uid, num.result) await g_pDBService.userSoil.nextPhase(uid, num.result)

View File

@ -2,15 +2,108 @@ from pathlib import Path
from zhenxun.configs.path_config import DATA_PATH from zhenxun.configs.path_config import DATA_PATH
# 签到状态
g_bSignStatus = True
# 是否处于Debug模式
g_bIsDebug = False g_bIsDebug = False
# 数据库文件目录
g_sDBPath = DATA_PATH / "farm_db" g_sDBPath = DATA_PATH / "farm_db"
# 数据库文件路径
g_sDBFilePath = DATA_PATH / "farm_db/farm.db" g_sDBFilePath = DATA_PATH / "farm_db/farm.db"
# 农场资源文件目录
g_sResourcePath = Path(__file__).resolve().parent / "resource" g_sResourcePath = Path(__file__).resolve().parent / "resource"
# 农场作物数据库
g_sPlantPath = g_sResourcePath / "db/plant.db" g_sPlantPath = g_sResourcePath / "db/plant.db"
# 农场配置文件目录
g_sConfigPath = Path(__file__).resolve().parent / "config" g_sConfigPath = Path(__file__).resolve().parent / "config"
# 农场签到文件路径
g_sSignInPath = g_sConfigPath / "sign_in.json" 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": "❌ 签到失败!未知错误 💔",
},
}

View File

@ -1,4 +1,3 @@
import math
import os import os
import re import re
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
@ -13,9 +12,9 @@ from ..config import g_sDBFilePath, g_sDBPath
class CSqlManager: class CSqlManager:
def __init__(self): def __init__(self):
dbPath = Path(g_sDBPath) dbPath = Path(g_sDBPath)
if dbPath and not dbPath.exists(): if dbPath and not dbPath.exists():
os.makedirs(dbPath, exist_ok=True) os.makedirs(dbPath, exist_ok=True)
@classmethod @classmethod
async def cleanup(cls): async def cleanup(cls):
@ -46,7 +45,7 @@ class CSqlManager:
@classmethod @classmethod
async def getTableInfo(cls, tableName: str) -> list: 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}") raise ValueError(f"Illegal table name: {tableName}")
try: try:
cursor = await cls.m_pDB.execute(f'PRAGMA table_info("{tableName}")') cursor = await cls.m_pDB.execute(f'PRAGMA table_info("{tableName}")')
@ -70,7 +69,7 @@ class CSqlManager:
""" """
info = await cls.getTableInfo(tableName) 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"} desired = {k: v.upper() for k, v in columns.items() if k != "PRIMARY KEY"}
primaryKey = columns.get("PRIMARY KEY", "") primaryKey = columns.get("PRIMARY KEY", "")
@ -83,7 +82,9 @@ class CSqlManager:
toAdd = [k for k in desired if k not in existing] toAdd = [k for k in desired if k not in existing]
toRemove = [k for k in existing if k not in desired] 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: if toAdd and not toRemove and not typeMismatch:
for col in toAdd: for col in toAdd:
@ -106,7 +107,9 @@ class CSqlManager:
f'INSERT INTO "{tmpTable}" ({colsStr}) SELECT {colsStr} FROM "{tableName}";' f'INSERT INTO "{tmpTable}" ({colsStr}) SELECT {colsStr} FROM "{tableName}";'
) )
await cls.m_pDB.execute(f'DROP TABLE "{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 return True
@classmethod @classmethod
@ -131,4 +134,5 @@ class CSqlManager:
logger.warning(f"数据库语句执行出错: {command}", e=e) logger.warning(f"数据库语句执行出错: {command}", e=e)
return False return False
g_pSqlManager = CSqlManager() g_pSqlManager = CSqlManager()

View File

@ -1,8 +1,5 @@
import ast
import os import os
import re
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from unittest import result
import aiosqlite import aiosqlite
@ -29,7 +26,9 @@ class CPlantManager:
_ = os.path.exists(g_sPlantPath) _ = os.path.exists(g_sPlantPath)
if g_bIsDebug: 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: else:
cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath)) cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath))
@ -73,7 +72,6 @@ class CPlantManager:
logger.warning(f"数据库语句执行出错: {command}", e=e) logger.warning(f"数据库语句执行出错: {command}", e=e)
return False return False
@classmethod @classmethod
async def getPlantByName(cls, name: str) -> dict | None: async def getPlantByName(cls, name: str) -> dict | None:
"""根据作物名称查询记录 """根据作物名称查询记录
@ -113,7 +111,7 @@ class CPlantManager:
if not row: if not row:
return [] return []
phase = row[0].split(',') phase = row[0].split(",")
seen = set() seen = set()
result = [] result = []
@ -149,9 +147,9 @@ class CPlantManager:
if not row: if not row:
return -1 return -1
phase = row[0].split(',') phase = row[0].split(",")
#去重 # 去重
seen = set() seen = set()
result = [] result = []
for x in phase: for x in phase:
@ -184,7 +182,7 @@ class CPlantManager:
if not row: if not row:
return -1 return -1
phase = row[0].split(',') phase = row[0].split(",")
again = phase[-1] - phase[3] / 60 / 60 again = phase[-1] - phase[3] / 60 / 60
return again return again
@ -241,7 +239,9 @@ class CPlantManager:
async def listPlants(cls) -> list[dict]: async def listPlants(cls) -> list[dict]:
"""查询所有作物记录""" """查询所有作物记录"""
try: 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() rows = await cursor.fetchall()
return [dict(r) for r in rows] return [dict(r) for r in rows]
except Exception as e: except Exception as e:

View File

@ -1,6 +1,4 @@
import math import math
from typing import List, Union
from unittest import result
from zhenxun.services.log import logger from zhenxun.services.log import logger
@ -13,19 +11,21 @@ class CUserDB(CSqlManager):
async def initDB(cls): async def initDB(cls):
"""初始化用户表结构确保user表存在且字段完整""" """初始化用户表结构确保user表存在且字段完整"""
userInfo = { userInfo = {
"uid": "TEXT PRIMARY KEY", #用户Uid "uid": "TEXT PRIMARY KEY", # 用户Uid
"name": "TEXT NOT NULL", #农场名称 "name": "TEXT NOT NULL", # 农场名称
"exp": "INTEGER DEFAULT 0", #经验值 "exp": "INTEGER DEFAULT 0", # 经验值
"point": "INTEGER DEFAULT 0", #金币 "point": "INTEGER DEFAULT 0", # 金币
"vipPoint": "INTEGER DEFAULT 0", #点券 "vipPoint": "INTEGER DEFAULT 0", # 点券
"soil": "INTEGER DEFAULT 3", #解锁土地数量 "soil": "INTEGER DEFAULT 3", # 解锁土地数量
"stealTime": "TEXT DEFAULT ''", #偷菜时间字符串 "stealTime": "TEXT DEFAULT ''", # 偷菜时间字符串
"stealCount": "INTEGER DEFAULT 0" #剩余偷菜次数 "stealCount": "INTEGER DEFAULT 0", # 剩余偷菜次数
} }
await cls.ensureTableSchema("user", userInfo) await cls.ensureTableSchema("user", userInfo)
@classmethod @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: Args:
@ -37,7 +37,7 @@ class CUserDB(CSqlManager):
Returns: Returns:
Union[bool, str]: False 表示失败字符串表示成功信息 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 = ( sql = (
f"INSERT INTO user (uid, name, exp, point, soil, stealTime, stealCount) " f"INSERT INTO user (uid, name, exp, point, soil, stealTime, stealCount) "
f"VALUES ({uid}, '{name}', {exp}, {point}, 3, '{nowStr}', 5)" f"VALUES ({uid}, '{name}', {exp}, {point}, 3, '{nowStr}', 5)"
@ -51,7 +51,7 @@ class CUserDB(CSqlManager):
return False return False
@classmethod @classmethod
async def getAllUsers(cls) -> List[str]: async def getAllUsers(cls) -> list[str]:
"""获取所有用户UID列表 """获取所有用户UID列表
Returns: Returns:
@ -395,8 +395,7 @@ class CUserDB(CSqlManager):
return "" return ""
try: try:
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT stealTime FROM user WHERE uid = ?", (uid, "SELECT stealTime FROM user WHERE uid = ?", (uid,)
)
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] if row and row[0] else "" return row[0] if row and row[0] else ""
@ -451,11 +450,14 @@ class CUserDB(CSqlManager):
return -1 return -1
@classmethod @classmethod
async def updateStealCountByUid(cls, uid: str, stealCount: int) -> bool: async def updateStealCountByUid(
cls, uid: str, stealTime: str, stealCount: int
) -> bool:
"""根据用户Uid更新剩余偷菜次数 """根据用户Uid更新剩余偷菜次数
Args: Args:
uid (str): 用户Uid uid (str): 用户Uid
stealTime (str): 偷菜日期
stealCount (int): 新剩余偷菜次数 stealCount (int): 新剩余偷菜次数
Returns: Returns:
@ -467,7 +469,8 @@ class CUserDB(CSqlManager):
try: try:
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( 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 return True
except Exception as e: except Exception as e:

View File

@ -1,5 +1,3 @@
from typing import Optional
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager from .database import CSqlManager
@ -9,16 +7,16 @@ class CUserItemDB(CSqlManager):
@classmethod @classmethod
async def initDB(cls): async def initDB(cls):
userItem = { userItem = {
"uid": "TEXT NOT NULL", #用户Uid "uid": "TEXT NOT NULL", # 用户Uid
"item": "TEXT NOT NULL", #物品名称 "item": "TEXT NOT NULL", # 物品名称
"count": "INTEGER NOT NULL DEFAULT 0", #数量 "count": "INTEGER NOT NULL DEFAULT 0", # 数量
"PRIMARY KEY": "(uid, item)" "PRIMARY KEY": "(uid, item)",
} }
await cls.ensureTableSchema("userItem", userItem) await cls.ensureTableSchema("userItem", userItem)
@classmethod @classmethod
async def getUserItemByName(cls, uid: str, item: str) -> Optional[int]: async def getUserItemByName(cls, uid: str, item: str) -> int | None:
"""根据道具名称查询某一项数量 """根据道具名称查询某一项数量
Args: Args:
@ -32,13 +30,12 @@ class CUserItemDB(CSqlManager):
return None return None
try: try:
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT count FROM userItem WHERE uid = ? AND item = ?", "SELECT count FROM userItem WHERE uid = ? AND item = ?", (uid, item)
(uid, item)
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] if row else None return row[0] if row else None
except Exception as e: except Exception as e:
logger.warning(f"getUserItemByName查询失败", e=e) logger.warning("getUserItemByName查询失败", e=e)
return None return None
@classmethod @classmethod
@ -55,13 +52,12 @@ class CUserItemDB(CSqlManager):
return {} return {}
try: try:
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
"SELECT item, count FROM userItem WHERE uid = ?", "SELECT item, count FROM userItem WHERE uid = ?", (uid,)
(uid,)
) )
rows = await cursor.fetchall() rows = await cursor.fetchall()
return {row["item"]: row["count"] for row in rows} return {row["item"]: row["count"] for row in rows}
except Exception as e: except Exception as e:
logger.warning(f"getUserItemByUid查询失败", e=e) logger.warning("getUserItemByUid查询失败", e=e)
return {} return {}
@classmethod @classmethod
@ -80,12 +76,11 @@ class CUserItemDB(CSqlManager):
try: try:
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?", "DELETE FROM userItem WHERE uid = ? AND item = ?", (uid, item)
(uid, item)
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"deleteUserItemByName失败", e=e) logger.warning("deleteUserItemByName失败", e=e)
return False return False
@classmethod @classmethod
@ -106,17 +101,16 @@ class CUserItemDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
if count <= 0: if count <= 0:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?", "DELETE FROM userItem WHERE uid = ? AND item = ?", (uid, item)
(uid, item)
) )
else: else:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"UPDATE userItem SET count = ? WHERE uid = ? AND item = ?", "UPDATE userItem SET count = ? WHERE uid = ? AND item = ?",
(count, uid, item) (count, uid, item),
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"updateUserItemByName失败", e=e) logger.warning("updateUserItemByName失败", e=e)
return False return False
@classmethod @classmethod
@ -136,8 +130,7 @@ class CUserItemDB(CSqlManager):
try: try:
async with cls._transaction(): async with cls._transaction():
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT count FROM userItem WHERE uid = ? AND item = ?", "SELECT count FROM userItem WHERE uid = ? AND item = ?", (uid, item)
(uid, item)
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
@ -146,20 +139,20 @@ class CUserItemDB(CSqlManager):
if newCount <= 0: if newCount <= 0:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userItem WHERE uid = ? AND item = ?", "DELETE FROM userItem WHERE uid = ? AND item = ?",
(uid, item) (uid, item),
) )
else: else:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"UPDATE userItem SET count = ? WHERE uid = ? AND item = ?", "UPDATE userItem SET count = ? WHERE uid = ? AND item = ?",
(newCount, uid, item) (newCount, uid, item),
) )
else: else:
if count > 0: if count > 0:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"INSERT INTO userItem (uid, item, count) VALUES (?, ?, ?)", "INSERT INTO userItem (uid, item, count) VALUES (?, ?, ?)",
(uid, item, count) (uid, item, count),
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"addUserItemByUid失败", e=e) logger.warning("addUserItemByUid失败", e=e)
return False return False

View File

@ -1,5 +1,3 @@
from typing import Dict, Optional
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager from .database import CSqlManager
@ -9,10 +7,10 @@ class CUserPlantDB(CSqlManager):
@classmethod @classmethod
async def initDB(cls): async def initDB(cls):
userPlant = { userPlant = {
"uid": "TEXT NOT NULL", #用户Uid "uid": "TEXT NOT NULL", # 用户Uid
"plant": "TEXT NOT NULL", #作物名称 "plant": "TEXT NOT NULL", # 作物名称
"count": "INTEGER NOT NULL DEFAULT 0", #数量 "count": "INTEGER NOT NULL DEFAULT 0", # 数量
"PRIMARY KEY": "(uid, plant)" "PRIMARY KEY": "(uid, plant)",
} }
await cls.ensureTableSchema("userPlant", userPlant) await cls.ensureTableSchema("userPlant", userPlant)
@ -31,33 +29,33 @@ class CUserPlantDB(CSqlManager):
""" """
try: try:
async with cls._transaction(): async with cls._transaction():
#检查是否已存在该作物 # 检查是否已存在该作物
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT count FROM userPlant WHERE uid = ? AND plant = ?", "SELECT count FROM userPlant WHERE uid = ? AND plant = ?",
(uid, plant) (uid, plant),
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
if row: if row:
#如果作物已存在,则更新数量 # 如果作物已存在,则更新数量
new_count = row[0] + count new_count = row[0] + count
await cls.m_pDB.execute( await cls.m_pDB.execute(
"UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?", "UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?",
(new_count, uid, plant) (new_count, uid, plant),
) )
else: else:
#如果作物不存在,则插入新记录 # 如果作物不存在,则插入新记录
await cls.m_pDB.execute( await cls.m_pDB.execute(
"INSERT INTO userPlant (uid, plant, count) VALUES (?, ?, ?)", "INSERT INTO userPlant (uid, plant, count) VALUES (?, ?, ?)",
(uid, plant, count) (uid, plant, count),
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"addUserPlantByUid 失败!", e=e) logger.warning("addUserPlantByUid 失败!", e=e)
return False return False
@classmethod @classmethod
async def getUserPlantByUid(cls, uid: str) -> Dict[str, int]: async def getUserPlantByUid(cls, uid: str) -> dict[str, int]:
"""根据用户uid获取全部作物信息 """根据用户uid获取全部作物信息
Args: Args:
@ -67,14 +65,13 @@ class CUserPlantDB(CSqlManager):
Dict[str, int]: 作物名称和数量 Dict[str, int]: 作物名称和数量
""" """
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
"SELECT plant, count FROM userPlant WHERE uid=?", "SELECT plant, count FROM userPlant WHERE uid=?", (uid,)
(uid,)
) )
rows = await cursor.fetchall() rows = await cursor.fetchall()
return {row["plant"]: row["count"] for row in rows} return {row["plant"]: row["count"] for row in rows}
@classmethod @classmethod
async def getUserPlantByName(cls, uid: str, plant: str) -> Optional[int]: async def getUserPlantByName(cls, uid: str, plant: str) -> int | None:
"""根据作物名称获取用户的作物数量 """根据作物名称获取用户的作物数量
Args: Args:
@ -86,13 +83,12 @@ class CUserPlantDB(CSqlManager):
""" """
try: try:
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT count FROM userPlant WHERE uid = ? AND plant = ?", "SELECT count FROM userPlant WHERE uid = ? AND plant = ?", (uid, plant)
(uid, plant)
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] if row else None return row[0] if row else None
except Exception as e: except Exception as e:
logger.warning(f"getUserPlantByName 查询失败!", e=e) logger.warning("getUserPlantByName 查询失败!", e=e)
return None return None
@classmethod @classmethod
@ -114,11 +110,11 @@ class CUserPlantDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
"UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?", "UPDATE userPlant SET count = ? WHERE uid = ? AND plant = ?",
(count, uid, plant) (count, uid, plant),
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"updateUserPlantByName失败", e=e) logger.warning("updateUserPlantByName失败", e=e)
return False return False
@classmethod @classmethod
@ -135,10 +131,9 @@ class CUserPlantDB(CSqlManager):
try: try:
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userPlant WHERE uid = ? AND plant = ?", "DELETE FROM userPlant WHERE uid = ? AND plant = ?", (uid, plant)
(uid, plant)
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"deleteUserPlantByName 失败!", e=e) logger.warning("deleteUserPlantByName 失败!", e=e)
return False return False

View File

@ -1,5 +1,3 @@
from typing import Optional
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager from .database import CSqlManager
@ -9,10 +7,10 @@ class CUserSeedDB(CSqlManager):
@classmethod @classmethod
async def initDB(cls): async def initDB(cls):
userSeed = { userSeed = {
"uid": "TEXT NOT NULL", #用户Uid "uid": "TEXT NOT NULL", # 用户Uid
"seed": "TEXT NOT NULL", #种子名称 "seed": "TEXT NOT NULL", # 种子名称
"count": "INTEGER NOT NULL DEFAULT 0", #数量 "count": "INTEGER NOT NULL DEFAULT 0", # 数量
"PRIMARY KEY": "(uid, seed)" "PRIMARY KEY": "(uid, seed)",
} }
await cls.ensureTableSchema("userSeed", userSeed) await cls.ensureTableSchema("userSeed", userSeed)
@ -32,8 +30,7 @@ class CUserSeedDB(CSqlManager):
try: try:
async with cls._transaction(): async with cls._transaction():
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT count FROM userSeed WHERE uid = ? AND seed = ?", "SELECT count FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed)
(uid, seed)
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
@ -41,23 +38,22 @@ class CUserSeedDB(CSqlManager):
newCount = row[0] + count newCount = row[0] + count
await cls.m_pDB.execute( await cls.m_pDB.execute(
"UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?", "UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?",
(newCount, uid, seed) (newCount, uid, seed),
) )
else: else:
newCount = count newCount = count
await cls.m_pDB.execute( await cls.m_pDB.execute(
"INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)", "INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)",
(uid, seed, count) (uid, seed, count),
) )
if newCount <= 0: if newCount <= 0:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userSeed WHERE uid = ? AND seed = ?", "DELETE FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed)
(uid, seed)
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"addUserSeedByUid 失败!", e=e) logger.warning("addUserSeedByUid 失败!", e=e)
return False return False
@classmethod @classmethod
@ -72,7 +68,7 @@ class CUserSeedDB(CSqlManager):
else: else:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)", "INSERT INTO userSeed (uid, seed, count) VALUES (?, ?, ?)",
(uid, seed, newCount) (uid, seed, newCount),
) )
if newCount <= 0: if newCount <= 0:
@ -80,12 +76,11 @@ class CUserSeedDB(CSqlManager):
return True return True
except Exception as e: except Exception as e:
logger.warning(f"_addUserSeedByUid 失败!", e=e) logger.warning("_addUserSeedByUid 失败!", e=e)
return False return False
@classmethod @classmethod
async def getUserSeedByName(cls, uid: str, seed: str) -> Optional[int]: async def getUserSeedByName(cls, uid: str, seed: str) -> int | None:
"""根据种子名称获取种子数量 """根据种子名称获取种子数量
Args: Args:
@ -98,13 +93,12 @@ class CUserSeedDB(CSqlManager):
try: try:
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT count FROM userSeed WHERE uid = ? AND seed = ?", "SELECT count FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed)
(uid, seed)
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] if row else None return row[0] if row else None
except Exception as e: except Exception as e:
logger.warning(f"getUserSeedByName 查询失败!", e=e) logger.warning("getUserSeedByName 查询失败!", e=e)
return None return None
@classmethod @classmethod
@ -119,8 +113,7 @@ class CUserSeedDB(CSqlManager):
""" """
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
"SELECT seed, count FROM userSeed WHERE uid=?", "SELECT seed, count FROM userSeed WHERE uid=?", (uid,)
(uid,)
) )
rows = await cursor.fetchall() rows = await cursor.fetchall()
return {row["seed"]: row["count"] for row in rows} return {row["seed"]: row["count"] for row in rows}
@ -144,11 +137,11 @@ class CUserSeedDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
"UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?", "UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?",
(count, uid, seed) (count, uid, seed),
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"updateUserSeedByName失败", e=e) logger.warning("updateUserSeedByName失败", e=e)
return False return False
@classmethod @classmethod
@ -170,11 +163,11 @@ class CUserSeedDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
"UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?", "UPDATE userSeed SET count = ? WHERE uid = ? AND seed = ?",
(count, uid, seed) (count, uid, seed),
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"updateUserSeedByName失败", e=e) logger.warning("updateUserSeedByName失败", e=e)
return False return False
@classmethod @classmethod
@ -191,12 +184,11 @@ class CUserSeedDB(CSqlManager):
try: try:
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userSeed WHERE uid = ? AND seed = ?", "DELETE FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed)
(uid, seed)
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"deleteUserSeedByName 删除失败!", e=e) logger.warning("deleteUserSeedByName 删除失败!", e=e)
return False return False
@classmethod @classmethod
@ -212,10 +204,9 @@ class CUserSeedDB(CSqlManager):
""" """
try: try:
await cls.m_pDB.execute( await cls.m_pDB.execute(
"DELETE FROM userSeed WHERE uid = ? AND seed = ?", "DELETE FROM userSeed WHERE uid = ? AND seed = ?", (uid, seed)
(uid, seed)
) )
return True return True
except Exception as e: except Exception as e:
logger.warning(f"deleteUserSeedByName 删除失败!", e=e) logger.warning("deleteUserSeedByName 删除失败!", e=e)
return False return False

View File

@ -1,7 +1,6 @@
import calendar import calendar
import random import random
from datetime import date, datetime, timedelta from datetime import timedelta
from typing import Optional
from zhenxun.services.log import logger from zhenxun.services.log import logger
from zhenxun.utils._build_image import BuildImage from zhenxun.utils._build_image import BuildImage
@ -16,27 +15,27 @@ from .database import CSqlManager
class CUserSignDB(CSqlManager): class CUserSignDB(CSqlManager):
@classmethod @classmethod
async def initDB(cls): async def initDB(cls):
#userSignLog 表结构,每条为一次签到事件 # userSignLog 表结构,每条为一次签到事件
userSignLog = { userSignLog = {
"uid": "TEXT NOT NULL", #用户ID "uid": "TEXT NOT NULL", # 用户ID
"signDate": "DATE NOT NULL", #签到日期 "signDate": "DATE NOT NULL", # 签到日期
"isSupplement": "TINYINT NOT NULL DEFAULT 0", #是否补签 "isSupplement": "TINYINT NOT NULL DEFAULT 0", # 是否补签
"exp": "INT NOT NULL DEFAULT 0", #当天签到经验 "exp": "INT NOT NULL DEFAULT 0", # 当天签到经验
"point": "INT NOT NULL DEFAULT 0", #当天签到金币 "point": "INT NOT NULL DEFAULT 0", # 当天签到金币
"createdAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))",#创建时间 "createdAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))", # 创建时间
"PRIMARY KEY": "(uid, signDate)" "PRIMARY KEY": "(uid, signDate)",
} }
#userSignSummary 表结构,每用户一行用于缓存签到状态 # userSignSummary 表结构,每用户一行用于缓存签到状态
userSignSummary = { userSignSummary = {
"uid": "TEXT PRIMARY KEY NOT NULL", #用户ID "uid": "TEXT PRIMARY KEY NOT NULL", # 用户ID
"totalSignDays": "INT NOT NULL DEFAULT 0", #累计签到天数 "totalSignDays": "INT NOT NULL DEFAULT 0", # 累计签到天数
"currentMonth": "CHAR(7) NOT NULL DEFAULT ''", #当前月份如2025-05 "currentMonth": "CHAR(7) NOT NULL DEFAULT ''", # 当前月份如2025-05
"monthSignDays": "INT NOT NULL DEFAULT 0", #本月签到次数 "monthSignDays": "INT NOT NULL DEFAULT 0", # 本月签到次数
"lastSignDate": "DATE DEFAULT NULL", #上次签到日期 "lastSignDate": "DATE DEFAULT NULL", # 上次签到日期
"continuousDays": "INT NOT NULL DEFAULT 0", #连续签到天数 "continuousDays": "INT NOT NULL DEFAULT 0", # 连续签到天数
"supplementCount": "INT NOT NULL DEFAULT 0", #补签次数 "supplementCount": "INT NOT NULL DEFAULT 0", # 补签次数
"updatedAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))"#更新时间 "updatedAt": "DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime'))", # 更新时间
} }
await cls.ensureTableSchema("userSignLog", userSignLog) await cls.ensureTableSchema("userSignLog", userSignLog)
@ -57,15 +56,15 @@ class CUserSignDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
async with cls.m_pDB.execute( async with cls.m_pDB.execute(
"SELECT exp, point FROM userSignLog WHERE uid=? AND signDate=?", "SELECT exp, point FROM userSignLog WHERE uid=? AND signDate=?",
(uid, date) (uid, date),
) as cursor: ) as cursor:
row = await cursor.fetchone() row = await cursor.fetchone()
if row is None: if row is None:
return 0, 0 return 0, 0
exp = row['exp'] exp = row["exp"]
point = row['point'] point = row["point"]
return exp, point return exp, point
except Exception as e: except Exception as e:
@ -114,7 +113,7 @@ class CUserSignDB(CSqlManager):
return False return False
@classmethod @classmethod
async def sign(cls, uid: str, signDate: str = '') -> int: async def sign(cls, uid: str, signDate: str = "") -> int:
"""签到 """签到
Args: Args:
@ -151,20 +150,35 @@ class CUserSignDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
"INSERT INTO userSignLog (uid, signDate, isSupplement, exp, point) VALUES (?, ?, ?, ?, ?)", "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() row = await cursor.fetchone()
currentMonth = signDate[:7] currentMonth = signDate[:7]
if row: if row:
monthSignDays = row['monthSignDays'] + 1 if row['currentMonth'] == currentMonth else 1 monthSignDays = (
totalSignDays = row['totalSignDays'] row["monthSignDays"] + 1
lastDate = row['lastSignDate'] if row["currentMonth"] == currentMonth
prevDate = (g_pToolManager.dateTime().strptime(signDate, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d") else 1
continuousDays = row['continuousDays'] + 1 if lastDate == prevDate else 1 )
supplementCount = row['supplementCount'] + 1 if isSupplement else row['supplementCount'] 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( await cls.m_pDB.execute(
""" """
UPDATE userSignSummary UPDATE userSignSummary
@ -176,7 +190,14 @@ class CUserSignDB(CSqlManager):
supplementCount=? supplementCount=?
WHERE uid=? WHERE uid=?
""", """,
(currentMonth, monthSignDays, signDate, continuousDays, supplementCount, uid) (
currentMonth,
monthSignDays,
signDate,
continuousDays,
supplementCount,
uid,
),
) )
else: else:
totalSignDays = 1 totalSignDays = 1
@ -186,18 +207,26 @@ class CUserSignDB(CSqlManager):
(uid, totalSignDays, currentMonth, monthSignDays, lastSignDate, continuousDays, supplementCount) (uid, totalSignDays, currentMonth, monthSignDays, lastSignDate, continuousDays, supplementCount)
VALUES (?, ?, ?, ?, ?, ?, ?) 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: if reward:
point += reward.get('point', 0) point += reward.get("point", 0)
exp += reward.get('exp', 0) exp += reward.get("exp", 0)
vipPoint = reward.get('vipPoint', 0) vipPoint = reward.get("vipPoint", 0)
plant = reward.get('plant', {}) plant = reward.get("plant", {})
if plant: if plant:
for key, value in plant.items(): for key, value in plant.items():
@ -206,7 +235,7 @@ class CUserSignDB(CSqlManager):
if g_bIsDebug: if g_bIsDebug:
exp += 9999 exp += 9999
#向数据库更新 # 向数据库更新
currentExp = await g_pDBService.user.getUserExpByUid(uid) currentExp = await g_pDBService.user.getUserExpByUid(uid)
await g_pDBService.user.updateUserExpByUid(uid, currentExp + exp) await g_pDBService.user.updateUserExpByUid(uid, currentExp + exp)
@ -215,7 +244,9 @@ class CUserSignDB(CSqlManager):
if vipPoint > 0: if vipPoint > 0:
currentVipPoint = await g_pDBService.user.getUserVipPointByUid(uid) 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 return 1
except Exception as e: except Exception as e:
@ -224,7 +255,7 @@ class CUserSignDB(CSqlManager):
@classmethod @classmethod
async def drawSignCalendarImage(cls, uid: str, year: int, month: int): async def drawSignCalendarImage(cls, uid: str, year: int, month: int):
#绘制签到图,自动提取数据库中该用户该月的签到天数 # 绘制签到图,自动提取数据库中该用户该月的签到天数
cellSize = 80 cellSize = 80
padding = 40 padding = 40
titleHeight = 80 titleHeight = 80

View File

@ -1,10 +1,7 @@
from typing import Optional
from zhenxun.services.log import logger from zhenxun.services.log import logger
from ..config import g_bIsDebug from ..config import g_bIsDebug
from ..dbService import g_pDBService from ..dbService import g_pDBService
from ..json import g_pJsonManager
from ..tool import g_pToolManager from ..tool import g_pToolManager
from .database import CSqlManager from .database import CSqlManager
@ -14,17 +11,17 @@ class CUserSoilDB(CSqlManager):
async def initDB(cls): async def initDB(cls):
userSoil = { userSoil = {
"uid": "TEXT NOT NULL", "uid": "TEXT NOT NULL",
"soilIndex": "INTEGER NOT NULL", #地块索引从1开始 "soilIndex": "INTEGER NOT NULL", # 地块索引从1开始
"plantName": "TEXT DEFAULT ''", #作物名称 "plantName": "TEXT DEFAULT ''", # 作物名称
"plantTime": "INTEGER DEFAULT 0", #播种时间 "plantTime": "INTEGER DEFAULT 0", # 播种时间
"matureTime": "INTEGER DEFAULT 0", #成熟时间 "matureTime": "INTEGER DEFAULT 0", # 成熟时间
"soilLevel": "INTEGER DEFAULT 0", #土地等级 0=普通地1=红土地2=黑土地3=金土地 "soilLevel": "INTEGER DEFAULT 0", # 土地等级 0=普通地1=红土地2=黑土地3=金土地
"wiltStatus": "INTEGER DEFAULT 0", #枯萎状态 0=未枯萎1=枯萎 "wiltStatus": "INTEGER DEFAULT 0", # 枯萎状态 0=未枯萎1=枯萎
"fertilizerStatus": "INTEGER DEFAULT 0",#施肥状态 0=未施肥1=施肥 2=增肥 "fertilizerStatus": "INTEGER DEFAULT 0", # 施肥状态 0=未施肥1=施肥 2=增肥
"bugStatus": "INTEGER DEFAULT 0", #虫害状态 0=无虫害1=有虫害 "bugStatus": "INTEGER DEFAULT 0", # 虫害状态 0=无虫害1=有虫害
"weedStatus": "INTEGER DEFAULT 0", #杂草状态 0=无杂草1=有杂草 "weedStatus": "INTEGER DEFAULT 0", # 杂草状态 0=无杂草1=有杂草
"waterStatus": "INTEGER DEFAULT 0", #缺水状态 0=不缺水1=缺水 "waterStatus": "INTEGER DEFAULT 0", # 缺水状态 0=不缺水1=缺水
"harvestCount": "INTEGER DEFAULT 0", #收获次数 "harvestCount": "INTEGER DEFAULT 0", # 收获次数
"PRIMARY KEY": "(uid, soilIndex)", "PRIMARY KEY": "(uid, soilIndex)",
} }
@ -45,30 +42,30 @@ class CUserSoilDB(CSqlManager):
if not soilInfo: if not soilInfo:
return return
plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName']) plantInfo = await g_pDBService.plant.getPlantByName(soilInfo["plantName"])
if not plantInfo: if not plantInfo:
return return
currentTime = g_pToolManager.dateTime().now().timestamp() 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 return
elapsedTime = currentTime - soilInfo['plantTime'] elapsedTime = currentTime - soilInfo["plantTime"]
currentStage = currentStage = sum(1 for thr in phaseList if elapsedTime >= thr) currentStage = currentStage = sum(1 for thr in phaseList if elapsedTime >= thr)
t = int(soilInfo['plantTime']) - phaseList[currentStage] t = int(soilInfo["plantTime"]) - phaseList[currentStage]
s = int(soilInfo['matureTime']) - phaseList[currentStage] s = int(soilInfo["matureTime"]) - phaseList[currentStage]
await cls.updateUserSoilFields(uid, soilIndex, await cls.updateUserSoilFields(
{ uid, soilIndex, {"plantTime": t, "matureTime": s}
"plantTime": t, )
"matureTime": s
})
logger.debug(f"当前阶段{currentStage}, 阶段时间{phaseList[currentStage]}, 播种时间{t}, 收获时间{s}") logger.debug(
f"当前阶段{currentStage}, 阶段时间{phaseList[currentStage]}, 播种时间{t}, 收获时间{s}"
)
@classmethod @classmethod
async def getUserFarmByUid(cls, uid: str) -> dict: async def getUserFarmByUid(cls, uid: str) -> dict:
@ -95,7 +92,7 @@ class CUserSoilDB(CSqlManager):
Returns: Returns:
bool: 如果旧表不存在则返回 False否则迁移并删除后返回 True bool: 如果旧表不存在则返回 False否则迁移并删除后返回 True
""" """
#检查旧表是否存在 # 检查旧表是否存在
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name='soil'" "SELECT name FROM sqlite_master WHERE type='table' AND name='soil'"
) )
@ -184,31 +181,31 @@ class CUserSoilDB(CSqlManager):
None None
""" """
await cls.m_pDB.execute( await cls.m_pDB.execute(
""" """
INSERT INTO userSoil INSERT INTO userSoil
(uid, soilIndex, plantName, plantTime, matureTime, (uid, soilIndex, plantName, plantTime, matureTime,
soilLevel, wiltStatus, fertilizerStatus, bugStatus, soilLevel, wiltStatus, fertilizerStatus, bugStatus,
weedStatus, waterStatus, harvestCount) weedStatus, waterStatus, harvestCount)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
""", """,
( (
soilInfo["uid"], soilInfo["uid"],
soilInfo["soilIndex"], soilInfo["soilIndex"],
soilInfo.get("plantName", ""), soilInfo.get("plantName", ""),
soilInfo.get("plantTime", 0), soilInfo.get("plantTime", 0),
soilInfo.get("matureTime", 0), soilInfo.get("matureTime", 0),
soilInfo.get("soilLevel", 0), soilInfo.get("soilLevel", 0),
soilInfo.get("wiltStatus", 0), soilInfo.get("wiltStatus", 0),
soilInfo.get("fertilizerStatus", 0), soilInfo.get("fertilizerStatus", 0),
soilInfo.get("bugStatus", 0), soilInfo.get("bugStatus", 0),
soilInfo.get("weedStatus", 0), soilInfo.get("weedStatus", 0),
soilInfo.get("waterStatus", 0), soilInfo.get("waterStatus", 0),
soilInfo.get("harvestCount", 0), soilInfo.get("harvestCount", 0),
), ),
) )
@classmethod @classmethod
async def getUserSoil(cls, uid: str, soilIndex: int) -> Optional[dict]: async def getUserSoil(cls, uid: str, soilIndex: int) -> dict | None:
"""获取指定用户某块土地的详细信息 """获取指定用户某块土地的详细信息
Args: Args:
@ -230,7 +227,7 @@ class CUserSoilDB(CSqlManager):
return dict(zip(columns, row)) return dict(zip(columns, row))
@classmethod @classmethod
async def _getUserSoil(cls, uid: str, soilIndex: int) -> Optional[dict]: async def _getUserSoil(cls, uid: str, soilIndex: int) -> dict | None:
"""获取指定用户某块土地的详细信息 """获取指定用户某块土地的详细信息
Args: Args:
@ -288,7 +285,9 @@ class CUserSoilDB(CSqlManager):
) )
@classmethod @classmethod
async def updateUserSoilFields(cls, uid: str, soilIndex: int, updates: dict) -> bool: async def updateUserSoilFields(
cls, uid: str, soilIndex: int, updates: dict
) -> bool:
"""批量更新指定用户土地的多个字段 """批量更新指定用户土地的多个字段
Args: Args:
@ -299,11 +298,18 @@ class CUserSoilDB(CSqlManager):
Returns: Returns:
bool: 如果无可更新字段则返回 False否则更新成功返回 True bool: 如果无可更新字段则返回 False否则更新成功返回 True
""" """
#允许更新的列白名单 # 允许更新的列白名单
allowedFields = { allowedFields = {
"plantName", "plantTime", "matureTime", "soilLevel", "plantName",
"wiltStatus", "fertilizerStatus", "bugStatus", "plantTime",
"weedStatus", "waterStatus", "harvestCount" "matureTime",
"soilLevel",
"wiltStatus",
"fertilizerStatus",
"bugStatus",
"weedStatus",
"waterStatus",
"harvestCount",
} }
setClauses = [] setClauses = []
values = [] values = []
@ -316,7 +322,7 @@ class CUserSoilDB(CSqlManager):
return False return False
values.extend([uid, soilIndex]) 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: try:
async with cls._transaction(): async with cls._transaction():
@ -386,12 +392,12 @@ class CUserSoilDB(CSqlManager):
Returns: Returns:
bool: 播种成功返回 True否则返回 False bool: 播种成功返回 True否则返回 False
""" """
#校验土地区是否已种植 # 校验土地区是否已种植
soilRecord = await cls.getUserSoil(uid, soilIndex) soilRecord = await cls.getUserSoil(uid, soilIndex)
if soilRecord and soilRecord.get("plantName"): if soilRecord and soilRecord.get("plantName"):
return False return False
#获取植物配置 # 获取植物配置
plantCfg = await g_pDBService.plant.getPlantByName(plantName) plantCfg = await g_pDBService.plant.getPlantByName(plantName)
if not plantCfg: if not plantCfg:
logger.error(f"未知植物: {plantName}") logger.error(f"未知植物: {plantName}")
@ -417,12 +423,12 @@ class CUserSoilDB(CSqlManager):
"bugStatus": prev.get("bugStatus", 0), "bugStatus": prev.get("bugStatus", 0),
"weedStatus": prev.get("weedStatus", 0), "weedStatus": prev.get("weedStatus", 0),
"waterStatus": prev.get("waterStatus", 0), "waterStatus": prev.get("waterStatus", 0),
"harvestCount": 0 "harvestCount": 0,
} }
) )
return True return True
except Exception as e: except Exception as e:
logger.error(f"播种失败!", e=e) logger.error("播种失败!", e=e)
return False return False
@classmethod @classmethod

View File

@ -7,17 +7,19 @@ class CUserStealDB(CSqlManager):
@classmethod @classmethod
async def initDB(cls): async def initDB(cls):
userSteal = { userSteal = {
"uid": "TEXT NOT NULL", #被偷用户Uid "uid": "TEXT NOT NULL", # 被偷用户Uid
"soilIndex": "INTEGER NOT NULL", #被偷的地块索引 从1开始 "soilIndex": "INTEGER NOT NULL", # 被偷的地块索引 从1开始
"stealerUid": "TEXT NOT NULL", #偷菜用户Uid "stealerUid": "TEXT NOT NULL", # 偷菜用户Uid
"stealCount": "INTEGER NOT NULL", #被偷数量 "stealCount": "INTEGER NOT NULL", # 被偷数量
"stealTime": "INTEGER NOT NULL", #被偷时间 "stealTime": "INTEGER NOT NULL", # 被偷时间
"PRIMARY KEY": "(uid, soilIndex, stealerUid)" "PRIMARY KEY": "(uid, soilIndex, stealerUid)",
} }
await cls.ensureTableSchema("userSteal", userSteal) await cls.ensureTableSchema("userSteal", userSteal)
@classmethod @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: Args:
@ -34,7 +36,7 @@ class CUserStealDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
'INSERT INTO "userSteal"(uid, soilIndex, stealerUid, stealCount, stealTime) VALUES(?, ?, ?, ?, ?);', 'INSERT INTO "userSteal"(uid, soilIndex, stealerUid, stealCount, stealTime) VALUES(?, ?, ?, ?, ?);',
(uid, soilIndex, stealerUid, stealCount, stealTime) (uid, soilIndex, stealerUid, stealCount, stealTime),
) )
return True return True
except Exception as e: except Exception as e:
@ -55,7 +57,7 @@ class CUserStealDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
'SELECT soilIndex, stealerUid, stealCount, stealTime FROM "userSteal" WHERE uid=?;', 'SELECT soilIndex, stealerUid, stealCount, stealTime FROM "userSteal" WHERE uid=?;',
(uid,) (uid,),
) )
rows = await cursor.fetchall() rows = await cursor.fetchall()
return [ return [
@ -64,7 +66,7 @@ class CUserStealDB(CSqlManager):
"soilIndex": row[0], "soilIndex": row[0],
"stealerUid": row[1], "stealerUid": row[1],
"stealCount": row[2], "stealCount": row[2],
"stealTime": row[3] "stealTime": row[3],
} }
for row in rows for row in rows
] ]
@ -87,7 +89,7 @@ class CUserStealDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
'SELECT stealerUid, stealCount, stealTime FROM "userSteal" WHERE uid=? AND soilIndex=?;', 'SELECT stealerUid, stealCount, stealTime FROM "userSteal" WHERE uid=? AND soilIndex=?;',
(uid, soilIndex) (uid, soilIndex),
) )
rows = await cursor.fetchall() rows = await cursor.fetchall()
return [ return [
@ -96,7 +98,7 @@ class CUserStealDB(CSqlManager):
"soilIndex": soilIndex, "soilIndex": soilIndex,
"stealerUid": row[0], "stealerUid": row[0],
"stealCount": row[1], "stealCount": row[1],
"stealTime": row[2] "stealTime": row[2],
} }
for row in rows for row in rows
] ]
@ -119,10 +121,10 @@ class CUserStealDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
'SELECT SUM(stealCount) FROM "userSteal" WHERE uid=? AND soilIndex=?;', 'SELECT SUM(stealCount) FROM "userSteal" WHERE uid=? AND soilIndex=?;',
(uid, soilIndex) (uid, soilIndex),
) )
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] or 0 # type: ignore return row[0] or 0 # type: ignore
except Exception as e: except Exception as e:
logger.warning("计算总偷菜数量失败", e=e) logger.warning("计算总偷菜数量失败", e=e)
return 0 return 0
@ -142,10 +144,10 @@ class CUserStealDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
'SELECT COUNT(DISTINCT stealerUid) FROM "userSteal" WHERE uid=? AND soilIndex=?;', 'SELECT COUNT(DISTINCT stealerUid) FROM "userSteal" WHERE uid=? AND soilIndex=?;',
(uid, soilIndex) (uid, soilIndex),
) )
row = await cursor.fetchone() row = await cursor.fetchone()
return row[0] or 0 # type: ignore return row[0] or 0 # type: ignore
except Exception as e: except Exception as e:
logger.warning("计算偷菜者数量失败", e=e) logger.warning("计算偷菜者数量失败", e=e)
return 0 return 0
@ -166,7 +168,7 @@ class CUserStealDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
cursor = await cls.m_pDB.execute( cursor = await cls.m_pDB.execute(
'SELECT 1 FROM "userSteal" WHERE uid=? AND soilIndex=? AND stealerUid=? LIMIT 1;', 'SELECT 1 FROM "userSteal" WHERE uid=? AND soilIndex=? AND stealerUid=? LIMIT 1;',
(uid, soilIndex, stealerUid) (uid, soilIndex, stealerUid),
) )
row = await cursor.fetchone() row = await cursor.fetchone()
return bool(row) return bool(row)
@ -175,7 +177,9 @@ class CUserStealDB(CSqlManager):
return False return False
@classmethod @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: Args:
@ -192,7 +196,7 @@ class CUserStealDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
'UPDATE "userSteal" SET stealCount=?, stealTime=? WHERE uid=? AND soilIndex=? AND stealerUid=?;', 'UPDATE "userSteal" SET stealCount=?, stealTime=? WHERE uid=? AND soilIndex=? AND stealerUid=?;',
(stealCount, stealTime, uid, soilIndex, stealerUid) (stealCount, stealTime, uid, soilIndex, stealerUid),
) )
return True return True
except Exception as e: except Exception as e:
@ -214,7 +218,7 @@ class CUserStealDB(CSqlManager):
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
'DELETE FROM "userSteal" WHERE uid=? AND soilIndex=?;', 'DELETE FROM "userSteal" WHERE uid=? AND soilIndex=?;',
(uid, soilIndex) (uid, soilIndex),
) )
return True return True
except Exception as e: except Exception as e:

View File

@ -1,6 +1,3 @@
from typing import Optional
class CDBService: class CDBService:
@classmethod @classmethod
async def init(cls): async def init(cls):
@ -37,11 +34,12 @@ class CDBService:
cls.userSign = CUserSignDB() cls.userSign = CUserSignDB()
await cls.userSign.initDB() await cls.userSign.initDB()
#迁移旧数据库 # 迁移旧数据库
await cls.userSoil.migrateOldFarmData() await cls.userSoil.migrateOldFarmData()
@classmethod @classmethod
async def cleanup(cls): async def cleanup(cls):
await cls.plant.cleanup() await cls.plant.cleanup()
g_pDBService = CDBService() g_pDBService = CDBService()

View File

@ -6,8 +6,8 @@ from zhenxun.services.log import logger
class Signal: class Signal:
def __init__(self): def __init__(self):
self._slots = [] #绑定的槽函数列表 self._slots = [] # 绑定的槽函数列表
self._onceSlots = [] #只触发一次的槽函数列表 self._onceSlots = [] # 只触发一次的槽函数列表
def connect(self, slot, priority=0): def connect(self, slot, priority=0):
if callable(slot) and not any(s[0] == slot for s in self._slots): 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}") logger.warning(f"事件槽 {slot.__name__} 触发异常: {e}")
class FarmEventManager: class FarmEventManager:
def __init__(self): def __init__(self):
self.m_beforePlant = Signal() self.m_beforePlant = Signal()
"""播种前信号 """播种前信号
@ -100,4 +98,5 @@ class FarmEventManager:
self.m_beforeSteal = Signal() self.m_beforeSteal = Signal()
self.m_afterSteal = Signal() self.m_afterSteal = Signal()
g_pEventManager = FarmEventManager() g_pEventManager = FarmEventManager()

View File

@ -1,7 +1,5 @@
import asyncio
import math import math
import random import random
from typing import Dict, List, Tuple
from zhenxun.configs.config import Config from zhenxun.configs.config import Config
from zhenxun.models.user_console import UserConsole 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.image_utils import ImageTemplate
from zhenxun.utils.platform import PlatformUtils 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 ..dbService import g_pDBService
from ..event.event import g_pEventManager from ..event.event import g_pEventManager
from ..json import g_pJsonManager from ..json import g_pJsonManager
@ -29,16 +27,20 @@ class CFarmManager:
pro = float(Config.get_config("zhenxun_plugin_farm", "兑换倍数")) pro = float(Config.get_config("zhenxun_plugin_farm", "兑换倍数"))
tax = float(Config.get_config("zhenxun_plugin_farm", "手续费")) tax = float(Config.get_config("zhenxun_plugin_farm", "手续费"))
#计算手续费 # 计算手续费
fee = math.floor(num * tax) fee = math.floor(num * tax)
#实际扣费金额 # 实际扣费金额
deduction = num + fee deduction = num + fee
if user.gold < deduction: if user.gold < deduction:
return f"你的金币不足或不足承担手续费。当前手续费为{fee}" return f"你的金币不足或不足承担手续费。当前手续费为{fee}"
await UserConsole.reduce_gold(uid, num, GoldHandle.PLUGIN , 'zhenxun_plugin_farm') # type: ignore await UserConsole.reduce_gold(
await UserConsole.reduce_gold(uid, fee, GoldHandle.PLUGIN , 'zhenxun_plugin_farm') # type: ignore 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 point = num * pro
@ -59,48 +61,59 @@ class CFarmManager:
Returns: Returns:
bytes: 返回绘制结果 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 缺少判断用户土地资源状况 # TODO 缺少判断用户土地资源状况
soil = BuildImage(background = g_sResourcePath / "soil/普通土地.png") soil = BuildImage(background=g_sResourcePath / "soil/普通土地.png")
await soil.resize(0, soilSize[0], soilSize[1]) 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]) 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) userInfo = await g_pDBService.user.getUserInfoByUid(uid)
soilUnlock = int(userInfo['soil']) soilUnlock = int(userInfo["soil"])
x = 0 x = 0
y = 0 y = 0
isFirstExpansion = True #首次添加扩建图片 isFirstExpansion = True # 首次添加扩建图片
isFirstRipe = True isFirstRipe = True
plant = None plant = None
for index in range(0, 30): for index in range(0, 30):
x = soilPos[str(index + 1)]['x'] x = soilPos[str(index + 1)]["x"]
y = soilPos[str(index + 1)]['y'] y = soilPos[str(index + 1)]["y"]
#如果土地已经到达对应等级 # 如果土地已经到达对应等级
if index < soilUnlock: if index < soilUnlock:
await img.paste(soil, (x, y)) 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: if isPlant:
await img.paste(plant, (x + soilSize[0] // 2 - plant.width // 2 + offsetX, await img.paste(
y + soilSize[1] // 2 - plant.height // 2 + offsetY)) 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: 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, await img.paste(
y - ripe.height // 2)) ripe,
(x + soilSize[0] // 2 - ripe.width // 2, y - ripe.height // 2),
)
isFirstRipe = False isFirstRipe = False
else: else:
@ -109,54 +122,71 @@ class CFarmManager:
if isFirstExpansion: if isFirstExpansion:
isFirstExpansion = False 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 expansion.resize(0, 69, 69)
await img.paste(expansion, (x + soilSize[0] // 2 - expansion.width // 2, await img.paste(
y + soilSize[1] // 2 - expansion.height)) expansion,
(
x + soilSize[0] // 2 - expansion.width // 2,
y + soilSize[1] // 2 - expansion.height,
),
)
#左上角绘制用户信息 # 左上角绘制用户信息
#头像 # 头像
image = await PlatformUtils.get_user_avatar(uid, "qq") image = await PlatformUtils.get_user_avatar(uid, "qq")
if image: if image:
avatar = BuildImage(background = image) avatar = BuildImage(background=image)
await img.paste(avatar, (125, 85)) 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)) 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)) await img.paste(nameImg, (300, 92))
#经验值 # 经验值
level = await g_pDBService.user.getUserLevelByUid(uid) level = await g_pDBService.user.getUserLevelByUid(uid)
beginX = 309 beginX = 309
endX = 627 endX = 627
#绘制宽度计算公式为 (当前经验值 / 经验值上限) * 宽度 # 绘制宽度计算公式为 (当前经验值 / 经验值上限) * 宽度
width = int((level[2] / level[1]) * (endX - beginX)) width = int((level[2] / level[1]) * (endX - beginX))
await img.rectangle((beginX, 188, beginX + width, 222), (171, 194, 41)) 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)) 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)) 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)) await img.paste(pointImg, (330, 255))
#点券 TODO # 点券 TODO
bondsImg = await BuildImage.build_text_image("0", size = 24, font_color = (253, 253, 253)) bondsImg = await BuildImage.build_text_image(
"0", size=24, font_color=(253, 253, 253)
)
await img.paste(bondsImg, (570, 255)) await img.paste(bondsImg, (570, 255))
#清晰度 # 清晰度
definition = Config.get_config("zhenxun_plugin_farm", "绘制农场清晰度") definition = Config.get_config("zhenxun_plugin_farm", "绘制农场清晰度")
if definition == "medium": if definition == "medium":
await img.resize(0.6) await img.resize(0.6)
@ -196,9 +226,9 @@ class CFarmManager:
if soilInfo: if soilInfo:
if soilInfo["soilLevel"] == 1: if soilInfo["soilLevel"] == 1:
iconPath = g_sResourcePath / f"soil/TODO.png" iconPath = g_sResourcePath / "soil/TODO.png"
else: else:
iconPath = g_sResourcePath / f"soil/普通土地.png" iconPath = g_sResourcePath / "soil/普通土地.png"
if iconPath.exists(): if iconPath.exists():
icon = (iconPath, 33, 33) icon = (iconPath, 33, 33)
@ -211,27 +241,34 @@ class CFarmManager:
totalNumber = "-" totalNumber = "-"
plantNumber = "-" plantNumber = "-"
else: 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) 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) planInfo = await g_pDBService.plant.getPlantByName(plantName)
if not planInfo: if not planInfo:
plantNumber = f"None" plantNumber = "None"
else: else:
plantNumber = f"{planInfo['harvest'] - totalNumber}" plantNumber = f"{planInfo['harvest'] - totalNumber}"
dataList.append( dataList.append(
[ [
icon, icon,
i, i,
plantName, plantName,
matureTime, matureTime,
soilStatus, soilStatus,
totalNumber, totalNumber,
plantNumber, plantNumber,
]) ]
)
if len(dataList) >= 15: if len(dataList) >= 15:
result = await ImageTemplate.table_page( result = await ImageTemplate.table_page(
@ -258,7 +295,9 @@ class CFarmManager:
return info return info
@classmethod @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: Args:
@ -273,54 +312,64 @@ class CFarmManager:
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex) soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
if not soilInfo: 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: 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) await plant.resize(0, 150, 212)
return True, plant, False, 0, 0 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: if not plantInfo:
logger.error(f"绘制植物资源失败: {soilInfo['plantName']}") 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) offsetX = plantInfo.get("officX", 0)
offsetY = plantInfo.get('officY', 0) offsetY = plantInfo.get("officY", 0)
offsetW = plantInfo.get('officW', 0) offsetW = plantInfo.get("officW", 0)
offsetH = plantInfo.get('officH', 0) offsetH = plantInfo.get("officH", 0)
currentTime = g_pToolManager.dateTime().now().timestamp() 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"]:
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{len(phaseList)}.png") plant = BuildImage(
background=g_sResourcePath
/ f"plant/{soilInfo['plantName']}/{len(phaseList)}.png"
)
return True, plant, True, offsetX, offsetY return True, plant, True, offsetX, offsetY
else: else:
#如果是多阶段作物 且没有成熟 #早期思路 多阶段作物 直接是倒数第二阶段图片 # 如果是多阶段作物 且没有成熟 #早期思路 多阶段作物 直接是倒数第二阶段图片
# if soilInfo['harvestCount'] >= 1: # if soilInfo['harvestCount'] >= 1:
# plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{plantInfo['phase'] - 1s}.png") # plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{plantInfo['phase'] - 1s}.png")
# return True, plant, False, offsetX, offsetY # return True, plant, False, offsetX, offsetY
#如果没有成熟 则根据当前阶段进行绘制 # 如果没有成熟 则根据当前阶段进行绘制
elapsedTime = currentTime - soilInfo["plantTime"]
elapsedTime = currentTime - soilInfo['plantTime'] currentStage = currentStage = sum(
currentStage = sum(1 for thr in phaseList if elapsedTime >= thr) 1 for thr in phaseList if elapsedTime >= thr
)
if currentStage <= 0: if currentStage <= 0:
if plantInfo['general'] == False: if not plantInfo["general"]:
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/0.png") plant = BuildImage(
background=g_sResourcePath
/ f"plant/{soilInfo['plantName']}/0.png"
)
else: 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) await plant.resize(0, 35 + offsetW, 58 + offsetH)
else: 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 return True, plant, False, offsetX, offsetY
@ -336,10 +385,10 @@ class CFarmManager:
"收获数量", "收获数量",
"成熟时间(小时)", "成熟时间(小时)",
"收获次数", "收获次数",
"是否可以上架交易行" "是否可以上架交易行",
] ]
#从数据库获取结构化数据 # 从数据库获取结构化数据
seedRecords = await g_pDBService.userSeed.getUserSeedByUid(uid) or {} seedRecords = await g_pDBService.userSeed.getUserSeedByUid(uid) or {}
if not seedRecords: if not seedRecords:
@ -359,18 +408,20 @@ class CFarmManager:
iconPath = g_sResourcePath / f"plant/{seedName}/icon.png" iconPath = g_sResourcePath / f"plant/{seedName}/icon.png"
icon = (iconPath, 33, 33) if iconPath.exists() else "" icon = (iconPath, 33, 33) if iconPath.exists() else ""
sellable = "可以" if plantInfo['sell'] else "不可以" sellable = "可以" if plantInfo["sell"] else "不可以"
dataList.append([ dataList.append(
icon, [
seedName, icon,
count, seedName,
plantInfo['experience'], count,
plantInfo['harvest'], plantInfo["experience"],
plantInfo['time'], plantInfo["harvest"],
plantInfo['crop'], plantInfo["time"],
sellable plantInfo["crop"],
]) sellable,
]
)
except KeyError: except KeyError:
continue continue
@ -395,58 +446,61 @@ class CFarmManager:
str: 返回结果 str: 返回结果
""" """
try: try:
#获取用户的种子数量 # 获取用户的种子数量
count = await g_pDBService.userSeed.getUserSeedByName(uid, name) count = await g_pDBService.userSeed.getUserSeedByName(uid, name)
if count is None: if count is None:
count = 0 #如果返回 None则视为没有种子 count = 0 # 如果返回 None则视为没有种子
if count <= 0: if count <= 0:
return f"没有在你的仓库发现{name}种子,快去买点吧!" return g_sTranslation["sowing"]["noSeed"].format(name=name)
#如果播种数量超过仓库种子数量 # 如果播种数量超过仓库种子数量
if count < num and num != -1: 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) soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
#如果播种数量为 -1表示播种所有可播种的土地 # 如果播种数量为 -1表示播种所有可播种的土地
if num == -1: if num == -1:
num = count num = count
#发送播种前信号 # 发送播种前信号
await g_pEventManager.m_beforePlant.emit(uid=uid, name=name, num=num) await g_pEventManager.m_beforePlant.emit(uid=uid, name=name, num=num)
#记录是否成功播种 # 记录是否成功播种
successCount = 0 successCount = 0
for i in range(1, soilNumber + 1): for i in range(1, soilNumber + 1):
if count > 0 and num > 0: 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: if success:
#更新种子数量 # 更新种子数量
num -= 1 num -= 1
count -= 1 count -= 1
#记录种子消耗数量 # 记录种子消耗数量
successCount += 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: if successCount > 0:
await g_pDBService.userSeed.updateUserSeedByName(uid, name, count) await g_pDBService.userSeed.updateUserSeedByName(uid, name, count)
#根据播种结果给出反馈 # 根据播种结果给出反馈
if num == 0: if num == 0:
return f"播种{name}成功!仓库剩余{count}个种子" return g_sTranslation["sowing"]["success"].format(name=name, num=count)
else: else:
return f"播种数量超出开垦土地数量,已将可播种土地成功播种{name}!仓库剩余{count}个种子" return g_sTranslation["sowing"]["success2"].format(name=name, num=count)
except Exception as e: except Exception as e:
logger.warning(f"播种操作失败!", e=e) logger.warning("播种操作失败!", e=e)
return "播种失败,请稍后重试!" return g_sTranslation["sowing"]["error"]
@classmethod @classmethod
async def harvest(cls, uid: str) -> str: async def harvest(cls, uid: str) -> str:
@ -463,12 +517,12 @@ class CFarmManager:
soilNumber = await g_pDBService.user.getUserSoilByUid(uid) soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
harvestRecords = [] #收获日志记录 harvestRecords = [] # 收获日志记录
experience = 0 #总经验值 experience = 0 # 总经验值
harvestCount = 0 #成功收获数量 harvestCount = 0 # 成功收获数量
for i in range(1, soilNumber + 1): for i in range(1, soilNumber + 1):
#如果没有种植 # 如果没有种植
if not await g_pDBService.userSoil.isSoilPlanted(uid, i): if not await g_pDBService.userSoil.isSoilPlanted(uid, i):
continue continue
@ -476,21 +530,25 @@ class CFarmManager:
if not soilInfo: if not soilInfo:
continue continue
#如果是枯萎状态 # 如果是枯萎状态
if soilInfo.get("wiltStatus", 1) == 1: if soilInfo.get("wiltStatus", 1) == 1:
continue continue
plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName']) plantInfo = await g_pDBService.plant.getPlantByName(
soilInfo["plantName"]
)
if not plantInfo: if not plantInfo:
continue continue
currentTime = g_pToolManager.dateTime().now() 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 currentTime >= matureTime:
number = plantInfo['harvest'] number = plantInfo["harvest"]
#处理偷菜扣除数量 # 处理偷菜扣除数量
stealNum = await g_pDBService.userSteal.getTotalStolenCount(uid, i) stealNum = await g_pDBService.userSteal.getTotalStolenCount(uid, i)
number -= stealNum number -= stealNum
@ -499,44 +557,67 @@ class CFarmManager:
continue continue
harvestCount += 1 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']: if soilInfo["harvestCount"] + 1 >= plantInfo["crop"]:
await g_pDBService.userSoil.updateUserSoil(uid, i, "wiltStatus", 1) await g_pDBService.userSoil.updateUserSoil(
uid, i, "wiltStatus", 1
)
else: else:
phase = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName']) 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),
}
) )
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: if experience > 0:
exp = await g_pDBService.user.getUserExpByUid(uid) exp = await g_pDBService.user.getUserExpByUid(uid)
await g_pDBService.user.updateUserExpByUid(uid, exp + experience) 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: if harvestCount <= 0:
return "没有可收获的作物哦~ 不要试图拔苗助长" return g_sTranslation["harvest"]["no"]
else: else:
return "\n".join(harvestRecords) return "\n".join(harvestRecords)
except Exception as e: except Exception as e:
logger.warning(f"收获操作失败!", e=e) logger.warning("收获操作失败!", e=e)
return "收获失败,请稍后重试!" return g_sTranslation["harvest"]["error"]
@classmethod @classmethod
async def eradicate(cls, uid: str) -> str: async def eradicate(cls, uid: str) -> str:
@ -554,38 +635,38 @@ class CFarmManager:
experience = 0 experience = 0
for i in range(1, soilNumber + 1): for i in range(1, soilNumber + 1):
#如果没有种植 # 如果没有种植
if not await g_pDBService.userSoil.isSoilPlanted(uid, i): if not await g_pDBService.userSoil.isSoilPlanted(uid, i):
continue continue
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i) soilInfo = await g_pDBService.userSoil.getUserSoil(uid, i)
if not soilInfo: if not soilInfo:
continue continue
#如果不是枯萎状态 # 如果不是枯萎状态
if soilInfo.get("wiltStatus", 0) == 0: if soilInfo.get("wiltStatus", 0) == 0:
continue continue
experience += 3 experience += 3
if g_bIsDebug: if g_bIsDebug:
experience += 999 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: if experience > 0:
exp = await g_pDBService.user.getUserExpByUid(uid) exp = await g_pDBService.user.getUserExpByUid(uid)
await g_pDBService.user.updateUserExpByUid(uid, exp + experience) await g_pDBService.user.updateUserExpByUid(uid, exp + experience)
return f"成功铲除荒废作物,累计获得经验:{experience}" return g_sTranslation["eradicate"]["success"].format(exp=experience)
else: else:
return "没有可以铲除的作物" return g_sTranslation["eradicate"]["error"]
@classmethod @classmethod
async def getUserPlantByUid(cls, uid: str) -> bytes: async def getUserPlantByUid(cls, uid: str) -> bytes:
@ -598,14 +679,7 @@ class CFarmManager:
bytes: 返回图片 bytes: 返回图片
""" """
data_list = [] data_list = []
column_name = [ column_name = ["-", "作物名称", "数量", "单价", "总价", "是否可以上架交易行"]
"-",
"作物名称",
"数量",
"单价",
"总价",
"是否可以上架交易行"
]
plant = await g_pDBService.userPlant.getUserPlantByUid(uid) plant = await g_pDBService.userPlant.getUserPlantByUid(uid)
@ -629,23 +703,14 @@ class CFarmManager:
if icon_path.exists(): if icon_path.exists():
icon = (icon_path, 33, 33) icon = (icon_path, 33, 33)
if plantInfo['sell'] == True: if plantInfo["sell"] == True:
sell = "可以" sell = "可以"
else: else:
sell = "不可以" sell = "不可以"
number = int(count) * plantInfo['price'] number = int(count) * plantInfo["price"]
data_list.append( data_list.append([icon, name, count, plantInfo["price"], number, sell])
[
icon,
name,
count,
plantInfo['price'],
number,
sell
]
)
result = await ImageTemplate.table_page( result = await ImageTemplate.table_page(
"作物仓库", "作物仓库",
@ -667,30 +732,33 @@ class CFarmManager:
Returns: Returns:
str: 返回 str: 返回
""" """
#用户信息 # 用户信息
userInfo = await g_pDBService.user.getUserInfoByUid(uid) userInfo = await g_pDBService.user.getUserInfoByUid(uid)
stealTime = userInfo.get('stealTime', "") stealTime = userInfo.get("stealTime", "")
stealCount = int(userInfo['stealCount']) stealCount = int(userInfo["stealCount"])
if stealTime == "" or not stealTime: 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 stealCount = 5
elif g_pToolManager.dateTime().date().fromisoformat(stealTime) != g_pToolManager.dateTime().date().today(): elif (
stealTime = g_pToolManager.dateTime().date().today().strftime('%Y-%m-%d') g_pToolManager.dateTime().date().fromisoformat(stealTime)
!= g_pToolManager.dateTime().date().today()
):
stealTime = g_pToolManager.dateTime().date().today().strftime("%Y-%m-%d")
stealCount = 5 stealCount = 5
if stealCount <= 0: if stealCount <= 0:
return "你今天可偷次数到达上限啦,手下留情吧" return g_sTranslation["stealing"]["max"]
#获取用户解锁地块数量 # 获取用户解锁地块数量
soilNumber = await g_pDBService.user.getUserSoilByUid(target) soilNumber = await g_pDBService.user.getUserSoilByUid(target)
harvestRecords: List[str] = [] harvestRecords: list[str] = []
isStealingNumber = 0 isStealingNumber = 0
isStealingPlant = 0 isStealingPlant = 0
for i in range(1, soilNumber + 1): for i in range(1, soilNumber + 1):
#如果没有种植 # 如果没有种植
if not await g_pDBService.userSoil.isSoilPlanted(target, i): if not await g_pDBService.userSoil.isSoilPlanted(target, i):
continue continue
@ -698,123 +766,162 @@ class CFarmManager:
if not soilInfo: if not soilInfo:
continue continue
#如果是枯萎状态 # 如果是枯萎状态
if soilInfo.get("wiltStatus", 1) == 1: if soilInfo.get("wiltStatus", 1) == 1:
continue continue
#作物信息 # 作物信息
plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName']) plantInfo = await g_pDBService.plant.getPlantByName(soilInfo["plantName"])
if not plantInfo: if not plantInfo:
continue continue
currentTime = g_pToolManager.dateTime().now() 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 currentTime >= matureTime:
#如果偷过,则跳过该土地 # 如果偷过,则跳过该土地
if await g_pDBService.userSteal.hasStealed(target, i, uid): if await g_pDBService.userSteal.hasStealed(target, i, uid):
isStealingNumber += 1
continue 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 = random.choice([1, 2])
randomNumber = min(randomNumber, stealingNumber) randomNumber = min(randomNumber, stealingNumber)
if randomNumber > 0: 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 isStealingPlant += 1
#如果将作物偷完,就直接更新状态 并记录用户偷取过 # 如果将作物偷完,就直接更新状态 并记录用户偷取过
if plantInfo['harvest'] - randomNumber + stealingNumber == 0: if plantInfo["harvest"] - randomNumber + stealingNumber == 0:
#如果作物 是最后一阶段作物且偷完 则直接枯萎 # 如果作物 是最后一阶段作物且偷完 则直接枯萎
if soilInfo['harvestCount'] + 1 >= plantInfo['crop']: if soilInfo["harvestCount"] + 1 >= plantInfo["crop"]:
await g_pDBService.userSoil.updateUserSoil(target, i, "wiltStatus", 1) await g_pDBService.userSoil.updateUserSoil(
target, i, "wiltStatus", 1
)
else: else:
phase = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName']) 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),
}
) )
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: 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: if isStealingPlant <= 0 and isStealingNumber <= 0:
return "目标没有作物可以被偷" return g_sTranslation["stealing"]["noPlant"]
elif isStealingPlant <= 0 and isStealingNumber > 0: elif isStealingPlant <= 0 and isStealingNumber > 0:
return "你已经偷过目标啦,请手下留情" return g_sTranslation["stealing"]["repeat"]
else: else:
stealCount -= 1 stealCount -= 1
await g_pDBService.user.updateStealCountByUid(uid, stealCount) await g_pDBService.user.updateStealCountByUid(uid, stealTime, stealCount)
return "\n".join(harvestRecords) return "\n".join(harvestRecords)
@classmethod @classmethod
async def reclamationCondition(cls, uid: str) -> str: async def reclamationCondition(cls, uid: str) -> str:
userInfo = await g_pDBService.user.getUserInfoByUid(uid) userInfo = await g_pDBService.user.getUserInfoByUid(uid)
rec = g_pJsonManager.m_pLevel['reclamation'] rec = g_pJsonManager.m_pLevel["reclamation"]
try: try:
if userInfo['soil'] >= 30: if userInfo["soil"] >= 30:
return "你已经开垦了全部土地" return g_sTranslation["reclamation"]["perfect"]
rec = rec[f"{userInfo['soil'] + 1}"] rec = rec[f"{userInfo['soil'] + 1}"]
level = rec['level'] level = rec["level"]
point = rec['point'] point = rec["point"]
item = rec['item'] item = rec["item"]
str = "" str = ""
if len(item) == 0: if len(item) == 0:
str = f"下次开垦所需条件:等级:{level},农场币:{point}" str = g_sTranslation["reclamation"]["next"].format(
level=level, num=point
)
else: else:
str = f"下次开垦所需条件:等级:{level},农场币:{point},物品:{item}" str = g_sTranslation["reclamation"]["next2"].format(
level=level, num=point, item=item
)
return str return str
except Exception as e: except Exception:
return "获取开垦土地条件失败!" return g_sTranslation["reclamation"]["error"]
@classmethod @classmethod
async def reclamation(cls, uid: str) -> str: async def reclamation(cls, uid: str) -> str:
userInfo = await g_pDBService.user.getUserInfoByUid(uid) userInfo = await g_pDBService.user.getUserInfoByUid(uid)
level = await g_pDBService.user.getUserLevelByUid(uid) level = await g_pDBService.user.getUserLevelByUid(uid)
rec = g_pJsonManager.m_pLevel['reclamation'] rec = g_pJsonManager.m_pLevel["reclamation"]
try: try:
if userInfo['soil'] >= 30: if userInfo["soil"] >= 30:
return "你已经开垦了全部土地" return g_sTranslation["reclamation"]["perfect"]
rec = rec[f"{userInfo['soil'] + 1}"] rec = rec[f"{userInfo['soil'] + 1}"]
levelFileter = rec['level'] levelFileter = rec["level"]
point = rec['point'] point = rec["point"]
item = rec['item'] item = rec["item"]
if level[0] < levelFileter: if level[0] < levelFileter:
return f"当前用户等级{level[0]},升级所需等级为{levelFileter}" return g_sTranslation["reclamation"]["nextLevel"].format(
level=level[0], next=levelFileter
)
if userInfo['point'] < point: if userInfo["point"] < point:
return f"当前用户农场币不足,升级所需农场币为{point}" return g_sTranslation["reclamation"]["noNum"].format(num=point)
#TODO 缺少判断消耗的item # TODO 缺少判断消耗的item
await g_pDBService.user.updateUserPointByUid(uid, userInfo['point'] - point) await g_pDBService.user.updateUserPointByUid(uid, userInfo["point"] - point)
await g_pDBService.user.updateUserSoilByUid(uid, userInfo['soil'] + 1) 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() g_pFarmManager = CFarmManager()

View File

@ -1,19 +1,15 @@
import math import math
import plistlib
from itertools import islice
from zhenxun.services.log import logger from zhenxun.services.log import logger
from zhenxun.utils._build_image import BuildImage
from zhenxun.utils.image_utils import ImageTemplate 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 ..dbService import g_pDBService
from ..json import g_pJsonManager
class CShopManager: class CShopManager:
@classmethod @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: Args:
@ -44,7 +40,7 @@ class CShopManager:
"收获数量", "收获数量",
"成熟时间(小时)", "成熟时间(小时)",
"收获次数", "收获次数",
"是否可以上架交易行" "是否可以上架交易行",
] ]
# 查询所有可购买作物,并根据筛选关键字过滤 # 查询所有可购买作物,并根据筛选关键字过滤
@ -52,10 +48,10 @@ class CShopManager:
filteredPlants = [] filteredPlants = []
for plant in plants: for plant in plants:
# 跳过未解锁购买的种子 # 跳过未解锁购买的种子
if plant['isBuy'] == 0: if plant["isBuy"] == 0:
continue continue
# 字符串筛选 # 字符串筛选
if filterStr and filterStr not in plant['name']: if filterStr and filterStr not in plant["name"]:
continue continue
filteredPlants.append(plant) filteredPlants.append(plant)
@ -63,7 +59,7 @@ class CShopManager:
totalCount = len(filteredPlants) totalCount = len(filteredPlants)
pageCount = math.ceil(totalCount / 15) if totalCount else 1 pageCount = math.ceil(totalCount / 15) if totalCount else 1
startIndex = (page - 1) * 15 startIndex = (page - 1) * 15
pageItems = filteredPlants[startIndex: startIndex + 15] pageItems = filteredPlants[startIndex : startIndex + 15]
# 构建数据行 # 构建数据行
dataList = [] dataList = []
@ -75,20 +71,22 @@ class CShopManager:
icon = (iconPath, 33, 33) icon = (iconPath, 33, 33)
# 交易行标记 # 交易行标记
sell = "可以" if plant['sell'] else "不可以" sell = "可以" if plant["sell"] else "不可以"
dataList.append([ dataList.append(
icon, [
plant['name'], # 种子名称 icon,
plant['buy'], # 种子单价 plant["name"], # 种子名称
plant['level'], # 解锁等级 plant["buy"], # 种子单价
plant['price'], # 果实单价 plant["level"], # 解锁等级
plant['experience'], # 收获经验 plant["price"], # 果实单价
plant['harvest'], # 收获数量 plant["experience"], # 收获经验
plant['time'], # 成熟时间(小时) plant["harvest"], # 收获数量
plant['crop'], # 收获次数 plant["time"], # 成熟时间(小时)
sell # 是否可上架交易行 plant["crop"], # 收获次数
]) sell, # 是否可上架交易行
]
)
# 页码标题 # 页码标题
title = f"种子商店 页数: {page}/{pageCount}" title = f"种子商店 页数: {page}/{pageCount}"
@ -102,7 +100,6 @@ class CShopManager:
) )
return result.pic2bytes() return result.pic2bytes()
@classmethod @classmethod
async def buySeed(cls, uid: str, name: str, num: int = 1) -> str: async def buySeed(cls, uid: str, name: str, num: int = 1) -> str:
"""购买种子 """购买种子
@ -117,31 +114,35 @@ class CShopManager:
""" """
if num <= 0: if num <= 0:
return "请输入购买数量!" return g_sTranslation["buySeed"]["notNum"]
plantInfo = await g_pDBService.plant.getPlantByName(name) plantInfo = await g_pDBService.plant.getPlantByName(name)
if not plantInfo: if not plantInfo:
return "购买出错!请检查需购买的种子名称!" return g_sTranslation["buySeed"]["error"]
level = await g_pDBService.user.getUserLevelByUid(uid) level = await g_pDBService.user.getUserLevelByUid(uid)
if level[0] < int(plantInfo['level']): if level[0] < int(plantInfo["level"]):
return "你的等级不够哦,努努力吧" return g_sTranslation["buySeed"]["noLevel"]
point = await g_pDBService.user.getUserPointByUid(uid) 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: if point < total:
return "你的农场币不够哦~ 快速速氪金吧!" return g_sTranslation["buySeed"]["noPoint"]
else: else:
await g_pDBService.user.updateUserPointByUid(uid, point - total) await g_pDBService.user.updateUserPointByUid(uid, point - total)
if not await g_pDBService.userSeed.addUserSeedByUid(uid, name, num): 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 @classmethod
async def sellPlantByUid(cls, uid: str, name: str = "", num: int = 1) -> str: 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) plant = await g_pDBService.userPlant.getUserPlantByUid(uid)
if not plant: if not plant:
return "你仓库没有可以出售的作物" return g_sTranslation["sellPlant"]["no"]
point = 0 point = 0
totalSold = 0 totalSold = 0
isAll = (num == -1) isAll = num == -1
if name == "": if name == "":
for plantName, count in plant.items(): for plantName, count in plant.items():
@ -170,16 +171,18 @@ class CShopManager:
if not plantInfo: if not plantInfo:
continue continue
point += plantInfo['price'] * count point += plantInfo["price"] * count
await g_pDBService.userPlant.updateUserPlantByName(uid, plantName, 0) await g_pDBService.userPlant.updateUserPlantByName(uid, plantName, 0)
else: else:
if name not in plant: if name not in plant:
return f"出售作物{name}出错:仓库中不存在该作物" return g_sTranslation["sellPlant"]["error"].format(name=name)
available = plant[name] available = plant[name]
sellAmount = available if isAll else min(available, num) sellAmount = available if isAll else min(available, num)
if sellAmount <= 0: if sellAmount <= 0:
return f"出售作物{name}出错:数量不足" return g_sTranslation["sellPlant"]["error1"].format(name=name)
await g_pDBService.userPlant.updateUserPlantByName(uid, name, available - sellAmount) await g_pDBService.userPlant.updateUserPlantByName(
uid, name, available - sellAmount
)
totalSold = sellAmount totalSold = sellAmount
if name == "": if name == "":
@ -189,7 +192,7 @@ class CShopManager:
if not plantInfo: if not plantInfo:
price = 0 price = 0
else: else:
price = plantInfo['price'] price = plantInfo["price"]
totalPoint = totalSold * price totalPoint = totalSold * price
@ -197,8 +200,13 @@ class CShopManager:
await g_pDBService.user.updateUserPointByUid(uid, currentPoint + totalPoint) await g_pDBService.user.updateUserPointByUid(uid, currentPoint + totalPoint)
if name == "": if name == "":
return f"成功出售所有作物,获得农场币:{totalPoint},当前农场币:{currentPoint + totalPoint}" return g_sTranslation["sellPlant"]["success"].format(
point=totalPoint, num=currentPoint + totalPoint
)
else: else:
return f"成功出售{name},获得农场币:{totalPoint},当前农场币:{currentPoint + totalPoint}" return g_sTranslation["sellPlant"]["success1"].format(
name=name, point=totalPoint, num=currentPoint + totalPoint
)
g_pShopManager = CShopManager() g_pShopManager = CShopManager()

View File

@ -1,5 +1,4 @@
import json import json
from pathlib import Path
from zhenxun.services.log import logger from zhenxun.services.log import logger
@ -98,4 +97,5 @@ class CJsonManager:
logger.warning(f"sign_in.json JSON格式错误: {e}") logger.warning(f"sign_in.json JSON格式错误: {e}")
return False return False
g_pJsonManager = CJsonManager() g_pJsonManager = CJsonManager()

View File

@ -1,6 +1,5 @@
import json import json
import os import os
from datetime import datetime
import httpx import httpx
@ -15,12 +14,14 @@ class CRequestManager:
m_sTokens = "xZ%?z5LtWV7H:0-Xnwp+bNRNQ-jbfrxG" m_sTokens = "xZ%?z5LtWV7H:0-Xnwp+bNRNQ-jbfrxG"
@classmethod @classmethod
async def download(cls, async def download(
url: str, cls,
savePath: str, url: str,
fileName: str, savePath: str,
params: dict | None = None, fileName: str,
jsonData: dict | None = None) -> bool: params: dict | None = None,
jsonData: dict | None = None,
) -> bool:
"""下载文件到指定路径并覆盖已存在的文件 """下载文件到指定路径并覆盖已存在的文件
Args: Args:
@ -51,7 +52,9 @@ class CRequestManager:
f.write(response.content) f.write(response.content)
return True return True
else: else:
logger.warning(f"文件下载失败: HTTP {response.status_code} {response.text}") logger.warning(
f"文件下载失败: HTTP {response.status_code} {response.text}"
)
return False return False
except Exception as e: except Exception as e:
@ -84,7 +87,9 @@ class CRequestManager:
if response.status_code == 200: if response.status_code == 200:
return response.json() return response.json()
else: else:
logger.warning(f"{name}请求失败: HTTP {response.status_code} {response.text}") logger.warning(
f"{name}请求失败: HTTP {response.status_code} {response.text}"
)
return {} return {}
except httpx.RequestError as e: except httpx.RequestError as e:
logger.warning(f"{name}请求异常", e=e) logger.warning(f"{name}请求异常", e=e)
@ -115,7 +120,9 @@ class CRequestManager:
if response.status_code == 200: if response.status_code == 200:
return response.json() return response.json()
else: else:
logger.warning(f"{name}请求失败: HTTP {response.status_code} {response.text}") logger.warning(
f"{name}请求失败: HTTP {response.status_code} {response.text}"
)
return {} return {}
except httpx.RequestError as e: except httpx.RequestError as e:
logger.warning(f"{name}请求异常", e=e) logger.warning(f"{name}请求异常", e=e)
@ -128,7 +135,7 @@ class CRequestManager:
async def initSignInFile(cls) -> bool: async def initSignInFile(cls) -> bool:
if os.path.exists(g_sSignInPath): if os.path.exists(g_sSignInPath):
try: try:
with open(g_sSignInPath, "r", encoding="utf-8") as f: with open(g_sSignInPath, encoding="utf-8") as f:
content = f.read() content = f.read()
sign = json.loads(content) sign = json.loads(content)
@ -141,8 +148,8 @@ class CRequestManager:
else: else:
logger.warning("真寻农场签到文件检查失败, 即将下载") logger.warning("真寻农场签到文件检查失败, 即将下载")
return await cls.downloadSignInFile() return await cls.downloadSignInFile()
except json.JSONDecodeError as e: except json.JSONDecodeError:
logger.warning(f"真寻农场签到文件格式错误, 即将下载") logger.warning("真寻农场签到文件格式错误, 即将下载")
return await cls.downloadSignInFile() return await cls.downloadSignInFile()
else: else:
return await cls.downloadSignInFile() return await cls.downloadSignInFile()
@ -156,7 +163,7 @@ class CRequestManager:
path = str(g_sSignInPath.parent.resolve(strict=False)) path = str(g_sSignInPath.parent.resolve(strict=False))
yearMonth = g_pToolManager.dateTime().now().strftime("%Y%m") 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") g_pToolManager.renameFile(f"{path}/signTemp.json", "sign_in.json")
return True return True

138
tool.py
View File

@ -3,9 +3,146 @@ from datetime import datetime
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from zhenxun.services.log import logger from zhenxun.services.log import logger
from zhenxun.utils.message import MessageUtils
from .dbService import g_pDBService
class CToolManager: 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 @classmethod
def renameFile(cls, currentFilePath: str, newFileName: str) -> bool: def renameFile(cls, currentFilePath: str, newFileName: str) -> bool:
@ -36,4 +173,5 @@ class CToolManager:
tz = ZoneInfo("Asia/Shanghai") tz = ZoneInfo("Asia/Shanghai")
return datetime.now(tz) return datetime.now(tz)
g_pToolManager = CToolManager() g_pToolManager = CToolManager()