Compare commits

...

5 Commits

Author SHA1 Message Date
c94db33b11 更新 ptimeparse 到 0.2.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-19 22:45:34 +08:00
67382a0c0a 在我写的模块采用更安全的 asyncio 锁写法
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-19 20:27:18 +08:00
fd4c9302c2 async with lock
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-19 20:24:47 +08:00
f30ad0cb7d 判定部分优化
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-19 18:48:10 +08:00
f7afe48680 精度修复
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-19 18:36:27 +08:00
5 changed files with 80 additions and 69 deletions

View File

@ -254,11 +254,10 @@ def _save_longtask_data(data: LongTaskModuleData):
@asynccontextmanager @asynccontextmanager
async def longtask_data(): async def longtask_data():
await longtask_lock.acquire() async with longtask_lock:
data = _load_longtask_data() data = _load_longtask_data()
yield data yield data
_save_longtask_data(data) _save_longtask_data(data)
longtask_lock.release()
async def create_longtask( async def create_longtask(

View File

@ -157,24 +157,24 @@ class IdiomGame:
""" """
跳过当前成语,选择下一个成语 跳过当前成语,选择下一个成语
""" """
await self.lock.acquire() async with self.lock:
self._skip_idiom_async(buff_score) self._skip_idiom_async()
self.lock.release() self.add_buff_score(buff_score)
return self.last_idiom return self.last_idiom
def _skip_idiom_async(self, buff_score: int = -100) -> str: def _skip_idiom_async(self) -> str:
self.last_idiom = secrets.choice(IdiomGame.ALL_IDIOMS) self.last_idiom = secrets.choice(IdiomGame.ALL_IDIOMS)
self.last_char = self.last_idiom[-1] self.last_char = self.last_idiom[-1]
self.add_buff_score(buff_score) if not self.is_nextable(self.last_char):
self._skip_idiom_async()
return self.last_idiom return self.last_idiom
async def try_verify_idiom(self, idiom: str, user_id: str) -> TryVerifyState: async def try_verify_idiom(self, idiom: str, user_id: str) -> TryVerifyState:
""" """
用户发送成语 用户发送成语
""" """
await self.lock.acquire() async with self.lock:
state = self._verify_idiom(idiom, user_id) state = self._verify_idiom(idiom, user_id)
self.lock.release()
return state return state
def is_nextable(self, last_char: str) -> bool: def is_nextable(self, last_char: str) -> bool:
@ -203,10 +203,12 @@ class IdiomGame:
return TryVerifyState.VERIFIED_BUT_NO_NEXT return TryVerifyState.VERIFIED_BUT_NO_NEXT
return TryVerifyState.VERIFIED return TryVerifyState.VERIFIED
def get_user_score(self, user_id: str) -> int: def get_user_score(self, user_id: str) -> float:
if user_id not in self.score_board: if user_id not in self.score_board:
return 0 return 0
return self.score_board[user_id]["score"] # 避免浮点数精度问题导致过长
handled_score = round(self.score_board[user_id]["score"], 1)
return handled_score
def add_score(self, user_id: str, score: int): def add_score(self, user_id: str, score: int):
if user_id not in self.score_board: if user_id not in self.score_board:
@ -377,7 +379,7 @@ async def end_game(event: BaseEvent, group_id: str):
result_text += ( result_text += (
f"{i + 1}. " f"{i + 1}. "
+ UniMessage().at(user_id) + UniMessage().at(user_id)
+ f": {info['score'] + instance.get_all_buff_score()}\n" + f": {round(info['score'] + instance.get_all_buff_score(), 1)}\n"
) )
await evt.send(await result_text.export()) await evt.send(await result_text.export())
instance.clear_score_board() instance.clear_score_board()

View File

@ -15,10 +15,10 @@ from nonebot.adapters.console.event import MessageEvent as ConsoleMessageEvent
from nonebot.adapters.discord import Bot as DiscordBot from nonebot.adapters.discord import Bot as DiscordBot
from nonebot.adapters.discord.event import MessageEvent as DiscordMessageEvent from nonebot.adapters.discord.event import MessageEvent as DiscordMessageEvent
from nonebot.adapters.onebot.v11 import Bot as OnebotV11Bot from nonebot.adapters.onebot.v11 import Bot as OnebotV11Bot
from nonebot.adapters.onebot.v11.event import \ from nonebot.adapters.onebot.v11.event import (
GroupMessageEvent as OnebotV11GroupMessageEvent GroupMessageEvent as OnebotV11GroupMessageEvent,
from nonebot.adapters.onebot.v11.event import \ )
MessageEvent as OnebotV11MessageEvent from nonebot.adapters.onebot.v11.event import MessageEvent as OnebotV11MessageEvent
from nonebot_plugin_alconna import UniMessage, UniMsg from nonebot_plugin_alconna import UniMessage, UniMsg
from pydantic import BaseModel from pydantic import BaseModel
@ -68,14 +68,14 @@ def save_notify_config(config: NotifyConfigFile):
async def notify_now(notify: Notify): async def notify_now(notify: Notify):
if notify.platform == 'console': if notify.platform == "console":
bot = [b for b in nonebot.get_bots().values() if isinstance(b, ConsoleBot)] bot = [b for b in nonebot.get_bots().values() if isinstance(b, ConsoleBot)]
if len(bot) != 1: if len(bot) != 1:
logger.warning(f"提醒未成功发送出去:{nonebot.get_bots()} {notify}") logger.warning(f"提醒未成功发送出去:{nonebot.get_bots()} {notify}")
return False return False
bot = bot[0] bot = bot[0]
await bot.send_private_message(notify.target, f"代办通知:{notify.notify_msg}") await bot.send_private_message(notify.target, f"代办通知:{notify.notify_msg}")
elif notify.platform == 'discord': elif notify.platform == "discord":
bot = [b for b in nonebot.get_bots().values() if isinstance(b, DiscordBot)] bot = [b for b in nonebot.get_bots().values() if isinstance(b, DiscordBot)]
if len(bot) != 1: if len(bot) != 1:
logger.warning(f"提醒未成功发送出去:{nonebot.get_bots()} {notify}") logger.warning(f"提醒未成功发送出去:{nonebot.get_bots()} {notify}")
@ -83,7 +83,7 @@ async def notify_now(notify: Notify):
bot = bot[0] bot = bot[0]
channel = await bot.create_DM(recipient_id=int(notify.target)) channel = await bot.create_DM(recipient_id=int(notify.target))
await bot.send_to(channel.id, f"代办通知:{notify.notify_msg}") await bot.send_to(channel.id, f"代办通知:{notify.notify_msg}")
elif notify.platform == 'qq': elif notify.platform == "qq":
bot = [b for b in nonebot.get_bots().values() if isinstance(b, OnebotV11Bot)] bot = [b for b in nonebot.get_bots().values() if isinstance(b, OnebotV11Bot)]
if len(bot) != 1: if len(bot) != 1:
logger.warning(f"提醒未成功发送出去:{nonebot.get_bots()} {notify}") logger.warning(f"提醒未成功发送出去:{nonebot.get_bots()} {notify}")
@ -92,17 +92,22 @@ async def notify_now(notify: Notify):
if notify.target_env is None: if notify.target_env is None:
await bot.send_private_msg( await bot.send_private_msg(
user_id=int(notify.target), user_id=int(notify.target),
message=cast(Any, await UniMessage.text(f"代办通知:{notify.notify_msg}").export( message=cast(
bot=bot, Any,
)), await UniMessage.text(f"代办通知:{notify.notify_msg}").export(
bot=bot,
),
),
) )
else: else:
await bot.send_group_msg( await bot.send_group_msg(
group_id=int(notify.target_env), group_id=int(notify.target_env),
message=cast(Any, message=cast(
await UniMessage().at( Any,
notify.target await UniMessage()
).text(f" 代办通知:{notify.notify_msg}").export(bot=bot) .at(notify.target)
.text(f" 代办通知:{notify.notify_msg}")
.export(bot=bot),
), ),
) )
else: else:
@ -127,15 +132,17 @@ def create_notify_task(notify: Notify, fail2remove: bool = True):
) )
res = await notify_now(notify) res = await notify_now(notify)
if fail2remove or res: if fail2remove or res:
await DATA_FILE_LOCK.acquire() async with DATA_FILE_LOCK:
cfg = load_notify_config() cfg = load_notify_config()
cfg.notifies = [n for n in cfg.notifies if n.get_str() != notify.get_str()] cfg.notifies = [
if not res: n for n in cfg.notifies if n.get_str() != notify.get_str()
cfg.unsent.append(notify) ]
save_notify_config(cfg) if not res:
DATA_FILE_LOCK.release() cfg.unsent.append(notify)
save_notify_config(cfg)
else: else:
pass pass
return asynkio.create_task(mission()) return asynkio.create_task(mission())
@ -155,7 +162,8 @@ async def _(msg: UniMsg, mEvt: Event):
notify_time, notify_text = segments notify_time, notify_text = segments
# target_time = get_target_time(notify_time) # target_time = get_target_time(notify_time)
try: try:
target_time = ptimeparse.parse(notify_time) # target_time = ptimeparse.parse(notify_time)
target_time = ptimeparse.Parser().parse(notify_time)
logger.info(f"{notify_time} 解析出了时间:{target_time}") logger.info(f"{notify_time} 解析出了时间:{target_time}")
except Exception: except Exception:
logger.info(f"无法从 {notify_time} 中解析出时间") logger.info(f"无法从 {notify_time} 中解析出时间")
@ -201,8 +209,12 @@ async def _(msg: UniMsg, mEvt: Event):
save_notify_config(cfg) save_notify_config(cfg)
DATA_FILE_LOCK.release() DATA_FILE_LOCK.release()
await evt.send(await UniMessage().at(mEvt.get_user_id()).text( await evt.send(
f" 了解啦!将会在 {notify.notify_time} 提醒你哦~").export()) await UniMessage()
.at(mEvt.get_user_id())
.text(f" 了解啦!将会在 {notify.notify_time} 提醒你哦~")
.export()
)
logger.info(f"创建了一条于 {notify.notify_time} 的代办提醒") logger.info(f"创建了一条于 {notify.notify_time} 的代办提醒")
@ -253,8 +265,8 @@ async def _():
logger.info("所有的代办提醒 Task 都已经退出了") logger.info("所有的代办提醒 Task 都已经退出了")
for sig in (signal.SIGINT, signal.SIGTERM): for sig in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(sig, functools.partial( loop.add_signal_handler(
asynkio.create_task, shutdown(sig) sig, functools.partial(asynkio.create_task, shutdown(sig))
)) )
await asynkio.gather(*ASYNK_TASKS) await asynkio.gather(*ASYNK_TASKS)

