Compare commits

...

7 Commits

Author SHA1 Message Date
4305548ab5 submodule
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-13 22:53:44 +08:00
99382a3bf5 Merge branch 'master' of https://gitea.service.jazzwhom.top/mttu-developers/konabot
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-13 22:48:17 +08:00
92e43785bf submodule 2025-10-13 22:46:30 +08:00
fc5b11c5e8 调整 notify 的强制退出
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-10-13 22:16:50 +08:00
0ec66988fa 更新投票存储位置
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-13 22:05:21 +08:00
e5c3081c22 Merge branch 'master' of https://gitea.service.jazzwhom.top/mttu-developers/konabot
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-13 22:02:44 +08:00
14b356120a 成语接龙 2025-10-13 22:02:33 +08:00
6 changed files with 360467 additions and 22 deletions

View File

@ -10,6 +10,10 @@ trigger:
- master - master
steps: steps:
- name: submodules
image: alpine/git
commands:
- git submodule update --init --recursive
- name: 构建 Docker 镜像 - name: 构建 Docker 镜像
image: plugins/docker:latest image: plugins/docker:latest
privileged: true privileged: true
@ -50,6 +54,10 @@ trigger:
- tag - tag
steps: steps:
- name: submodules
image: alpine/git
commands:
- git submodule update --init --recursive
- name: 构建并推送 Release Docker 镜像 - name: 构建并推送 Release Docker 镜像
image: plugins/docker:latest image: plugins/docker:latest
privileged: true privileged: true

360393
assets/lexicon/common.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -12,3 +12,10 @@ DOCS_PATH_MAN1 = DOCS_PATH / "user"
DOCS_PATH_MAN3 = DOCS_PATH / "lib" DOCS_PATH_MAN3 = DOCS_PATH / "lib"
DOCS_PATH_MAN7 = DOCS_PATH / "concepts" DOCS_PATH_MAN7 = DOCS_PATH / "concepts"
DOCS_PATH_MAN8 = DOCS_PATH / "sys" DOCS_PATH_MAN8 = DOCS_PATH / "sys"
if not DATA_PATH.exists():
DATA_PATH.mkdir()
if not LOG_PATH.exists():
LOG_PATH.mkdir()

View File

@ -27,6 +27,14 @@ def init_lexicon():
# 词语大表 # 词语大表
with open(ASSETS_PATH / "lexicon" / "ci.json", "r", encoding="utf-8") as f: with open(ASSETS_PATH / "lexicon" / "ci.json", "r", encoding="utf-8") as f:
ALL_WORDS = json.load(f) ALL_WORDS = json.load(f)
COMMON_WORDS = []
# 读取 COMMON 词语大表
with open(ASSETS_PATH / "lexicon" / "common.txt", "r", encoding="utf-8") as f:
for line in f:
word = line.strip()
if len(word) == 4:
COMMON_WORDS.append(word)
# 读取 THUOCL 成语库 # 读取 THUOCL 成语库
with open(ASSETS_PATH / "lexicon" / "THUOCL" / "data" / "THUOCL_chengyu.txt", "r", encoding="utf-8") as f: with open(ASSETS_PATH / "lexicon" / "THUOCL" / "data" / "THUOCL_chengyu.txt", "r", encoding="utf-8") as f:
@ -49,7 +57,7 @@ def init_lexicon():
ALL_IDIOMS = list(set(ALL_IDIOMS)) # 去重 ALL_IDIOMS = list(set(ALL_IDIOMS)) # 去重
# 其他四字词语表,仅表示可以有这个词 # 其他四字词语表,仅表示可以有这个词
ALL_WORDS = [word for word in ALL_WORDS if len(word) == 4] + THUOCL_WORDS ALL_WORDS = [word for word in ALL_WORDS if len(word) == 4] + THUOCL_WORDS + COMMON_WORDS
ALL_WORDS = list(set(ALL_WORDS)) # 去重 ALL_WORDS = list(set(ALL_WORDS)) # 去重
# 根据成语大表,划分出成语首字字典 # 根据成语大表,划分出成语首字字典
@ -109,7 +117,7 @@ async def _(event: BaseEvent):
# 按分数排序,名字用 at 的方式 # 按分数排序,名字用 at 的方式
sorted_score = sorted(SCORE_BOARD.items(), key=lambda x: x[1]["score"], reverse=True) sorted_score = sorted(SCORE_BOARD.items(), key=lambda x: x[1]["score"], reverse=True)
for i, (user_id, info) in enumerate(sorted_score): for i, (user_id, info) in enumerate(sorted_score):
result_text += f"{i+1}. " + UniMessage().at(user_id) + f" - {info['score']}\n" result_text += f"{i+1}. " + UniMessage().at(user_id) + f": {info['score']}\n"
await evt.send(await result_text.export()) await evt.send(await result_text.export())
# 重置分数板 # 重置分数板
SCORE_BOARD = {} SCORE_BOARD = {}

