新增每天00:30检查签到文件

This commit is contained in:
Art_Sakura 2025-05-29 18:20:03 +08:00
parent a475d0188b
commit 1ef506d2fd
31 changed files with 172 additions and 24 deletions

View File

@ -1,5 +1,6 @@
from nonebot import get_driver from nonebot import get_driver
from nonebot.plugin import PluginMetadata from nonebot.plugin import PluginMetadata
from nonebot_plugin_apscheduler import scheduler
from zhenxun.configs.utils import Command, PluginExtraData, RegisterConfig from zhenxun.configs.utils import Command, PluginExtraData, RegisterConfig
from zhenxun.services.log import logger from zhenxun.services.log import logger
@ -89,3 +90,12 @@ async def shutdown():
await g_pSqlManager.cleanup() await g_pSqlManager.cleanup()
await g_pDBService.cleanup() await g_pDBService.cleanup()
@scheduler.add_job(
trigger="cron",
hour=0,
minute=30,
)
async def _():
await g_pJsonManager.initSignInFile()

View File

@ -140,6 +140,7 @@ diuse_farm = on_alconna(
#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="农场下阶段"),
), ),
priority=5, priority=5,
block=True, block=True,
@ -513,6 +514,11 @@ async def _(session: Uninfo):
message += f"\n\n成功领取累计签到奖励:\n额外获得经验{extraExp},额外获得金币{extraPoint}" message += f"\n\n成功领取累计签到奖励:\n额外获得经验{extraExp},额外获得金币{extraPoint}"
vipPoint = reward.get('vipPoint', 0)
if vipPoint > 0:
message += f",额外获得点券{vipPoint}"
if plant: if plant:
for key, value in plant.items(): for key, value in plant.items():
message += f"\n获得{key}种子 * {value}" message += f"\n获得{key}种子 * {value}"
@ -522,3 +528,19 @@ async def _(session: Uninfo):
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(
"农场下阶段(.*?)",
command="我的农场",
arguments=["admin-up"],
prefix=True,
)
@diuse_farm.assign("admin-up")
async def _(session: Uninfo, num: Query[int] = AlconnaQuery("num", 0)):
uid = str(session.user.id)
if not await isRegisteredByUid(uid):
return
await g_pDBService.userSoil.nextPhase(uid, num.result)

View File

@ -2,6 +2,8 @@ from pathlib import Path
from zhenxun.configs.path_config import DATA_PATH from zhenxun.configs.path_config import DATA_PATH
g_bIsDebug = True
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"

View File

@ -8,7 +8,7 @@ import aiosqlite
from zhenxun.services.log import logger from zhenxun.services.log import logger
from ..config import g_sPlantPath from ..config import g_bIsDebug, g_sPlantPath
class CPlantManager: class CPlantManager:
@ -27,7 +27,12 @@ class CPlantManager:
async def init(cls) -> bool: async def init(cls) -> bool:
try: try:
_ = os.path.exists(g_sPlantPath) _ = os.path.exists(g_sPlantPath)
cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath))
if g_bIsDebug:
cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath.parent / "plant-test.db"))
else:
cls.m_pDB = await aiosqlite.connect(str(g_sPlantPath))
cls.m_pDB.row_factory = aiosqlite.Row cls.m_pDB.row_factory = aiosqlite.Row
return True return True
except Exception as e: except Exception as e:

View File

@ -3,8 +3,8 @@ from typing import List, Union
from zhenxun.services.log import logger from zhenxun.services.log import logger
from .database import CSqlManager
from ..tool import g_pToolManager from ..tool import g_pToolManager
from .database import CSqlManager
class CUserDB(CSqlManager): class CUserDB(CSqlManager):
@ -16,6 +16,7 @@ class CUserDB(CSqlManager):
"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", #点券
"soil": "INTEGER DEFAULT 3", #解锁土地数量 "soil": "INTEGER DEFAULT 3", #解锁土地数量
"stealTime": "TEXT DEFAULT NULL", #偷菜时间字符串 "stealTime": "TEXT DEFAULT NULL", #偷菜时间字符串
"stealCount": "INTEGER DEFAULT 0" #剩余偷菜次数 "stealCount": "INTEGER DEFAULT 0" #剩余偷菜次数
@ -203,6 +204,52 @@ class CUserDB(CSqlManager):
logger.error("updateUserPointByUid 事务执行失败!", e=e) logger.error("updateUserPointByUid 事务执行失败!", e=e)
return False return False
@classmethod
async def getUserVipPointByUid(cls, uid: str) -> int:
"""获取指定用户点券
Args:
uid (str): 用户Uid
Returns:
int: 点券数量失败返回 -1
"""
if not uid:
return -1
try:
async with cls.m_pDB.execute(
"SELECT vipPoint FROM user WHERE uid = ?", (uid,)
) as cursor:
row = await cursor.fetchone()
return int(row[0]) if row and row[0] is not None else -1
except Exception as e:
logger.warning("getUservipPointByUid 查询失败!", e=e)
return -1
@classmethod
async def updateUserVipPointByUid(cls, uid: str, vipPoint: int) -> bool:
"""根据用户Uid更新农场币数量
Args:
uid (str): 用户Uid
vipPoint (int): 新农场币数量
Returns:
bool: 是否更新成功
"""
if not uid or vipPoint < 0:
logger.warning("updateUservipPointByUid 参数校验失败!")
return False
try:
async with cls._transaction():
await cls.m_pDB.execute(
"UPDATE user SET vipPoint = ? WHERE uid = ?", (vipPoint, uid)
)
return True
except Exception as e:
logger.error("updateUservipPointByUid 事务执行失败!", e=e)
return False
@classmethod @classmethod
async def getUserExpByUid(cls, uid: str) -> int: async def getUserExpByUid(cls, uid: str) -> int:
"""获取指定用户经验值 """获取指定用户经验值