8
poetry.lock generated
View File

@ -2402,14 +2402,14 @@ reference = "mirrors"
[[package]] [[package]]
name = "ptimeparse" name = "ptimeparse"
version = "0.1.2" version = "0.2.0"
description = "一个用于解析中文的时间表达的库" description = "一个用于解析中文的时间表达的库"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "ptimeparse-0.1.2-py3-none-any.whl", hash = "sha256:0eea791396e53b63330fadb40d9f0a2e6272bd5467246f10d1d6971bc606edff"}, {file = "ptimeparse-0.2.0-py3-none-any.whl", hash = "sha256:57055f8fd99fb69e19deac3b8a5c7ac91af86c7ac09781632e9abf318df0d6d2"},
{file = "ptimeparse-0.1.2.tar.gz", hash = "sha256:658be90a3cc2994c09c4ea2f276d257e7eb84bc330be79950baefe32b19779a2"}, {file = "ptimeparse-0.2.0.tar.gz", hash = "sha256:867c265f2e157fe4d793d20fe9c449b8ede5c855f336d7e6b2eb78551e622766"},
] ]
[package.source] [package.source]
@ -3807,4 +3807,4 @@ reference = "mirrors"
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.12,<4.0" python-versions = ">=3.12,<4.0"
content-hash = "d6a325b769fb3ed207c1a8891e65ea20bae20166fa281d6fa620faf54ad15bd8" content-hash = "02530953efe65da1a788845cd43f8856be62db5bfb59de691cad813f57bab25e"

View File

@ -2,30 +2,28 @@
name = "konabot" name = "konabot"
version = "0.1.0" version = "0.1.0"
description = "在 MTTU 内部使用的 bot" description = "在 MTTU 内部使用的 bot"
authors = [ authors = [{ name = "passthem", email = "Passthem183@gmail.com" }]
{name = "passthem",email = "Passthem183@gmail.com"}
]
readme = "README.md" readme = "README.md"
requires-python = ">=3.12,<4.0" requires-python = ">=3.12,<4.0"
dependencies = [ dependencies = [
"nonebot2[all] (>=2.4.3,<3.0.0)", "nonebot2[all] (>=2.4.3,<3.0.0)",
"nonebot-adapter-onebot (>=2.4.6,<3.0.0)", "nonebot-adapter-onebot (>=2.4.6,<3.0.0)",
"nonebot-adapter-console (>=0.9.0,<0.10.0)", "nonebot-adapter-console (>=0.9.0,<0.10.0)",
"nonebot-adapter-discord (>=0.1.8,<0.2.0)", "nonebot-adapter-discord (>=0.1.8,<0.2.0)",
"nonebot-adapter-minecraft (>=1.5.2,<2.0.0)", "nonebot-adapter-minecraft (>=1.5.2,<2.0.0)",
"nonebot-plugin-alconna (>=0.59.4,<0.60.0)", "nonebot-plugin-alconna (>=0.59.4,<0.60.0)",
"nonebot-plugin-apscheduler (>=0.5.0,<0.6.0)", "nonebot-plugin-apscheduler (>=0.5.0,<0.6.0)",
"requests (>=2.32.5,<3.0.0)", "requests (>=2.32.5,<3.0.0)",
"beautifulsoup4 (>=4.13.5,<5.0.0)", "beautifulsoup4 (>=4.13.5,<5.0.0)",
"lxml (>=6.0.2,<7.0.0)", "lxml (>=6.0.2,<7.0.0)",
"pillow (>=11.3.0,<12.0.0)", "pillow (>=11.3.0,<12.0.0)",
"imagetext-py (>=2.2.0,<3.0.0)", "imagetext-py (>=2.2.0,<3.0.0)",
"opencv-python-headless (>=4.12.0.88,<5.0.0.0)", "opencv-python-headless (>=4.12.0.88,<5.0.0.0)",
"returns (>=0.26.0,<0.27.0)", "returns (>=0.26.0,<0.27.0)",
"ptimeparse (>=0.1.1,<0.2.0)", "ptimeparse (>=0.1.1,<1.0.0)",
"skia-python (>=138.0,<139.0)", "skia-python (>=138.0,<139.0)",
"nonebot-plugin-analysis-bilibili (>=2.8.1,<3.0.0)", "nonebot-plugin-analysis-bilibili (>=2.8.1,<3.0.0)",
"qrcode (>=8.2,<9.0)", "qrcode (>=8.2,<9.0)",
] ]
[build-system] [build-system]
@ -43,4 +41,4 @@ url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
priority = "primary" priority = "primary"
[tool.poetry.dependencies] [tool.poetry.dependencies]
ptimeparse = {source = "pt-gitea-pypi"} ptimeparse = { source = "pt-gitea-pypi" }