View File

@ -1,16 +1,19 @@
import json, time import json, time
from nonebot_plugin_alconna import (Alconna, Args, Field, MultiVar, UniMessage, from nonebot_plugin_alconna import Alconna, Args, Field, MultiVar, on_alconna
on_alconna)
from nonebot_plugin_alconna.uniseg import UniMsg, At, Reply
from nonebot.adapters.onebot.v11 import Event from nonebot.adapters.onebot.v11 import Event
poll_json_path = "assets/json/poll.json" from konabot.common.path import ASSETS_PATH, DATA_PATH
poll_file = open(poll_json_path,"r",encoding="utf-8")
poll_list_raw = poll_file.read() POLL_TEMPLATE_FILE = ASSETS_PATH / "json" / "poll.json"
poll_file.close() POLL_DATA_FILE = DATA_PATH / "poll.json"
poll_list = json.loads(poll_list_raw)['poll']
if not POLL_DATA_FILE.exists():
POLL_DATA_FILE.write_bytes(POLL_TEMPLATE_FILE.read_bytes())
poll_list = json.loads(POLL_DATA_FILE.read_text())['poll']
async def createpoll(title,qqid,options): async def createpoll(title,qqid,options):
polllength = len(poll_list) polllength = len(poll_list)
@ -44,8 +47,11 @@ def getpolldata(pollid_or_title):
return [thepoll,polnum] return [thepoll,polnum]
def writeback(): def writeback():
file = open(poll_json_path,"w",encoding="utf-8") # file = open(poll_json_path,"w",encoding="utf-8")
json.dump({'poll':poll_list},file,ensure_ascii=False,sort_keys=True) # json.dump({'poll':poll_list},file,ensure_ascii=False,sort_keys=True)
POLL_DATA_FILE.write_text(json.dumps({
'poll': poll_list,
}, ensure_ascii=False, sort_keys=True))
async def pollvote(polnum,optionnum,qqnum): async def pollvote(polnum,optionnum,qqnum):
optiond = poll_list[polnum]["polldata"] optiond = poll_list[polnum]["polldata"]
@ -157,4 +163,4 @@ async def _(saying: list, event: Event):
# 写入项目 # 写入项目
else: else:
await pollvote(polnum,optionnum,event.get_user_id()) await pollvote(polnum,optionnum,event.get_user_id())
await viewpoll.send("投票成功!你投给了 "+saying[1]) await viewpoll.send("投票成功!你投给了 "+saying[1])

View File

@ -1,8 +1,10 @@
import asyncio import asyncio as asynkio
import datetime import datetime
import functools
from pathlib import Path from pathlib import Path
from typing import Any, Literal, cast from typing import Any, Literal, cast
import signal
import nonebot import nonebot
import ptimeparse import ptimeparse
from loguru import logger from loguru import logger
@ -24,7 +26,9 @@ evt = on_message()
(Path(__file__).parent.parent.parent.parent / "data").mkdir(exist_ok=True) (Path(__file__).parent.parent.parent.parent / "data").mkdir(exist_ok=True)
DATA_FILE_PATH = Path(__file__).parent.parent.parent.parent / "data" / "notify.json" DATA_FILE_PATH = Path(__file__).parent.parent.parent.parent / "data" / "notify.json"
DATA_FILE_LOCK = asyncio.Lock() DATA_FILE_LOCK = asynkio.Lock()
ASYNK_TASKS: set[asynkio.Task[Any]] = set()
class Notify(BaseModel): class Notify(BaseModel):
@ -111,7 +115,11 @@ def create_notify_task(notify: Notify, fail2remove: bool = True):
async def mission(): async def mission():
begin_time = datetime.datetime.now() begin_time = datetime.datetime.now()
if begin_time < notify.notify_time: if begin_time < notify.notify_time:
await asyncio.sleep((notify.notify_time - begin_time).total_seconds()) try:
await asynkio.sleep((notify.notify_time - begin_time).total_seconds())
except asynkio.CancelledError:
logger.debug("代办提醒被信号中止,任务退出")
return
else: else:
logger.warning( logger.warning(
f"期望在 {notify.notify_time} 在平台 {notify.platform} {notify.target_env}" f"期望在 {notify.notify_time} 在平台 {notify.platform} {notify.target_env}"
@ -128,7 +136,7 @@ def create_notify_task(notify: Notify, fail2remove: bool = True):
DATA_FILE_LOCK.release() DATA_FILE_LOCK.release()
else: else:
pass pass
return asyncio.create_task(mission()) return asynkio.create_task(mission())
@evt.handle() @evt.handle()
@ -214,11 +222,11 @@ async def _():
DELTA = 2 DELTA = 2
logger.info(f"第一次探测到 Bot 连接,等待 {DELTA} 秒后开始通知") logger.info(f"第一次探测到 Bot 连接,等待 {DELTA} 秒后开始通知")
await asyncio.sleep(DELTA) await asynkio.sleep(DELTA)
await DATA_FILE_LOCK.acquire() await DATA_FILE_LOCK.acquire()
tasks: set[asyncio.Task[Any]] = set() # tasks: set[asynkio.Task[Any]] = set()
cfg = load_notify_config() cfg = load_notify_config()
if cfg.version == 1: if cfg.version == 1:
logger.info("将配置文件的版本升级为 2") logger.info("将配置文件的版本升级为 2")
@ -227,11 +235,26 @@ async def _():
counter = 0 counter = 0
for notify in [*cfg.notifies]: for notify in [*cfg.notifies]:
task = create_notify_task(notify, fail2remove=False) task = create_notify_task(notify, fail2remove=False)
tasks.add(task) ASYNK_TASKS.add(task)
task.add_done_callback(lambda self: tasks.remove(self)) task.add_done_callback(lambda self: ASYNK_TASKS.remove(self))
counter += 1 counter += 1
logger.info(f"成功创建了 {counter} 条代办事项") logger.info(f"成功创建了 {counter} 条代办事项")
save_notify_config(cfg) save_notify_config(cfg)
DATA_FILE_LOCK.release() DATA_FILE_LOCK.release()
await asyncio.gather(*tasks) loop = asynkio.get_running_loop()
# 解决 asynk task 没有被 cancel 的问题
async def shutdown(sig: signal.Signals):
logger.info(f"收到 {sig.name} 指令,正在关闭所有的东西")
for task in ASYNK_TASKS:
task.cancel()
await asynkio.gather(*ASYNK_TASKS, return_exceptions=True)
logger.info("所有的代办提醒 Task 都已经退出了")
for sig in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(sig, functools.partial(
asynkio.create_task, shutdown(sig)
))
await asynkio.gather(*ASYNK_TASKS)