View File

@ -6,6 +6,7 @@ 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
from ..config import g_bIsDebug
from ..dbService import g_pDBService from ..dbService import g_pDBService
from ..json import g_pJsonManager from ..json import g_pJsonManager
from ..tool import g_pToolManager from ..tool import g_pToolManager
@ -145,6 +146,7 @@ class CUserSignDB(CSqlManager):
exp = random.randint(expMin, expMax) exp = random.randint(expMin, expMax)
point = random.randint(pointMin, pointMax) point = random.randint(pointMin, pointMax)
vipPoint = 0
async with cls._transaction(): async with cls._transaction():
await cls.m_pDB.execute( await cls.m_pDB.execute(
@ -193,6 +195,7 @@ class CUserSignDB(CSqlManager):
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)
plant = reward.get('plant', {}) plant = reward.get('plant', {})
@ -200,6 +203,9 @@ class CUserSignDB(CSqlManager):
for key, value in plant.items(): for key, value in plant.items():
await g_pDBService.userSeed.addUserSeedByUid(uid, key, value) await g_pDBService.userSeed.addUserSeedByUid(uid, key, value)
if g_bIsDebug:
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)
@ -207,6 +213,10 @@ class CUserSignDB(CSqlManager):
currentPoint = await g_pDBService.user.getUserPointByUid(uid) currentPoint = await g_pDBService.user.getUserPointByUid(uid)
await g_pDBService.user.updateUserPointByUid(uid, currentPoint + point) await g_pDBService.user.updateUserPointByUid(uid, currentPoint + point)
if vipPoint > 0:
currentVipPoint = await g_pDBService.user.getUserVipPointByUid(uid)
await g_pDBService.user.updateUserVipPointByUid(uid, currentVipPoint + vipPoint)
return 1 return 1
except Exception as e: except Exception as e:
logger.warning("执行签到失败", e=e) logger.warning("执行签到失败", e=e)

View File

@ -2,6 +2,7 @@ from typing import Optional
from zhenxun.services.log import logger from zhenxun.services.log import logger
from ..config import g_bIsDebug
from ..dbService import g_pDBService from ..dbService import g_pDBService
from ..json import g_pJsonManager from ..json import g_pJsonManager
from ..tool import g_pToolManager from ..tool import g_pToolManager
@ -29,6 +30,46 @@ class CUserSoilDB(CSqlManager):
await cls.ensureTableSchema("userSoil", userSoil) await cls.ensureTableSchema("userSoil", userSoil)
@classmethod
async def nextPhase(cls, uid: str, soilIndex: int):
"""将指定地块的作物进入下个阶段
Args:
soilIndex (int): 地块索引 从1开始
"""
if not g_bIsDebug:
return
soilInfo = await cls.getUserSoil(uid, soilIndex)
if not soilInfo:
return
plantInfo = await g_pDBService.plant.getPlantByName(soilInfo['plantName'])
if not plantInfo:
return
currentTime = g_pToolManager.dateTime().now()
matureTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['matureTime']))
phase = await g_pDBService.plant.getPlantPhaseNumberByName(soilInfo['plantName'])
phaseList = await g_pDBService.plant.getPlantPhaseByName(soilInfo['plantName'])
if currentTime >= matureTime:
return
plantedTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['plantTime']))
elapsedTime = currentTime - plantedTime
elapsedHour = elapsedTime.total_seconds() / 3600
currentStage = int(elapsedHour / (plantInfo['time'] / phase))
t = int(soilInfo['plantTime']) - phaseList[currentStage]
await cls.updateUserSoil(uid, soilIndex, "plantTime", t)
@classmethod @classmethod
async def getUserFarmByUid(cls, uid: str) -> dict: async def getUserFarmByUid(cls, uid: str) -> dict:
"""获取指定用户的旧农场数据 """获取指定用户的旧农场数据

View File

