✨ 新增在线查漏补缺功能
19
README.md
@ -77,31 +77,30 @@
|
|||||||
| 购买农场币 | 将真寻金币兑换成农场币 | 兑换比例默认为1:2 手续费默认20% |
|
| 购买农场币 | 将真寻金币兑换成农场币 | 兑换比例默认为1:2 手续费默认20% |
|
||||||
| 更改农场名 [新的农场名] | 改名 | 农场名称无法存储特殊字符 |
|
| 更改农场名 [新的农场名] | 改名 | 农场名称无法存储特殊字符 |
|
||||||
| 农场签到 | 签到 | 需要注意,该项会从服务器拉取签到数据 |
|
| 农场签到 | 签到 | 需要注意,该项会从服务器拉取签到数据 |
|
||||||
|
| 土地升级 [地块ID] | 将土地升级,带来收益提升 | 如果土地升级时,土地有播种作物,那么将直接成熟 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 更新日志[(详细)](./log/log.md):
|
## 更新日志[(详细)](./log/log.md):
|
||||||
用户方面
|
用户方面
|
||||||
---
|
---
|
||||||
- 新增种子商店筛选功能(如果没有BUG的话后续我的种子、我的作物等也会加入筛选功能
|
- 新增土地升级指令,快来将土地升级至红土地、黑土地、金土地吧
|
||||||
- 新增签到功能(测试
|
- 新增查漏补缺作物资源功能,自动更新最新作物和作物资源文件
|
||||||
- 新增农场详述功能,能通过该功能更加详细的观看农场数据
|
- 定时更新签到文件、作物资源从00:30调整至04:30
|
||||||
- 修复了迁移旧数据库无法正常迁移的BUG
|
- 修正了部分土地资源错误的情况
|
||||||
- 修复了偷菜会导致偷自己的BUG
|
- 修正了部分文本信息错误的情况
|
||||||
- 修正了作物阶段绘制不正确的BUG
|
|
||||||
|
|
||||||
代码方面
|
代码方面
|
||||||
---
|
---
|
||||||
- 修正了多阶段作物成长素材计算逻辑
|
- 修正部分事件连接机制
|
||||||
- 修正了作物数据库字段错乱的问题
|
- 修正网络请求端口
|
||||||
- 作物新增offset字段,用于以后偏移坐标和大小(尚未启用,该模式有商议
|
|
||||||
---
|
|
||||||
|
|
||||||
## 待办事宜 `Todo` 列表
|
## 待办事宜 `Todo` 列表
|
||||||
|
|
||||||
- [x] 完善我的农场图片,例如左上角显示用户数据
|
- [x] 完善我的农场图片,例如左上角显示用户数据
|
||||||
- [ ] 完善升级数据、作物数据、作物图片
|
- [ ] 完善升级数据、作物数据、作物图片
|
||||||
- [x] 签到功能
|
- [x] 签到功能
|
||||||
|
- [x] 在线更新作物信息
|
||||||
- [ ] 添加渔场功能
|
- [ ] 添加渔场功能
|
||||||
- [ ] 增加活动、交易行功能
|
- [ ] 增加活动、交易行功能
|
||||||
- [ ] 增加交易行总行功能
|
- [ ] 增加交易行总行功能
|
||||||
|
|||||||
15
__init__.py
@ -9,6 +9,7 @@ from zhenxun.utils.message import MessageUtils
|
|||||||
from .command import diuse_farm, diuse_register, reclamation
|
from .command import diuse_farm, diuse_register, reclamation
|
||||||
from .database.database import g_pSqlManager
|
from .database.database import g_pSqlManager
|
||||||
from .dbService import g_pDBService
|
from .dbService import g_pDBService
|
||||||
|
from .event.event import g_pEventManager
|
||||||
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
|
||||||
@ -22,6 +23,7 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
指令:
|
指令:
|
||||||
at 开通农场
|
at 开通农场
|
||||||
我的农场
|
我的农场
|
||||||
|
农场详述
|
||||||
我的农场币
|
我的农场币
|
||||||
种子商店 [筛选关键字] [页数] or [页数]
|
种子商店 [筛选关键字] [页数] or [页数]
|
||||||
购买种子 [作物/种子名称] [数量]
|
购买种子 [作物/种子名称] [数量]
|
||||||
@ -36,10 +38,11 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
购买农场币 [数量] 数量为消耗金币的数量
|
购买农场币 [数量] 数量为消耗金币的数量
|
||||||
更改农场名 [新农场名]
|
更改农场名 [新农场名]
|
||||||
农场签到
|
农场签到
|
||||||
|
土地升级 [地块ID](通过农场详述获取)
|
||||||
""".strip(),
|
""".strip(),
|
||||||
extra=PluginExtraData(
|
extra=PluginExtraData(
|
||||||
author="Art_Sakura",
|
author="Art_Sakura",
|
||||||
version="1.4.3",
|
version="1.5.0",
|
||||||
commands=[Command(command="我的农场")],
|
commands=[Command(command="我的农场")],
|
||||||
menu_type="群内小游戏",
|
menu_type="群内小游戏",
|
||||||
configs=[
|
configs=[
|
||||||
@ -84,6 +87,9 @@ async def start():
|
|||||||
|
|
||||||
await g_pDBService.init()
|
await g_pDBService.init()
|
||||||
|
|
||||||
|
# 检查作物文件是否缺失 or 更新
|
||||||
|
await g_pRequestManager.initPlantDBFile()
|
||||||
|
|
||||||
|
|
||||||
# 析构函数
|
# 析构函数
|
||||||
@driver.on_shutdown
|
@driver.on_shutdown
|
||||||
@ -93,9 +99,10 @@ async def shutdown():
|
|||||||
await g_pDBService.cleanup()
|
await g_pDBService.cleanup()
|
||||||
|
|
||||||
|
|
||||||
@scheduler.scheduled_job(trigger="cron", hour=0, minute=30, id="signInFile")
|
@scheduler.scheduled_job(trigger="cron", hour=4, minute=30, id="signInFile")
|
||||||
async def signInFile():
|
async def signInFile():
|
||||||
try:
|
try:
|
||||||
await g_pJsonManager.initSignInFile()
|
await g_pJsonManager.initSignInFile()
|
||||||
except:
|
await g_pRequestManager.initPlantDBFile()
|
||||||
logger.info("农场签到文件下载失败!")
|
except Exception as e:
|
||||||
|
logger.error("农场定时检查出错", e=e)
|
||||||
|
|||||||
37
command.py
@ -58,7 +58,7 @@ async def handle_register(session: Uninfo):
|
|||||||
)
|
)
|
||||||
|
|
||||||
msg = (
|
msg = (
|
||||||
g_sTranslation["register"]["success"]
|
g_sTranslation["register"]["success"].format(point=500)
|
||||||
if success
|
if success
|
||||||
else g_sTranslation["register"]["error"]
|
else g_sTranslation["register"]["error"]
|
||||||
)
|
)
|
||||||
@ -549,6 +549,41 @@ async def _(session: Uninfo):
|
|||||||
# 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)
|
||||||
|
|
||||||
|
|
||||||
|
soil_upgrade = on_alconna(
|
||||||
|
Alconna("土地升级", Args["index", int]),
|
||||||
|
priority=5,
|
||||||
|
block=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@soil_upgrade.handle()
|
||||||
|
async def _(session: Uninfo, index: Query[int] = AlconnaQuery("num", 1)):
|
||||||
|
uid = str(session.user.id)
|
||||||
|
|
||||||
|
if not await g_pToolManager.isRegisteredByUid(uid):
|
||||||
|
return
|
||||||
|
|
||||||
|
condition = await g_pFarmManager.soilUpgradeCondition(uid, index.result)
|
||||||
|
|
||||||
|
await MessageUtils.build_message(condition).send(reply_to=True)
|
||||||
|
|
||||||
|
@waiter(waits=["message"], keep_session=True)
|
||||||
|
async def check(event: Event):
|
||||||
|
return event.get_plaintext()
|
||||||
|
|
||||||
|
resp = await check.wait(timeout=60)
|
||||||
|
if resp is None:
|
||||||
|
await MessageUtils.build_message(g_sTranslation["soilInfo"]["timeOut"]).send(
|
||||||
|
reply_to=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if not resp == "是":
|
||||||
|
return
|
||||||
|
|
||||||
|
res = await g_pFarmManager.soilUpgrade(uid, index.result)
|
||||||
|
await MessageUtils.build_message(res).send(reply_to=True)
|
||||||
|
|
||||||
|
|
||||||
diuse_farm.shortcut(
|
diuse_farm.shortcut(
|
||||||
"农场下阶段(.*?)",
|
"农场下阶段(.*?)",
|
||||||
command="我的农场",
|
command="我的农场",
|
||||||
|
|||||||
15
config.py
@ -26,6 +26,9 @@ g_sConfigPath = Path(__file__).resolve().parent / "config"
|
|||||||
# 农场签到文件路径
|
# 农场签到文件路径
|
||||||
g_sSignInPath = g_sConfigPath / "sign_in.json"
|
g_sSignInPath = g_sConfigPath / "sign_in.json"
|
||||||
|
|
||||||
|
# 土地等级上限
|
||||||
|
g_iSoilLevelMax = 3
|
||||||
|
|
||||||
# 农场同一文本
|
# 农场同一文本
|
||||||
g_sTranslation = {
|
g_sTranslation = {
|
||||||
"basic": {
|
"basic": {
|
||||||
@ -106,4 +109,16 @@ g_sTranslation = {
|
|||||||
"error": "❗️ 签到功能异常!",
|
"error": "❗️ 签到功能异常!",
|
||||||
"error1": "❌ 签到失败!未知错误 💔",
|
"error1": "❌ 签到失败!未知错误 💔",
|
||||||
},
|
},
|
||||||
|
"soilInfo": {
|
||||||
|
"success": "土地成功升级至{name},效果为:{text}",
|
||||||
|
"timeOut": "等待土地升级回复超时,请重试",
|
||||||
|
"error": "土地信息尚未查询到",
|
||||||
|
"error1": "该土地已经升至满级啦~",
|
||||||
|
"red": "增产+10%",
|
||||||
|
"black": "增产+20% 时间-20%",
|
||||||
|
"gold": "增产+28% 经验+28% 时间-20%",
|
||||||
|
"amethyst": "增产+30% 经验+30% 时间-25% 幸运+1%",
|
||||||
|
"aquamarine": "增产+32% 经验+32% 时间-28% 幸运+1%",
|
||||||
|
"blackcrystal": "增产+32% 经验+40% 时间-28% 幸运+2%",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -153,5 +153,104 @@
|
|||||||
"x": 1451,
|
"x": 1451,
|
||||||
"y": 1072
|
"y": 1072
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"upgrade":
|
||||||
|
{
|
||||||
|
"red": [
|
||||||
|
{ "level": 28, "point": 200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 29, "point": 220000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 30, "point": 240000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 31, "point": 260000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 32, "point": 290000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 33, "point": 320000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 34, "point": 350000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 35, "point": 380000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 36, "point": 410000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 37, "point": 440000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 38, "point": 480000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 39, "point": 520000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 40, "point": 560000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 41, "point": 600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 42, "point": 650000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 43, "point": 700000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 44, "point": 770000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 45, "point": 900000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 47, "point": 1500000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 49, "point": 2000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 53, "point": 4000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 53, "point": 4000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 55, "point": 5500000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 57, "point": 6800000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 60, "point": 10000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 64, "point": 15000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 68, "point": 20000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 73, "point": 30000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 78, "point": 50000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 83, "point": 80000000, "vipPoint": 0, "item":{}}
|
||||||
|
],
|
||||||
|
"black": [
|
||||||
|
{ "level": 40, "point": 500000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 41, "point": 600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 42, "point": 700000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 43, "point": 800000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 44, "point": 900000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 45, "point": 1000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 46, "point": 2000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 47, "point": 2200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 48, "point": 2400000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 49, "point": 2600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 50, "point": 2800000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 51, "point": 3000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 52, "point": 4000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 53, "point": 4200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 54, "point": 4400000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 55, "point": 4600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 56, "point": 4800000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 57, "point": 5000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 59, "point": 6000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 61, "point": 6200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 65, "point": 6600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 65, "point": 6600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 67, "point": 6800000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 69, "point": 7000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 72, "point": 10000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 76, "point": 16000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 80, "point": 27000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 85, "point": 43000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 90, "point": 65000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 95, "point": 93000000, "vipPoint": 0, "item":{}}
|
||||||
|
],
|
||||||
|
"gold": [
|
||||||
|
{ "level": 58, "point": 4000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 59, "point": 4200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 60, "point": 4400000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 61, "point": 4600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 62, "point": 4800000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 63, "point": 5000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 64, "point": 5200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 65, "point": 5400000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 66, "point": 5600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 67, "point": 5800000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 68, "point": 6000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 69, "point": 6200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 70, "point": 6600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 71, "point": 7000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 72, "point": 7400000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 73, "point": 7800000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 74, "point": 8200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 75, "point": 8600000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 77, "point": 9500000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 79, "point": 10400000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 83, "point": 12200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 83, "point": 12200000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 85, "point": 13100000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 87, "point": 14000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 90, "point": 16000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 94, "point": 20000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 98, "point": 28000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 103, "point": 44000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 108, "point": 76000000, "vipPoint": 0, "item":{}},
|
||||||
|
{ "level": 113, "point": 1080000000, "vipPoint": 0, "item":{}}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
import aiosqlite
|
import aiosqlite
|
||||||
|
|
||||||
@ -103,9 +103,14 @@ class CSqlManager:
|
|||||||
commonCols = [k for k in desired if k in existing]
|
commonCols = [k for k in desired if k in existing]
|
||||||
if commonCols:
|
if commonCols:
|
||||||
colsStr = ", ".join(f'"{c}"' for c in commonCols)
|
colsStr = ", ".join(f'"{c}"' for c in commonCols)
|
||||||
await cls.m_pDB.execute(
|
|
||||||
f'INSERT INTO "{tmpTable}" ({colsStr}) SELECT {colsStr} FROM "{tableName}";'
|
sql = (
|
||||||
|
f'INSERT INTO "{tmpTable}" ({colsStr}) '
|
||||||
|
f"SELECT {colsStr} "
|
||||||
|
f'FROM "{tableName}";'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await cls.m_pDB.execute(sql)
|
||||||
await cls.m_pDB.execute(f'DROP TABLE "{tableName}";')
|
await cls.m_pDB.execute(f'DROP TABLE "{tableName}";')
|
||||||
await cls.m_pDB.execute(
|
await cls.m_pDB.execute(
|
||||||
f'ALTER TABLE "{tmpTable}" RENAME TO "{tableName}";'
|
f'ALTER TABLE "{tmpTable}" RENAME TO "{tableName}";'
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import os
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
import os
|
||||||
|
|
||||||
import aiosqlite
|
import aiosqlite
|
||||||
|
|
||||||
|
from zhenxun.configs.config import Config
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
from ..config import g_bIsDebug, g_sPlantPath
|
from ..config import g_bIsDebug, g_sPlantPath, g_sResourcePath
|
||||||
|
from ..request import g_pRequestManager
|
||||||
|
|
||||||
|
|
||||||
class CPlantManager:
|
class CPlantManager:
|
||||||
@ -247,3 +249,45 @@ class CPlantManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("查询所有作物失败", e=e)
|
logger.warning("查询所有作物失败", e=e)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def downloadPlant(cls) -> bool:
|
||||||
|
"""遍历所有作物,下载各阶段图片及icon文件到指定文件夹
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 全部下载完成返回True,如有失败返回False
|
||||||
|
"""
|
||||||
|
success = True
|
||||||
|
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||||
|
|
||||||
|
baseUrl = baseUrl.rstrip("/") + ":8998/file"
|
||||||
|
try:
|
||||||
|
plants = await cls.listPlants()
|
||||||
|
for plant in plants:
|
||||||
|
name = plant["name"]
|
||||||
|
phaseCount = await cls.getPlantPhaseNumberByName(name)
|
||||||
|
saveDir = os.path.join(g_sResourcePath, "plant", name)
|
||||||
|
begin = 0 if plant["general"] == 0 else 1
|
||||||
|
|
||||||
|
for idx in range(begin, phaseCount + 1):
|
||||||
|
fileName = f"{idx}.png"
|
||||||
|
fullPath = os.path.join(saveDir, fileName)
|
||||||
|
|
||||||
|
if os.path.exists(fullPath):
|
||||||
|
continue
|
||||||
|
|
||||||
|
url = f"{baseUrl}/{name}/{idx}.png"
|
||||||
|
if not await g_pRequestManager.download(url, saveDir, f"{idx}.png"):
|
||||||
|
success = False
|
||||||
|
|
||||||
|
iconName = "icon.png"
|
||||||
|
iconPath = os.path.join(saveDir, iconName)
|
||||||
|
if not os.path.exists(iconPath):
|
||||||
|
iconUrl = f"{baseUrl}/{name}/{iconName}"
|
||||||
|
if not await g_pRequestManager.download(iconUrl, saveDir, iconName):
|
||||||
|
success = False
|
||||||
|
|
||||||
|
return success
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"下载作物资源异常: {e}")
|
||||||
|
return False
|
||||||
|
|||||||
@ -225,11 +225,11 @@ class CUserDB(CSqlManager):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def updateUserVipPointByUid(cls, uid: str, vipPoint: int) -> bool:
|
async def updateUserVipPointByUid(cls, uid: str, vipPoint: int) -> bool:
|
||||||
"""根据用户Uid更新农场币数量
|
"""根据用户Uid更新点券数量
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
uid (str): 用户Uid
|
uid (str): 用户Uid
|
||||||
vipPoint (int): 新农场币数量
|
vipPoint (int): 新点券数量
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 是否更新成功
|
bool: 是否更新成功
|
||||||
@ -300,7 +300,8 @@ class CUserDB(CSqlManager):
|
|||||||
uid (str): 用户Uid
|
uid (str): 用户Uid
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple[int, int, int]: (当前等级, 升至下级还需经验, 当前等级已获经验),失败返回(-1, -1, -1)
|
tuple[int, int, int]: 成功返回(当前等级, 升至下级还需经验, 当前等级已获经验)
|
||||||
|
失败返回(-1, -1, -1)
|
||||||
"""
|
"""
|
||||||
if not uid:
|
if not uid:
|
||||||
return -1, -1, -1
|
return -1, -1, -1
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import calendar
|
import calendar
|
||||||
import random
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import random
|
||||||
|
|
||||||
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
|
||||||
@ -22,7 +22,7 @@ class CUserSignDB(CSqlManager):
|
|||||||
"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'))", # 创建时间 # noqa: E501
|
||||||
"PRIMARY KEY": "(uid, signDate)",
|
"PRIMARY KEY": "(uid, signDate)",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ class CUserSignDB(CSqlManager):
|
|||||||
"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'))", # 更新时间 # noqa: E501
|
||||||
}
|
}
|
||||||
|
|
||||||
await cls.ensureTableSchema("userSignLog", userSignLog)
|
await cls.ensureTableSchema("userSignLog", userSignLog)
|
||||||
|
|||||||
@ -67,6 +67,35 @@ class CUserSoilDB(CSqlManager):
|
|||||||
f"当前阶段{currentStage}, 阶段时间{phaseList[currentStage]}, 播种时间{t}, 收获时间{s}"
|
f"当前阶段{currentStage}, 阶段时间{phaseList[currentStage]}, 播种时间{t}, 收获时间{s}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def matureNow(cls, uid: str, soilIndex: int):
|
||||||
|
"""将指定地块的作物直接成熟
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户ID
|
||||||
|
soilIndex (int): 地块索引(从1开始)
|
||||||
|
"""
|
||||||
|
# 与 nextPhase 不同:无需调试模式检查,允许在任何模式下调用
|
||||||
|
soilInfo = await cls.getUserSoil(uid, soilIndex)
|
||||||
|
if not soilInfo:
|
||||||
|
return
|
||||||
|
|
||||||
|
plantName = soilInfo.get("plantName")
|
||||||
|
if not plantName:
|
||||||
|
return
|
||||||
|
|
||||||
|
plantInfo = await g_pDBService.plant.getPlantByName(plantName)
|
||||||
|
if not plantInfo:
|
||||||
|
return
|
||||||
|
|
||||||
|
currentTime = g_pToolManager.dateTime().now().timestamp()
|
||||||
|
# 如果当前时间已经超过或等于成熟时间,则作物已成熟或可收获
|
||||||
|
if currentTime >= soilInfo["matureTime"]:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 将作物成熟时间直接更新为当前时间,实现立即成熟
|
||||||
|
await cls.updateUserSoilFields(uid, soilIndex, {"matureTime": currentTime})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def getUserFarmByUid(cls, uid: str) -> dict:
|
async def getUserFarmByUid(cls, uid: str) -> dict:
|
||||||
"""获取指定用户的旧农场数据
|
"""获取指定用户的旧农场数据
|
||||||
@ -247,6 +276,25 @@ class CUserSoilDB(CSqlManager):
|
|||||||
columns = [description[0] for description in cursor.description]
|
columns = [description[0] for description in cursor.description]
|
||||||
return dict(zip(columns, row))
|
return dict(zip(columns, row))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def countSoilByLevel(cls, uid: str, soilLevel: int) -> int:
|
||||||
|
"""统计指定用户在指定土地等级的土地数量
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户ID
|
||||||
|
soilLevel (int): 土地等级
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 符合条件的土地数量
|
||||||
|
"""
|
||||||
|
async with cls._transaction():
|
||||||
|
cursor = await cls.m_pDB.execute(
|
||||||
|
"SELECT COUNT(*) FROM userSoil WHERE uid = ? AND soilLevel = ?",
|
||||||
|
(uid, soilLevel),
|
||||||
|
)
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return row[0] if row else 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def updateUserSoil(cls, uid: str, soilIndex: int, field: str, value):
|
async def updateUserSoil(cls, uid: str, soilIndex: int, field: str, value):
|
||||||
"""更新指定用户土地的单个字段
|
"""更新指定用户土地的单个字段
|
||||||
@ -393,8 +441,8 @@ class CUserSoilDB(CSqlManager):
|
|||||||
bool: 播种成功返回 True,否则返回 False
|
bool: 播种成功返回 True,否则返回 False
|
||||||
"""
|
"""
|
||||||
# 校验土地区是否已种植
|
# 校验土地区是否已种植
|
||||||
soilRecord = await cls.getUserSoil(uid, soilIndex)
|
soilInfo = await cls.getUserSoil(uid, soilIndex)
|
||||||
if soilRecord and soilRecord.get("plantName"):
|
if not soilInfo:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 获取植物配置
|
# 获取植物配置
|
||||||
@ -404,11 +452,18 @@ class CUserSoilDB(CSqlManager):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
nowTs = int(g_pToolManager.dateTime().now().timestamp())
|
nowTs = int(g_pToolManager.dateTime().now().timestamp())
|
||||||
matureTs = nowTs + int(plantCfg.get("time", 0)) * 3600
|
|
||||||
|
time = int(plantCfg.get("time", 0))
|
||||||
|
percent = await cls.getSoilLevelTime(soilInfo.get("soilLevel", 0))
|
||||||
|
|
||||||
|
# 处理土地等级带来的时间缩短
|
||||||
|
time = time * (100 + percent) // 100
|
||||||
|
|
||||||
|
matureTs = nowTs + time * 3600
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with cls._transaction():
|
async with cls._transaction():
|
||||||
prev = soilRecord or {}
|
prev = soilInfo or {}
|
||||||
await cls._deleteUserSoil(uid, soilIndex)
|
await cls._deleteUserSoil(uid, soilIndex)
|
||||||
await cls._insertUserSoil(
|
await cls._insertUserSoil(
|
||||||
{
|
{
|
||||||
@ -457,3 +512,90 @@ class CUserSoilDB(CSqlManager):
|
|||||||
status.append("缺水")
|
status.append("缺水")
|
||||||
|
|
||||||
return ",".join(status)
|
return ",".join(status)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getSoilLevel(cls, level: int) -> str:
|
||||||
|
"""获取土地等级英文文本
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level (int): 土地等级
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str:
|
||||||
|
"""
|
||||||
|
if level == 1:
|
||||||
|
return "red"
|
||||||
|
elif level == 2:
|
||||||
|
return "black"
|
||||||
|
elif level == 3:
|
||||||
|
return "gold"
|
||||||
|
|
||||||
|
return "default"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getSoilLevelText(cls, level: int) -> str:
|
||||||
|
"""获取土地等级中文文本
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level (int): 土地等级
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str:
|
||||||
|
"""
|
||||||
|
if level == 1:
|
||||||
|
return "红土地"
|
||||||
|
elif level == 2:
|
||||||
|
return "黑土地"
|
||||||
|
elif level == 3:
|
||||||
|
return "金土地"
|
||||||
|
|
||||||
|
return "草土地"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getSoilLevelHarvestNumber(cls, level: int) -> int:
|
||||||
|
"""获取土地等级收获数量增加比例
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level (int): 土地等级
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int:
|
||||||
|
"""
|
||||||
|
if level == 2:
|
||||||
|
return 20
|
||||||
|
elif level == 3:
|
||||||
|
return 28
|
||||||
|
|
||||||
|
return 10
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getSoilLevelHarvestExp(cls, level: int) -> int:
|
||||||
|
"""获取土地等级收获经验增加比例
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level (int): 土地等级
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int:
|
||||||
|
"""
|
||||||
|
if level == 3:
|
||||||
|
return 28
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def getSoilLevelTime(cls, level: int) -> int:
|
||||||
|
"""获取土地等级播种减少时间消耗
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level (int): 土地等级
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int:
|
||||||
|
"""
|
||||||
|
if level == 2:
|
||||||
|
return 20
|
||||||
|
elif level == 3:
|
||||||
|
return 20
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|||||||
141
event/event.py
@ -1,102 +1,123 @@
|
|||||||
import asyncio
|
import inspect
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
|
|
||||||
class Signal:
|
class Signal:
|
||||||
|
def __set_name__(self, owner, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
if instance is None:
|
||||||
|
return self
|
||||||
|
bound = instance.__dict__.get(self.name)
|
||||||
|
if bound is None:
|
||||||
|
bound = _SignalBound()
|
||||||
|
instance.__dict__[self.name] = bound
|
||||||
|
return bound
|
||||||
|
|
||||||
|
|
||||||
|
class _SignalBound:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._slots = [] # 绑定的槽函数列表
|
self._slots = []
|
||||||
self._onceSlots = [] # 只触发一次的槽函数列表
|
self._onceSlots = []
|
||||||
|
|
||||||
def connect(self, slot, priority=0):
|
def connect(self, func=None, *, priority=0):
|
||||||
if callable(slot) and not any(s[0] == slot for s in self._slots):
|
if func is None:
|
||||||
self._slots.append((slot, priority))
|
return lambda f: self.connect(f, priority=priority)
|
||||||
|
if callable(func) and not any(s[0] == func for s in self._slots):
|
||||||
|
self._slots.append((func, priority))
|
||||||
self._slots.sort(key=lambda x: -x[1])
|
self._slots.sort(key=lambda x: -x[1])
|
||||||
|
return func
|
||||||
|
|
||||||
def connectOnce(self, slot, priority=0):
|
def connect_once(self, func=None, *, priority=0):
|
||||||
if callable(slot) and not any(s[0] == slot for s in self._onceSlots):
|
if func is None:
|
||||||
self._onceSlots.append((slot, priority))
|
return lambda f: self.connect_once(f, priority=priority)
|
||||||
|
if callable(func) and not any(s[0] == func for s in self._onceSlots):
|
||||||
|
self._onceSlots.append((func, priority))
|
||||||
self._onceSlots.sort(key=lambda x: -x[1])
|
self._onceSlots.sort(key=lambda x: -x[1])
|
||||||
|
return func
|
||||||
|
|
||||||
def disconnect(self, slot):
|
def disconnect(self, func):
|
||||||
self._slots = [s for s in self._slots if s[0] != slot]
|
self._slots = [s for s in self._slots if s[0] != func]
|
||||||
self._onceSlots = [s for s in self._onceSlots if s[0] != slot]
|
self._onceSlots = [s for s in self._onceSlots if s[0] != func]
|
||||||
|
|
||||||
async def emit(self, *args, **kwargs):
|
async def emit(self, *args, **kwargs):
|
||||||
slots = list(self._slots)
|
slots = list(self._slots)
|
||||||
onceSlots = list(self._onceSlots)
|
onceSlots = list(self._onceSlots)
|
||||||
self._onceSlots.clear()
|
self._onceSlots.clear()
|
||||||
|
|
||||||
for slot, _ in slots + onceSlots:
|
for slot, _ in slots + onceSlots:
|
||||||
startTime = time.time()
|
start = time.time()
|
||||||
try:
|
try:
|
||||||
if asyncio.iscoroutinefunction(slot):
|
if inspect.iscoroutinefunction(slot):
|
||||||
await slot(*args, **kwargs)
|
await slot(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
slot(*args, **kwargs)
|
slot(*args, **kwargs)
|
||||||
duration = (time.time() - startTime) * 1000
|
logger.debug(
|
||||||
logger.debug(f"事件槽 {slot.__name__} 执行完成,耗时 {duration:.2f} ms")
|
f"【真寻农场】事件槽 {slot.__name__} 执行完成,耗时 {(time.time() - start) * 1000:.2f} ms"
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"事件槽 {slot.__name__} 触发异常: {e}")
|
logger.warning(f"事件槽 {slot.__name__} 触发异常: {e}")
|
||||||
|
|
||||||
|
|
||||||
class FarmEventManager:
|
class FarmEventManager:
|
||||||
def __init__(self):
|
m_beforePlant = Signal()
|
||||||
self.m_beforePlant = Signal()
|
"""播种前信号
|
||||||
"""播种前信号
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
uid (str): 用户Uid
|
uid (str): 用户Uid
|
||||||
name (str): 播种种子名称
|
name (str): 播种种子名称
|
||||||
num (int): 播种数量
|
num (int): 播种数量
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.m_afterPlant = Signal()
|
m_afterPlant = Signal()
|
||||||
"""播种后信号 每块地播种都会触发该信号
|
"""播种后信号 每块地播种都会触发该信号
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
uid (str): 用户Uid
|
uid (str): 用户Uid
|
||||||
name (str): 播种种子名称
|
name (str): 播种种子名称
|
||||||
soilIndex (int): 播种地块索引 从1开始
|
soilIndex (int): 播种地块索引 从1开始
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.m_beforeHarvest = Signal()
|
m_beforeHarvest = Signal()
|
||||||
"""收获前信号
|
"""收获前信号
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
uid (str): 用户Uid
|
uid (str): 用户Uid
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.m_afterHarvest = Signal()
|
m_afterHarvest = Signal()
|
||||||
"""收获后信号 每块地收获都会触发该信号
|
"""收获后信号 每块地收获都会触发该信号
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
uid (str): 用户Uid
|
uid (str): 用户Uid
|
||||||
name (str): 收获作物名称
|
name (str): 收获作物名称
|
||||||
num (int): 收获数量
|
num (int): 收获数量
|
||||||
soilIndex (int): 收获地块索引 从1开始
|
soilIndex (int): 收获地块索引 从1开始
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.m_beforeEradicate = Signal()
|
m_beforeEradicate = Signal()
|
||||||
"""铲除前信号
|
"""铲除前信号
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
uid (str): 用户Uid
|
uid (str): 用户Uid
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.m_afterEradicate = Signal()
|
m_afterEradicate = Signal()
|
||||||
"""铲除后信号 每块地铲除都会触发该信号
|
"""铲除后信号 每块地铲除都会触发该信号
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
uid (str): 用户Uid
|
uid (str): 用户Uid
|
||||||
soilIndex (index): 铲除地块索引 从1开始
|
soilIndex (index): 铲除地块索引 从1开始
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.m_beforeExpand = Signal()
|
m_beforeExpand = Signal()
|
||||||
self.m_afterExpand = Signal()
|
m_afterExpand = Signal()
|
||||||
self.m_beforeSteal = Signal()
|
m_beforeSteal = Signal()
|
||||||
self.m_afterSteal = Signal()
|
m_afterSteal = Signal()
|
||||||
|
|
||||||
|
m_dit = Signal()
|
||||||
|
|
||||||
|
|
||||||
g_pEventManager = FarmEventManager()
|
g_pEventManager = FarmEventManager()
|
||||||
|
|||||||
203
farm/farm.py
@ -9,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, g_sTranslation
|
from ..config import g_bIsDebug, g_iSoilLevelMax, 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
|
||||||
@ -37,7 +37,7 @@ class CFarmManager:
|
|||||||
|
|
||||||
await UserConsole.reduce_gold(
|
await UserConsole.reduce_gold(
|
||||||
uid, num, GoldHandle.PLUGIN, "zhenxun_plugin_farm"
|
uid, num, GoldHandle.PLUGIN, "zhenxun_plugin_farm"
|
||||||
) # type: ignore
|
)
|
||||||
await UserConsole.reduce_gold(
|
await UserConsole.reduce_gold(
|
||||||
uid, fee, GoldHandle.PLUGIN, "zhenxun_plugin_farm"
|
uid, fee, GoldHandle.PLUGIN, "zhenxun_plugin_farm"
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
@ -65,10 +65,6 @@ class CFarmManager:
|
|||||||
|
|
||||||
soilSize = g_pJsonManager.m_pSoil["size"]
|
soilSize = g_pJsonManager.m_pSoil["size"]
|
||||||
|
|
||||||
# TODO 缺少判断用户土地资源状况
|
|
||||||
soil = BuildImage(background=g_sResourcePath / "soil/普通土地.png")
|
|
||||||
await soil.resize(0, soilSize[0], soilSize[1])
|
|
||||||
|
|
||||||
grass = BuildImage(background=g_sResourcePath / "soil/草土地.png")
|
grass = BuildImage(background=g_sResourcePath / "soil/草土地.png")
|
||||||
await grass.resize(0, soilSize[0], soilSize[1])
|
await grass.resize(0, soilSize[0], soilSize[1])
|
||||||
|
|
||||||
@ -88,6 +84,27 @@ class CFarmManager:
|
|||||||
|
|
||||||
# 如果土地已经到达对应等级
|
# 如果土地已经到达对应等级
|
||||||
if index < soilUnlock:
|
if index < soilUnlock:
|
||||||
|
soilUrl = ""
|
||||||
|
# TODO 缺少判断用户土地资源状况
|
||||||
|
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, index)
|
||||||
|
|
||||||
|
if not soilInfo:
|
||||||
|
soilUrl = "soil/普通土地.png"
|
||||||
|
else:
|
||||||
|
soilLevel = soilInfo.get("soilLevel", 0)
|
||||||
|
|
||||||
|
if soilLevel == 1:
|
||||||
|
soilUrl = "soil/红土地.png"
|
||||||
|
elif soilLevel == 2:
|
||||||
|
soilUrl = "soil/黑土地.png"
|
||||||
|
elif soilLevel == 3:
|
||||||
|
soilUrl = "soil/金土地.png"
|
||||||
|
else:
|
||||||
|
soilUrl = "soil/普通土地.png"
|
||||||
|
|
||||||
|
soil = BuildImage(background=g_sResourcePath / soilUrl)
|
||||||
|
await soil.resize(0, soilSize[0], soilSize[1])
|
||||||
|
|
||||||
await img.paste(soil, (x, y))
|
await img.paste(soil, (x, y))
|
||||||
|
|
||||||
isPlant, plant, isRipe, offsetX, offsetY = await cls.drawSoilPlant(
|
isPlant, plant, isRipe, offsetX, offsetY = await cls.drawSoilPlant(
|
||||||
@ -211,6 +228,7 @@ class CFarmManager:
|
|||||||
columnName = [
|
columnName = [
|
||||||
"-",
|
"-",
|
||||||
"土地ID",
|
"土地ID",
|
||||||
|
"土地等级",
|
||||||
"作物名称",
|
"作物名称",
|
||||||
"成熟时间",
|
"成熟时间",
|
||||||
"土地状态",
|
"土地状态",
|
||||||
@ -226,7 +244,7 @@ class CFarmManager:
|
|||||||
|
|
||||||
if soilInfo:
|
if soilInfo:
|
||||||
if soilInfo["soilLevel"] == 1:
|
if soilInfo["soilLevel"] == 1:
|
||||||
iconPath = g_sResourcePath / "soil/TODO.png"
|
iconPath = g_sResourcePath / "soil/红土地.png"
|
||||||
else:
|
else:
|
||||||
iconPath = g_sResourcePath / "soil/普通土地.png"
|
iconPath = g_sResourcePath / "soil/普通土地.png"
|
||||||
|
|
||||||
@ -262,6 +280,9 @@ class CFarmManager:
|
|||||||
[
|
[
|
||||||
icon,
|
icon,
|
||||||
i,
|
i,
|
||||||
|
await g_pDBService.userSoil.getSoilLevelText(
|
||||||
|
soilInfo["soilLevel"]
|
||||||
|
),
|
||||||
plantName,
|
plantName,
|
||||||
matureTime,
|
matureTime,
|
||||||
soilStatus,
|
soilStatus,
|
||||||
@ -344,8 +365,11 @@ class CFarmManager:
|
|||||||
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'] - 1}.png"
|
||||||
|
# )
|
||||||
|
|
||||||
# return True, plant, False, offsetX, offsetY
|
# return True, plant, False, offsetX, offsetY
|
||||||
|
|
||||||
@ -466,7 +490,7 @@ class CFarmManager:
|
|||||||
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) # type: ignore
|
||||||
|
|
||||||
# 记录是否成功播种
|
# 记录是否成功播种
|
||||||
successCount = 0
|
successCount = 0
|
||||||
@ -484,7 +508,7 @@ class CFarmManager:
|
|||||||
successCount += 1
|
successCount += 1
|
||||||
|
|
||||||
# 发送播种后信号
|
# 发送播种后信号
|
||||||
await g_pEventManager.m_afterPlant.emit(
|
await g_pEventManager.m_afterPlant.emit( # type: ignore
|
||||||
uid=uid, name=name, soilIndex=i
|
uid=uid, name=name, soilIndex=i
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -513,7 +537,7 @@ class CFarmManager:
|
|||||||
str: 返回
|
str: 返回
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
await g_pEventManager.m_beforeHarvest.emit(uid=uid)
|
await g_pEventManager.m_beforeHarvest.emit(uid=uid) # type: ignore
|
||||||
|
|
||||||
soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
|
soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
|
||||||
|
|
||||||
@ -530,6 +554,8 @@ class CFarmManager:
|
|||||||
if not soilInfo:
|
if not soilInfo:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
level = soilInfo.get("soilLevel", 0)
|
||||||
|
|
||||||
# 如果是枯萎状态
|
# 如果是枯萎状态
|
||||||
if soilInfo.get("wiltStatus", 1) == 1:
|
if soilInfo.get("wiltStatus", 1) == 1:
|
||||||
continue
|
continue
|
||||||
@ -550,20 +576,29 @@ class CFarmManager:
|
|||||||
|
|
||||||
# 处理偷菜扣除数量
|
# 处理偷菜扣除数量
|
||||||
stealNum = await g_pDBService.userSteal.getTotalStolenCount(uid, i)
|
stealNum = await g_pDBService.userSteal.getTotalStolenCount(uid, i)
|
||||||
|
|
||||||
number -= stealNum
|
number -= stealNum
|
||||||
|
|
||||||
|
# 处理土地等级带来的数量增长 向下取整
|
||||||
|
percent = await g_pDBService.userSoil.getSoilLevelHarvestNumber(
|
||||||
|
level
|
||||||
|
)
|
||||||
|
number = number * (100 + percent) // 100
|
||||||
|
|
||||||
if number <= 0:
|
if number <= 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
harvestCount += 1
|
harvestCount += 1
|
||||||
experience += plantInfo["experience"]
|
experience += plantInfo["experience"]
|
||||||
|
|
||||||
|
# 处理土地等级带来的经验增长 向下取整
|
||||||
|
percent = await g_pDBService.userSoil.getSoilLevelHarvestExp(level)
|
||||||
|
experience = experience * (100 + percent) // 100
|
||||||
|
|
||||||
harvestRecords.append(
|
harvestRecords.append(
|
||||||
g_sTranslation["harvest"]["append"].format(
|
g_sTranslation["harvest"]["append"].format(
|
||||||
name=soilInfo["plantName"],
|
name=soilInfo["plantName"],
|
||||||
num=number,
|
num=number,
|
||||||
exp=plantInfo["experience"],
|
exp=experience,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -597,7 +632,7 @@ class CFarmManager:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
await g_pEventManager.m_afterHarvest.emit(
|
await g_pEventManager.m_afterHarvest.emit( # type: ignore
|
||||||
uid=uid, name=soilInfo["plantName"], num=number, soilIndex=i
|
uid=uid, name=soilInfo["plantName"], num=number, soilIndex=i
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -631,7 +666,7 @@ class CFarmManager:
|
|||||||
"""
|
"""
|
||||||
soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
|
soilNumber = await g_pDBService.user.getUserSoilByUid(uid)
|
||||||
|
|
||||||
await g_pEventManager.m_beforeEradicate.emit(uid=uid)
|
await g_pEventManager.m_beforeEradicate.emit(uid=uid) # type: ignore
|
||||||
|
|
||||||
experience = 0
|
experience = 0
|
||||||
for i in range(1, soilNumber + 1):
|
for i in range(1, soilNumber + 1):
|
||||||
@ -658,7 +693,7 @@ class CFarmManager:
|
|||||||
# 铲除作物会将偷菜记录清空
|
# 铲除作物会将偷菜记录清空
|
||||||
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) # type: ignore
|
||||||
|
|
||||||
if experience > 0:
|
if experience > 0:
|
||||||
exp = await g_pDBService.user.getUserExpByUid(uid)
|
exp = await g_pDBService.user.getUserExpByUid(uid)
|
||||||
@ -703,7 +738,7 @@ 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"]:
|
||||||
sell = "可以"
|
sell = "可以"
|
||||||
else:
|
else:
|
||||||
sell = "不可以"
|
sell = "不可以"
|
||||||
@ -863,6 +898,14 @@ class CFarmManager:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def reclamationCondition(cls, uid: str) -> str:
|
async def reclamationCondition(cls, uid: str) -> str:
|
||||||
|
"""获取开垦条件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户Uid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
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"]
|
||||||
|
|
||||||
@ -892,6 +935,14 @@ class CFarmManager:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def reclamation(cls, uid: str) -> str:
|
async def reclamation(cls, uid: str) -> str:
|
||||||
|
"""开垦
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户Uid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: _description_
|
||||||
|
"""
|
||||||
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)
|
||||||
|
|
||||||
@ -905,7 +956,7 @@ class CFarmManager:
|
|||||||
|
|
||||||
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 g_sTranslation["reclamation"]["nextLevel"].format(
|
return g_sTranslation["reclamation"]["nextLevel"].format(
|
||||||
@ -923,5 +974,119 @@ class CFarmManager:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return g_sTranslation["reclamation"]["error1"]
|
return g_sTranslation["reclamation"]["error1"]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def soilUpgradeCondition(cls, uid: str, soilIndex: int) -> str:
|
||||||
|
"""获取土地升级条件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户Uid
|
||||||
|
soilIndex (str): 土地索引
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 返回土地升级条件
|
||||||
|
"""
|
||||||
|
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
|
||||||
|
|
||||||
|
if not soilInfo:
|
||||||
|
return g_sTranslation["soilInfo"]["error"]
|
||||||
|
|
||||||
|
soilLevel = soilInfo.get("soilLevel", 0) + 1
|
||||||
|
if soilLevel >= g_iSoilLevelMax:
|
||||||
|
return g_sTranslation["soilInfo"]["error1"]
|
||||||
|
|
||||||
|
# 获取用户当前土地 的下一级土地 数量
|
||||||
|
countSoil = await g_pDBService.userSoil.countSoilByLevel(uid, soilLevel)
|
||||||
|
|
||||||
|
# 获取升级所需
|
||||||
|
soilLevelText = await g_pDBService.userSoil.getSoilLevel(soilLevel)
|
||||||
|
fileter = g_pJsonManager.m_pSoil["upgrade"][soilLevelText][countSoil]
|
||||||
|
|
||||||
|
lines = ["升级该土地所需:"]
|
||||||
|
fields = [
|
||||||
|
("level", "等级"),
|
||||||
|
("point", "金币"),
|
||||||
|
("vipPoint", "点券"),
|
||||||
|
]
|
||||||
|
for key, label in fields:
|
||||||
|
value = fileter.get(key, 0)
|
||||||
|
if value > 0:
|
||||||
|
lines.append(f"{label}:{value}")
|
||||||
|
|
||||||
|
items = fileter.get("item", {})
|
||||||
|
for name, qty in items.items():
|
||||||
|
if qty:
|
||||||
|
lines.append(f"{name}:{qty}")
|
||||||
|
|
||||||
|
lines.append("回复“是”将执行升级")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def soilUpgrade(cls, uid: str, soilIndex: int) -> str:
|
||||||
|
"""土地升级
|
||||||
|
|
||||||
|
Args:
|
||||||
|
uid (str): 用户Uid
|
||||||
|
soilIndex (int): 土地索引
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str:
|
||||||
|
"""
|
||||||
|
userInfo = await g_pDBService.user.getUserInfoByUid(uid)
|
||||||
|
soilInfo = await g_pDBService.userSoil.getUserSoil(uid, soilIndex)
|
||||||
|
|
||||||
|
if not soilInfo:
|
||||||
|
return g_sTranslation["soilInfo"]["error"]
|
||||||
|
|
||||||
|
soilLevel = soilInfo.get("soilLevel", 0) + 1
|
||||||
|
if soilLevel >= g_iSoilLevelMax:
|
||||||
|
return g_sTranslation["soilInfo"]["error1"]
|
||||||
|
|
||||||
|
countSoil = await g_pDBService.userSoil.countSoilByLevel(uid, soilLevel)
|
||||||
|
|
||||||
|
soilLevelText = await g_pDBService.userSoil.getSoilLevel(soilLevel)
|
||||||
|
fileter = g_pJsonManager.m_pSoil["upgrade"][soilLevelText][countSoil]
|
||||||
|
|
||||||
|
getters = {
|
||||||
|
"level": (await g_pDBService.user.getUserLevelByUid(uid))[0],
|
||||||
|
"point": userInfo.get("point", 0),
|
||||||
|
"vipPoint": userInfo.get("vipPoint", 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
requirements = {
|
||||||
|
"level": "等级",
|
||||||
|
"point": "金币",
|
||||||
|
"vipPoint": "点券",
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, val in getters.items():
|
||||||
|
need = fileter.get(key, 0)
|
||||||
|
if val < need:
|
||||||
|
return f"你的{requirements[key]}不够哦~"
|
||||||
|
|
||||||
|
# 缺少item判断
|
||||||
|
|
||||||
|
# 更新数据库字段
|
||||||
|
await g_pDBService.userSoil.updateUserSoil(
|
||||||
|
uid, soilIndex, "soilLevel", soilLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
# 如果有作物的话直接成熟
|
||||||
|
await g_pDBService.userSoil.matureNow(uid, soilIndex)
|
||||||
|
|
||||||
|
# 更新数据库字段
|
||||||
|
await g_pDBService.user.updateUserPointByUid(
|
||||||
|
uid, userInfo.get("point", 0) - fileter.get("point", 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
await g_pDBService.user.updateUserPointByUid(
|
||||||
|
uid, userInfo.get("vipPoint", 0) - fileter.get("vipPoint", 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
return g_sTranslation["soilInfo"]["success"].format(
|
||||||
|
name=await g_pDBService.userSoil.getSoilLevelText(soilLevel),
|
||||||
|
text=g_sTranslation["soilInfo"][soilLevelText],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
g_pFarmManager = CFarmManager()
|
g_pFarmManager = CFarmManager()
|
||||||
|
|||||||
6
json.py
@ -79,7 +79,11 @@ class CJsonManager:
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return await self.initSign()
|
result = await self.initSign()
|
||||||
|
|
||||||
|
config.g_bSignStatus = result
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
async def initSign(self) -> bool:
|
async def initSign(self) -> bool:
|
||||||
try:
|
try:
|
||||||
|
|||||||
14
log/log.md
@ -1,5 +1,19 @@
|
|||||||
# 真寻农场更新日志
|
# 真寻农场更新日志
|
||||||
|
|
||||||
|
## V1.5
|
||||||
|
用户方面
|
||||||
|
---
|
||||||
|
- 新增土地升级指令,快来将土地升级至红土地、黑土地、金土地吧
|
||||||
|
- 新增查漏补缺作物资源功能,自动更新最新作物和作物资源文件
|
||||||
|
- 定时更新签到文件、作物资源从00:30调整至04:30
|
||||||
|
- 修正了部分土地资源错误的情况
|
||||||
|
- 修正了部分文本信息错误的情况
|
||||||
|
|
||||||
|
代码方面
|
||||||
|
---
|
||||||
|
- 修正部分事件连接机制
|
||||||
|
- 修正网络请求端口
|
||||||
|
|
||||||
## V1.4
|
## V1.4
|
||||||
用户方面
|
用户方面
|
||||||
---
|
---
|
||||||
|
|||||||
151
request.py
@ -2,11 +2,20 @@ import json
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
from rich.progress import (
|
||||||
|
BarColumn,
|
||||||
|
DownloadColumn,
|
||||||
|
Progress,
|
||||||
|
TextColumn,
|
||||||
|
TimeRemainingColumn,
|
||||||
|
TransferSpeedColumn,
|
||||||
|
)
|
||||||
|
|
||||||
from zhenxun.configs.config import Config
|
from zhenxun.configs.config import Config
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
|
||||||
from .config import g_sSignInPath
|
from .config import g_sPlantPath, g_sSignInPath
|
||||||
|
from .dbService import g_pDBService
|
||||||
from .tool import g_pToolManager
|
from .tool import g_pToolManager
|
||||||
|
|
||||||
|
|
||||||
@ -22,7 +31,7 @@ class CRequestManager:
|
|||||||
params: dict | None = None,
|
params: dict | None = None,
|
||||||
jsonData: dict | None = None,
|
jsonData: dict | None = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""下载文件到指定路径并覆盖已存在的文件
|
"""下载文件到指定路径并覆盖已存在的文件,并显示下载进度条
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
url (str): 文件的下载链接
|
url (str): 文件的下载链接
|
||||||
@ -30,33 +39,53 @@ class CRequestManager:
|
|||||||
fileName (str): 保存后的文件名
|
fileName (str): 保存后的文件名
|
||||||
params (dict | None): 可选的 URL 查询参数
|
params (dict | None): 可选的 URL 查询参数
|
||||||
jsonData (dict | None): 可选的 JSON 请求体
|
jsonData (dict | None): 可选的 JSON 请求体
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 是否下载成功
|
bool: 是否下载成功
|
||||||
"""
|
"""
|
||||||
headers = {"token": cls.m_sTokens}
|
headers = {"token": cls.m_sTokens}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with httpx.AsyncClient(timeout=10.0) as client:
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||||
requestArgs: dict = {"headers": headers}
|
requestArgs: dict = {"headers": headers}
|
||||||
if params:
|
if params:
|
||||||
requestArgs["params"] = params
|
requestArgs["params"] = params
|
||||||
if jsonData:
|
if jsonData:
|
||||||
requestArgs["json"] = jsonData
|
requestArgs["json"] = jsonData
|
||||||
|
|
||||||
response = await client.request("GET", url, **requestArgs)
|
response = await client.request(
|
||||||
|
"GET", url, **requestArgs, follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code != 200:
|
||||||
fullPath = os.path.join(savePath, fileName)
|
|
||||||
os.makedirs(os.path.dirname(fullPath), exist_ok=True)
|
|
||||||
with open(fullPath, "wb") as f:
|
|
||||||
f.write(response.content)
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"文件下载失败: HTTP {response.status_code} {response.text}"
|
f"文件下载失败: HTTP {response.status_code} {response.text}"
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
totalLength = int(response.headers.get("Content-Length", 0))
|
||||||
|
fullPath = os.path.join(savePath, fileName)
|
||||||
|
os.makedirs(os.path.dirname(fullPath), exist_ok=True)
|
||||||
|
|
||||||
|
with Progress(
|
||||||
|
TextColumn("[progress.description]{task.description}"),
|
||||||
|
BarColumn(),
|
||||||
|
DownloadColumn(),
|
||||||
|
TransferSpeedColumn(),
|
||||||
|
TimeRemainingColumn(),
|
||||||
|
transient=True,
|
||||||
|
) as progress:
|
||||||
|
task = progress.add_task(
|
||||||
|
f"[green]【真寻农场】正在下载 {fileName}", total=totalLength
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(fullPath, "wb") as f:
|
||||||
|
async for chunk in response.aiter_bytes(chunk_size=1024):
|
||||||
|
f.write(chunk)
|
||||||
|
progress.advance(task, len(chunk))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"下载文件异常: {e}")
|
logger.warning(f"下载文件异常: {e}")
|
||||||
return False
|
return False
|
||||||
@ -77,7 +106,7 @@ class CRequestManager:
|
|||||||
dict: 返回请求结果的JSON数据
|
dict: 返回请求结果的JSON数据
|
||||||
"""
|
"""
|
||||||
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||||
url = f"{baseUrl.rstrip('/')}/{endpoint.lstrip('/')}"
|
url = f"{baseUrl.rstrip('/')}:8998/{endpoint.lstrip('/')}"
|
||||||
headers = {"token": cls.m_sTokens}
|
headers = {"token": cls.m_sTokens}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -110,7 +139,7 @@ class CRequestManager:
|
|||||||
dict: 返回请求结果的JSON数据
|
dict: 返回请求结果的JSON数据
|
||||||
"""
|
"""
|
||||||
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||||
url = f"{baseUrl.rstrip('/')}/{endpoint.lstrip('/')}"
|
url = f"{baseUrl.rstrip('/')}:8998/{endpoint.lstrip('/')}"
|
||||||
headers = {"token": cls.m_sTokens}
|
headers = {"token": cls.m_sTokens}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -156,20 +185,110 @@ class CRequestManager:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def downloadSignInFile(cls) -> bool:
|
async def downloadSignInFile(cls) -> bool:
|
||||||
|
"""下载签到文件,并重命名为 sign_in.json
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否下载成功
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||||
|
|
||||||
url = f"{baseUrl.rstrip('/')}:8998/sign_in"
|
url = f"{baseUrl.rstrip('/')}:8998/sign_in"
|
||||||
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})
|
# 下载为 signTemp.json
|
||||||
g_pToolManager.renameFile(f"{path}/signTemp.json", "sign_in.json")
|
success = await cls.download(
|
||||||
|
url=url,
|
||||||
|
savePath=path,
|
||||||
|
fileName="signTemp.json",
|
||||||
|
jsonData={"date": yearMonth},
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 重命名为 sign_in.json
|
||||||
|
g_pToolManager.renameFile(f"{path}/signTemp.json", "sign_in.json")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("下载签到文件失败", e=e)
|
logger.error("下载签到文件失败", e=e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def initPlantDBFile(cls) -> bool:
|
||||||
|
"""检查本地 plant.db 版本,如远程版本更新则重新下载
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否为最新版或成功更新
|
||||||
|
"""
|
||||||
|
versionPath = os.path.join(os.path.dirname(g_sPlantPath), "version.json")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(versionPath, encoding="utf-8") as f:
|
||||||
|
localVersion = json.load(f).get("version", 0)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"读取本地版本失败,默认版本为0: {e}")
|
||||||
|
localVersion = 0
|
||||||
|
|
||||||
|
remoteInfo = await cls.get("plant_version", name="版本检查")
|
||||||
|
remoteVersion = remoteInfo.get("version")
|
||||||
|
|
||||||
|
if remoteVersion is None:
|
||||||
|
logger.warning("获取远程版本失败")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if float(remoteVersion) <= float(localVersion):
|
||||||
|
logger.debug("plant.db 已为最新版本")
|
||||||
|
return True
|
||||||
|
|
||||||
|
logger.warning(
|
||||||
|
f"发现新版本 plant.db(远程: {remoteVersion} / 本地: {localVersion}),开始更新..."
|
||||||
|
)
|
||||||
|
|
||||||
|
# 先断开数据库连接
|
||||||
|
await g_pDBService.cleanup()
|
||||||
|
|
||||||
|
return await cls.downloadPlantDBFile(remoteVersion)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def downloadPlantDBFile(cls, remoteVersion: float) -> bool:
|
||||||
|
"""下载最新版 plant.db 并更新本地 version.json
|
||||||
|
|
||||||
|
Args:
|
||||||
|
remoteVersion (float): 远程版本号
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否下载并更新成功
|
||||||
|
"""
|
||||||
|
baseUrl = Config.get_config("zhenxun_plugin_farm", "服务地址")
|
||||||
|
|
||||||
|
savePath = os.path.dirname(g_sPlantPath)
|
||||||
|
success = await cls.download(
|
||||||
|
url=f"{baseUrl.rstrip('/')}:8998/file/plant.db",
|
||||||
|
savePath=savePath,
|
||||||
|
fileName="plantTemp.db",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 重命名为 sign_in.json
|
||||||
|
g_pToolManager.renameFile(f"{savePath}/plantTemp.db", "plant.db")
|
||||||
|
|
||||||
|
versionPath = os.path.join(savePath, "version.json")
|
||||||
|
try:
|
||||||
|
with open(versionPath, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({"version": remoteVersion}, f)
|
||||||
|
logger.debug("版本文件已更新")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"写入版本文件失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
await g_pDBService.plant.init()
|
||||||
|
await g_pDBService.plant.downloadPlant()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
g_pRequestManager = CRequestManager()
|
g_pRequestManager = CRequestManager()
|
||||||
|
|||||||
1
resource/db/version.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version": 0.41}
|
||||||
BIN
resource/plant/南瓜/1.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 21 KiB |
BIN
resource/plant/双玉兰/1.png
Normal file
|
After Width: | Height: | Size: 795 B |
BIN
resource/plant/双玉兰/2.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
resource/plant/双玉兰/3.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
resource/plant/双玉兰/4.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
resource/plant/双玉兰/5.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
resource/plant/双玉兰/icon.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 11 KiB |
BIN
resource/plant/大白菜/5.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
resource/plant/红叶梅/1.png
Normal file
|
After Width: | Height: | Size: 966 B |
BIN
resource/plant/红叶梅/2.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
resource/plant/红叶梅/3.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
resource/plant/红叶梅/4.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
resource/plant/红叶梅/5.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
resource/plant/红叶梅/icon.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
resource/plant/红毛丹/1.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
resource/plant/红毛丹/2.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
resource/plant/红毛丹/3.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
resource/plant/红毛丹/4.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
resource/plant/红毛丹/5.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
resource/plant/红毛丹/icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
resource/plant/鸾风玉/1.png
Normal file
|
After Width: | Height: | Size: 1019 B |
BIN
resource/plant/鸾风玉/2.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
resource/plant/鸾风玉/3.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
resource/plant/鸾风玉/4.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
resource/plant/鸾风玉/5.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
resource/plant/鸾风玉/icon.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
resource/plant/黄豆/1.png
Normal file
|
After Width: | Height: | Size: 822 B |
|
Before Width: | Height: | Size: 822 B After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
90
tool.py
@ -40,89 +40,17 @@ class CToolManager:
|
|||||||
cleaned = username.strip()
|
cleaned = username.strip()
|
||||||
|
|
||||||
# 允许的字符白名单(可自定义扩展)
|
# 允许的字符白名单(可自定义扩展)
|
||||||
|
# fmt: off
|
||||||
safe_chars = {
|
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",
|
||||||
"^",
|
|
||||||
"&",
|
|
||||||
"*",
|
|
||||||
"(",
|
|
||||||
")",
|
|
||||||
"+",
|
|
||||||
"=",
|
|
||||||
".",
|
|
||||||
",",
|
|
||||||
"~",
|
|
||||||
"·",
|
|
||||||
" ",
|
|
||||||
"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",
|
|
||||||
}
|
}
|
||||||
|
# fmt: on
|
||||||
# 添加常用中文字符(Unicode范围)
|
# 添加常用中文字符(Unicode范围)
|
||||||
safe_chars.update(chr(c) for c in range(0x4E00, 0x9FFF + 1))
|
safe_chars.update(chr(c) for c in range(0x4E00, 0x9FFF + 1))
|
||||||
|
|
||||||
|
|||||||