@ -11,7 +11,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_sResourcePath from ..config import g_bIsDebug, g_sResourcePath
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
@ -88,11 +88,11 @@ class CFarmManager:
if index < soilUnlock: if index < soilUnlock:
await img.paste(soil, (x, y)) await img.paste(soil, (x, y))
isPlant, plant, isRipe= 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, await img.paste(plant, (x + soilSize[0] // 2 - plant.width // 2 + offsetX,
y + soilSize[1] // 2 - plant.height // 2)) y + soilSize[1] // 2 - plant.height // 2 + offsetY))
#1700 275 #1700 275
#首次添加可收获图片 #首次添加可收获图片
@ -258,7 +258,7 @@ class CFarmManager:
return info return info
@classmethod @classmethod
async def drawSoilPlant(cls, uid: str, soilIndex: int) -> tuple[bool, BuildImage, bool]: async def drawSoilPlant(cls, uid: str, soilIndex: int) -> tuple[bool, BuildImage, bool, int, int]:
"""绘制植物资源 """绘制植物资源
Args: Args:
@ -279,13 +279,18 @@ class CFarmManager:
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 / f"plant/basic/9.png")
await plant.resize(0, 150, 212) await plant.resize(0, 150, 212)
return True, plant, False 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 #type: ignore return False, None, False, 0, 0 #type: ignore
offsetX = plantInfo.get('officX', 0)
offsetY = plantInfo.get('officY', 0)
offsetW = plantInfo.get('officW', 0)
offsetH = plantInfo.get('officH', 0)
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']))
@ -296,13 +301,13 @@ class CFarmManager:
if currentTime >= matureTime: if currentTime >= matureTime:
plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{phase}.png") plant = BuildImage(background = g_sResourcePath / f"plant/{soilInfo['plantName']}/{phase}.png")
return True, plant, True 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 # return True, plant, False, offsetX, offsetY
#如果没有成熟 则根据当前阶段进行绘制 #如果没有成熟 则根据当前阶段进行绘制
plantedTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['plantTime'])) plantedTime = g_pToolManager.dateTime().fromtimestamp(int(soilInfo['plantTime']))
@ -318,11 +323,11 @@ class CFarmManager:
else: else:
plant = BuildImage(background = g_sResourcePath / f"plant/basic/0.png") plant = BuildImage(background = g_sResourcePath / f"plant/basic/0.png")
await plant.resize(0, 35, 58) 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 return True, plant, False, offsetX, offsetY
@classmethod @classmethod
async def getUserSeedByUid(cls, uid: str) -> bytes: async def getUserSeedByUid(cls, uid: str) -> bytes:
@ -568,6 +573,9 @@ class CFarmManager:
experience += 3 experience += 3
if g_bIsDebug:
experience += 999
#批量更新数据库操作 #批量更新数据库操作
await g_pDBService.userSoil.deleteUserSoil(uid, i) await g_pDBService.userSoil.deleteUserSoil(uid, i)
@ -775,13 +783,13 @@ class CFarmManager:
str = "" str = ""
if len(item) == 0: if len(item) == 0:
str = f"下次升级所需条件:等级:{level},农场币:{point}" str = f"下次开垦所需条件:等级:{level},农场币:{point}"
else: else:
str = f"下次升级所需条件:等级:{level},农场币:{point},物品:{item}" str = f"下次开垦所需条件:等级:{level},农场币:{point},物品:{item}"
return str return str
except Exception as e: except Exception as e:
return "获取升级土地条件失败!" return "获取开垦土地条件失败!"
@classmethod @classmethod
async def reclamation(cls, uid: str) -> str: async def reclamation(cls, uid: str) -> str:

15
json.py
View File

@ -24,12 +24,7 @@ class CJsonManager:
if not await self.initSoil(): if not await self.initSoil():
return False return False
if not await g_pRequestManager.initSignInFile(): return await self.initSignInFile()
config.g_bSignStatus = False
return False
else:
return await self.initSign()
async def initItem(self) -> bool: async def initItem(self) -> bool:
try: try:
@ -79,6 +74,14 @@ class CJsonManager:
logger.warning(f"soil.json JSON格式错误: {e}") logger.warning(f"soil.json JSON格式错误: {e}")
return False return False
async def initSignInFile(self) -> bool:
if not await g_pRequestManager.initSignInFile():
config.g_bSignStatus = False
return False
else:
return await self.initSign()
async def initSign(self) -> bool: async def initSign(self) -> bool:
try: try:
with open( with open(

View File

@ -12,7 +12,7 @@
--- ---
- 修正了多阶段作物成长素材计算逻辑 - 修正了多阶段作物成长素材计算逻辑
- 修正了作物数据库字段错乱的问题 - 修正了作物数据库字段错乱的问题
- 作物新增offset字段用于以后偏移坐标(尚未加入代码 TODO - 作物新增offset字段用于以后偏移坐标
## V1.3 ## V1.3
用户方面 用户方面

BIN
resource/db/plant-test.db Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB