From 0c942d98065655f6d3242f1cda7ab02ec9ff6348 Mon Sep 17 00:00:00 2001 From: snowy Date: Tue, 20 Aug 2024 06:20:41 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=B7=BB=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=E4=B8=BB=E6=B5=81=E6=A1=86=E6=9E=B6=E7=9A=84=E6=B6=88=E6=81=AF?= =?UTF-8?q?io=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vuepress/theme.ts | 10 +- liteyuki/__init__.py | 7 +- liteyuki/bot/__init__.py | 1 + liteyuki/comm/channel.py | 4 +- liteyuki/comm/storage.py | 10 +- liteyuki/message/event.py | 44 +++++- liteyuki/message/matcher.py | 50 ++++++- liteyuki/message/on.py | 33 ++++- liteyuki/message/permission.py | 20 --- liteyuki/message/rule.py | 25 +++- liteyuki/plugin/load.py | 2 +- liteyuki/plugin/model.py | 8 +- src/liteyuki_main/core.py | 135 ++---------------- src/liteyuki_plugins/hello_liteyuki.py | 24 ++++ .../liteyukibot_plugin_nonebot/__init__.py | 2 +- .../liteyuki_crt_utils/__init__.py | 4 +- src/nonebot_plugins/liteyuki_pacman/npm.py | 2 +- .../liteyuki_statistics/data_source.py | 2 + .../liteyuki_statistics/stat_matchers.py | 4 +- src/nonebot_plugins/to_liteyuki.py | 40 ++++++ .../liteyuki_crt/templates/js/crt_route.js | 2 +- .../templates/js/weather_now.js | 2 +- src/utils/base/data.py | 8 +- src/utils/base/data_manager.py | 2 +- src/utils/event/get_info.py | 10 +- src/utils/message/message.py | 8 +- 26 files changed, 267 insertions(+), 192 deletions(-) delete mode 100644 liteyuki/message/permission.py create mode 100644 src/liteyuki_plugins/hello_liteyuki.py create mode 100644 src/nonebot_plugins/to_liteyuki.py diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts index f33f7bf9..249938d9 100644 --- a/docs/.vuepress/theme.ts +++ b/docs/.vuepress/theme.ts @@ -163,23 +163,23 @@ export default hopeTheme({ // src: "/assets/icon/chrome-mask-512.png", // sizes: "512x512", // purpose: "maskable", - // type_: "image/png", + // type: "image/png", // }, // { // src: "/assets/icon/chrome-mask-192.png", // sizes: "192x192", // purpose: "maskable", - // type_: "image/png", + // type: "image/png", // }, // { // src: "/assets/icon/chrome-512.png", // sizes: "512x512", - // type_: "image/png", + // type: "image/png", // }, // { // src: "/assets/icon/chrome-192.png", // sizes: "192x192", - // type_: "image/png", + // type: "image/png", // }, // ], // shortcuts: [ @@ -192,7 +192,7 @@ export default hopeTheme({ // src: "/assets/icon/guide-maskable.png", // sizes: "192x192", // purpose: "maskable", - // type_: "image/png", + // type: "image/png", // }, // ], // }, diff --git a/liteyuki/__init__.py b/liteyuki/__init__.py index 1e7aaa3c..3bde5ff4 100644 --- a/liteyuki/__init__.py +++ b/liteyuki/__init__.py @@ -20,7 +20,6 @@ from liteyuki.log import ( logger ) - __all__ = [ "LiteyukiBot", "get_bot", @@ -34,6 +33,8 @@ __all__ = [ "logger", ] -__version__ = "6.3.7" # 测试版本号 - +__version__ = "6.3.8" # 测试版本号 +# 6.3.8 +# 1. 初步添加对聊天的支持 +# 2. 优化了通道的性能 diff --git a/liteyuki/bot/__init__.py b/liteyuki/bot/__init__.py index bf2d011d..986f7201 100644 --- a/liteyuki/bot/__init__.py +++ b/liteyuki/bot/__init__.py @@ -10,6 +10,7 @@ from typing import Any, Optional from liteyuki.bot.lifespan import (LIFESPAN_FUNC, Lifespan) from liteyuki.comm.channel import get_channel +from liteyuki.comm.storage import shared_memory from liteyuki.core.manager import ProcessManager from liteyuki.log import init_log, logger from liteyuki.plugin import load_plugin diff --git a/liteyuki/comm/channel.py b/liteyuki/comm/channel.py index 17bec928..3355be08 100644 --- a/liteyuki/comm/channel.py +++ b/liteyuki/comm/channel.py @@ -60,7 +60,7 @@ class Channel(Generic[T]): elif type_check: if self._get_generic_type() is None: - raise TypeError("Type hint is required for enforcing type_ check.") + raise TypeError("Type hint is required for enforcing type check.") self.type_check = type_check def _get_generic_type(self) -> Optional[type]: @@ -158,7 +158,7 @@ class Channel(Generic[T]): async def wrapper(data: T) -> Any: if filter_func is not None: if is_coroutine_callable(filter_func): - if not (await filter_func(data)): # type_: ignore + if not (await filter_func(data)): # type: ignore return else: if not filter_func(data): diff --git a/liteyuki/comm/storage.py b/liteyuki/comm/storage.py index 7fb4f81f..e3f34aa5 100644 --- a/liteyuki/comm/storage.py +++ b/liteyuki/comm/storage.py @@ -13,9 +13,9 @@ from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable, run_coroutine if IS_MAIN_PROCESS: _locks = {} -_on_main_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type_: ignore +_on_main_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type: ignore """主进程订阅者接收函数""" -_on_sub_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type_: ignore +_on_sub_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type: ignore """子进程订阅者接收函数""" @@ -169,7 +169,7 @@ class KeyValueStore: ( "publish", { - "channel_": channel_, + "channel": channel_, "data" : data } ) @@ -234,7 +234,7 @@ class KeyValueStore: data = self.active_chan.receive() if data[0] == "publish": # 运行主进程订阅函数 - self.run_subscriber_receive_funcs(data[1]["channel_"], data[1]["data"]) + self.run_subscriber_receive_funcs(data[1]["channel"], data[1]["data"]) # 推送给子进程 self.publish_channel.send(data) else: @@ -242,7 +242,7 @@ class KeyValueStore: data = self.publish_channel.receive() if data[0] == "publish": # 运行子进程订阅函数 - self.run_subscriber_receive_funcs(data[1]["channel_"], data[1]["data"]) + self.run_subscriber_receive_funcs(data[1]["channel"], data[1]["data"]) class GlobalKeyValueStore: diff --git a/liteyuki/message/event.py b/liteyuki/message/event.py index ae248f6c..834e6e9b 100644 --- a/liteyuki/message/event.py +++ b/liteyuki/message/event.py @@ -8,9 +8,49 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved @File : event.py @Software: PyCharm """ +from typing import Any + +from liteyuki.comm.storage import shared_memory class Event: - def __init__(self, type_: str, data: dict): - self.type = type_ + def __init__(self, type: str, data: dict[str, Any], bot_id: str, session_id: str, session_type: str, receive_channel: str = "event_to_nonebot"): + """ + 事件 + Args: + type: 类型 + data: 数据 + bot_id: 机器人ID + session_id: 会话ID + session_type: 会话类型 + receive_channel: 接收频道 + """ + self.type = type self.data = data + self.bot_id = bot_id + self.session_id = session_id + self.session_type = session_type + self.receive_channel = receive_channel + + def __str__(self): + return f"Event(type={self.type}, data={self.data}, bot_id={self.bot_id}, session_id={self.session_id}, session_type={self.session_type})" + + def reply(self, message: str | dict[str, Any]): + """ + 回复消息 + Args: + message: + Returns: + """ + to_nonebot_event = Event( + type=self.session_type, + data={ + "message": message + }, + bot_id=self.bot_id, + session_id=self.session_id, + session_type=self.session_type, + receive_channel="_" + ) + print(to_nonebot_event) + shared_memory.publish(self.receive_channel, to_nonebot_event) diff --git a/liteyuki/message/matcher.py b/liteyuki/message/matcher.py index 59fa9ad9..a29c94ab 100644 --- a/liteyuki/message/matcher.py +++ b/liteyuki/message/matcher.py @@ -8,7 +8,55 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved @File : matcher.py @Software: PyCharm """ +import traceback +from typing import Any, TypeAlias, Callable, Coroutine + +from liteyuki import Event +from liteyuki.message.rule import Rule + +EventHandler: TypeAlias = Callable[[Event], Coroutine[None, None, Any]] class Matcher: - pass + def __init__(self, rule: Rule, priority: int, block: bool): + """ + 匹配器 + Args: + rule: 规则 + priority: 优先级 >= 0 + block: 是否阻断后续优先级更低的匹配器 + """ + self.rule = rule + self.priority = priority + self.block = block + self.handlers: list[EventHandler] = [] + + def __str__(self): + return f"Matcher(rule={self.rule}, priority={self.priority}, block={self.block})" + + def handle(self, handler: EventHandler) -> EventHandler: + """ + 添加处理函数,装饰器 + Args: + handler: + Returns: + EventHandler + """ + self.handlers.append(handler) + return handler + + async def run(self, event: Event) -> None: + """ + 运行处理函数 + Args: + event: + Returns: + """ + if not await self.rule(event): + return + + for handler in self.handlers: + try: + await handler(event) + except Exception: + traceback.print_exc() diff --git a/liteyuki/message/on.py b/liteyuki/message/on.py index a5bba8c5..8b1259c4 100644 --- a/liteyuki/message/on.py +++ b/liteyuki/message/on.py @@ -8,11 +8,40 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved @File : on.py @Software: PyCharm """ +import threading +from queue import Queue + +from liteyuki.comm.storage import shared_memory +from liteyuki.log import logger +from liteyuki.message.event import Event from liteyuki.message.matcher import Matcher +from liteyuki.message.rule import Rule _matcher_list: list[Matcher] = [] +_queue: Queue = Queue() -def on_message(permission) -> Matcher: - pass +@shared_memory.on_subscriber_receive("event_to_liteyuki") +async def _(event: Event): + current_priority = -1 + for i, matcher in enumerate(_matcher_list): + logger.info(f"Running matcher {matcher} for event: {event}") + await matcher.run(event) + # 同优先级不阻断,不同优先级阻断 + if current_priority != matcher.priority: + current_priority = matcher.priority + if matcher.block: + break + + +def on_message(rule: Rule = Rule(), priority: int = 0, block: bool = True) -> Matcher: + matcher = Matcher(rule, priority, block) + # 按照优先级插入 + for i, m in enumerate(_matcher_list): + if m.priority < matcher.priority: + _matcher_list.insert(i, matcher) + break + else: + _matcher_list.append(matcher) + return matcher diff --git a/liteyuki/message/permission.py b/liteyuki/message/permission.py deleted file mode 100644 index 85c92455..00000000 --- a/liteyuki/message/permission.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved - -@Time : 2024/8/19 下午10:55 -@Author : snowykami -@Email : snowykami@outlook.com -@File : permission.py -@Software: PyCharm -""" - -from typing import Callable, Coroutine, TypeAlias - -PERMISSION_HANDLER: TypeAlias = Callable[[str], bool | Coroutine[None, None, bool]] - - -class Permission: - def __init__(self, handler: PERMISSION_HANDLER): - self.handler = handler - diff --git a/liteyuki/message/rule.py b/liteyuki/message/rule.py index 09eab1ec..3c1443ea 100644 --- a/liteyuki/message/rule.py +++ b/liteyuki/message/rule.py @@ -7,4 +7,27 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved @Email : snowykami@outlook.com @File : rule.py @Software: PyCharm -""" \ No newline at end of file +""" + +from typing import Optional, TypeAlias, Callable, Coroutine + +from liteyuki.message.event import Event + +RuleHandler: TypeAlias = Callable[[Event], Coroutine[None, None, bool]] +"""规则函数签名""" + + +class Rule: + def __init__(self, handler: Optional[RuleHandler] = None): + self.handler = handler + + def __or__(self, other: "Rule") -> "Rule": + return Rule(lambda event: self.handler(event) or other.handler(event)) + + def __and__(self, other: "Rule") -> "Rule": + return Rule(lambda event: self.handler(event) and other.handler(event)) + + async def __call__(self, event: Event) -> bool: + if self.handler is None: + return True + return await self.handler(event) diff --git a/liteyuki/plugin/load.py b/liteyuki/plugin/load.py index b60328cd..f944742a 100644 --- a/liteyuki/plugin/load.py +++ b/liteyuki/plugin/load.py @@ -117,7 +117,7 @@ def format_display_name(display_name: str, plugin_type: PluginType) -> str: match plugin_type: case PluginType.APPLICATION: color = "m" - case PluginType.IMPLEMENTATION: + case PluginType.TEST: color = "g" case PluginType.MODULE: color = "e" diff --git a/liteyuki/plugin/model.py b/liteyuki/plugin/model.py index 644a3d1f..cb066c37 100644 --- a/liteyuki/plugin/model.py +++ b/liteyuki/plugin/model.py @@ -25,15 +25,15 @@ class PluginType(Enum): SERVICE = "service" """服务端:例如AI绘画后端""" - IMPLEMENTATION = "implementation" - """实现端:例如与聊天平台的协议实现""" - MODULE = "module" """模块:导出对象给其他插件使用""" UNCLASSIFIED = "unclassified" """未分类:默认值""" + TEST = "test" + """测试:测试插件""" + class PluginMetadata(BaseModel): """ @@ -47,7 +47,7 @@ class PluginMetadata(BaseModel): 插件描述 usage: str 插件使用方法 - type_: str + type: str 插件类型 author: str 插件作者 diff --git a/src/liteyuki_main/core.py b/src/liteyuki_main/core.py index 78515fbc..4c02a16a 100644 --- a/src/liteyuki_main/core.py +++ b/src/liteyuki_main/core.py @@ -1,20 +1,21 @@ -import base64 import time -from typing import Any, AnyStr +from typing import AnyStr + +import time +from typing import AnyStr import nonebot import pip -from nonebot import Bot, get_driver, require +from nonebot import get_driver, require from nonebot.adapters import onebot, satori from nonebot.adapters.onebot.v11 import Message, unescape -from nonebot.exception import MockApiException from nonebot.internal.matcher import Matcher from nonebot.permission import SUPERUSER # from src.liteyuki.core import Reloader from src.utils import event as event_utils, satori_utils -from src.utils.base.config import get_config, load_from_yaml -from src.utils.base.data_manager import StoredConfig, TempConfig, common_db +from src.utils.base.config import get_config +from src.utils.base.data_manager import TempConfig, common_db from src.utils.base.language import get_user_lang from src.utils.base.ly_typing import T_Bot, T_MessageEvent from src.utils.message.message import MarkdownMessage as md, broadcast_to_superusers @@ -24,13 +25,11 @@ from ..utils.base.ly_function import get_function require("nonebot_plugin_alconna") require("nonebot_plugin_apscheduler") -from nonebot_plugin_alconna import on_alconna, Alconna, Args, Subcommand, Arparma, MultiVar +from nonebot_plugin_alconna import on_alconna, Alconna, Args, Arparma, MultiVar from nonebot_plugin_apscheduler import scheduler driver = get_driver() -markdown_image = common_db.where_one(StoredConfig(), default=StoredConfig()).config.get("markdown_image", False) - @on_alconna( command=Alconna( @@ -87,7 +86,7 @@ async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent): "reload_bot_id" : bot.self_id, "reload_session_type": event_utils.get_message_type(event), "reload_session_id" : (event.group_id if event.message_type == "group" else event.user_id) - if not isinstance(event, satori.event.Event) else event.chan_active.id, + if not isinstance(event, satori.event.Event) else event.chan_active.id, "delta_time" : 0 } ) @@ -96,90 +95,6 @@ async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent): reload() -@on_alconna( - aliases={"配置"}, - command=Alconna( - "config", - Subcommand( - "set", - Args["key", str]["value", Any], - alias=["设置"], - - ), - Subcommand( - "get", - Args["key", str, None], - alias=["查询", "获取"] - ), - Subcommand( - "remove", - Args["key", str], - alias=["删除"] - ) - ), - permission=SUPERUSER -).handle() -# Satori OK -async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, matcher: Matcher): - ulang = get_user_lang(str(event_utils.get_user_id(event))) - stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig()) - if result.subcommands.get("set"): - key, value = result.subcommands.get("set").args.get("key"), result.subcommands.get("set").args.get("value") - try: - value = eval(value) - except: - pass - stored_config.config[key] = value - common_db.save(stored_config) - await matcher.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}") - elif result.subcommands.get("get"): - key = result.subcommands.get("get").args.get("key") - file_config = load_from_yaml("config.yml") - reply = f"{ulang.get('liteyuki.current_config')}" - if key: - reply += f"```dotenv\n{key}={file_config.get(key, stored_config.config.get(key))}\n```" - else: - reply = f"{ulang.get('liteyuki.current_config')}" - reply += f"\n{ulang.get('liteyuki.static_config')}\n```dotenv" - for k, v in file_config.items(): - reply += f"\n{k}={v}" - reply += "\n```" - if len(stored_config.config) > 0: - reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv" - for k, v in stored_config.config.items(): - reply += f"\n{k}={v} {type(v)}" - reply += "\n```" - await md.send_md(reply, bot, event=event) - elif result.subcommands.get("remove"): - key = result.subcommands.get("remove").args.get("key") - if key in stored_config.config: - stored_config.config.pop(key) - common_db.save(stored_config) - await matcher.finish(f"{ulang.get('liteyuki.config_remove_success', KEY=key)}") - else: - await matcher.finish(f"{ulang.get('liteyuki.invalid_command', TEXT=key)}") - - -@on_alconna( - aliases={"切换图片模式"}, - command=Alconna( - "switch-image-mode" - ), - permission=SUPERUSER -).handle() -# Satori OK -async def _(event: T_MessageEvent, matcher: Matcher): - global markdown_image - # 切换图片模式,False以图片形式发送,True以markdown形式发送 - ulang = get_user_lang(str(event_utils.get_user_id(event))) - stored_config: StoredConfig = common_db.where_one(StoredConfig(), default=StoredConfig()) - stored_config.config["markdown_image"] = not stored_config.config.get("markdown_image", False) - markdown_image = stored_config.config["markdown_image"] - common_db.save(stored_config) - await matcher.finish( - ulang.get("liteyuki.image_mode_on" if stored_config.config["markdown_image"] else "liteyuki.image_mode_off")) - - @on_alconna( command=Alconna( "liteyuki-docs", @@ -285,38 +200,6 @@ async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher await matcher.finish(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}") -# system hook -@Bot.on_calling_api # 图片模式检测 -async def test_for_md_image(bot: T_Bot, api: str, data: dict): - # 截获大图发送,转换为markdown发送 - if api in ["send_msg", "send_private_msg", "send_group_msg"] and markdown_image and data.get( - "user_id") != bot.self_id: - if api == "send_msg" and data.get("message_type") == "private" or api == "send_private_msg": - session_type = "private" - session_id = data.get("user_id") - elif api == "send_msg" and data.get("message_type") == "group" or api == "send_group_msg": - session_type = "group" - session_id = data.get("group_id") - else: - return - if len(data.get("message", [])) == 1 and data["message"][0].get("type_") == "image": - file: str = data["message"][0].data.get("file") - # file:// http:// base64:// - if file.startswith("http"): - result = await md.send_md(await md.image_async(file), bot, message_type=session_type, - session_id=session_id) - elif file.startswith("file"): - file = file.replace("file://", "") - result = await md.send_image(open(file, "rb").read(), bot, message_type=session_type, - session_id=session_id) - elif file.startswith("base64"): - file_bytes = base64.b64decode(file.replace("base64://", "")) - result = await md.send_image(file_bytes, bot, message_type=session_type, session_id=session_id) - else: - return - raise MockApiException(result=result) - - @driver.on_startup async def on_startup(): temp_data = common_db.where_one(TempConfig(), default=TempConfig()) diff --git a/src/liteyuki_plugins/hello_liteyuki.py b/src/liteyuki_plugins/hello_liteyuki.py new file mode 100644 index 00000000..4ca03260 --- /dev/null +++ b/src/liteyuki_plugins/hello_liteyuki.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/20 上午5:12 +@Author : snowykami +@Email : snowykami@outlook.com +@File : liteyuki_reply.py +@Software: PyCharm +""" +from liteyuki.plugin import PluginMetadata, PluginType +from liteyuki.message.on import on_message +from liteyuki.message.event import Event + +__plugin_meta__ = PluginMetadata( + name="你好轻雪", + type=PluginType.TEST +) + + +@on_message().handle +async def _(event: Event): + if str(event.data["raw_message"]) == "你好轻雪": + event.reply("你好呀") diff --git a/src/liteyuki_plugins/liteyukibot_plugin_nonebot/__init__.py b/src/liteyuki_plugins/liteyukibot_plugin_nonebot/__init__.py index 58e1d11e..2aae4137 100644 --- a/src/liteyuki_plugins/liteyukibot_plugin_nonebot/__init__.py +++ b/src/liteyuki_plugins/liteyukibot_plugin_nonebot/__init__.py @@ -12,7 +12,7 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved import nonebot from liteyuki.utils import IS_MAIN_PROCESS from liteyuki.plugin import PluginMetadata, PluginType -from .nb_utils import adapter_manager, driver_manager # type_: ignore +from .nb_utils import adapter_manager, driver_manager # type: ignore __plugin_meta__ = PluginMetadata( name="NoneBot2启动器", diff --git a/src/nonebot_plugins/liteyuki_crt_utils/__init__.py b/src/nonebot_plugins/liteyuki_crt_utils/__init__.py index cb2365d4..c2437128 100644 --- a/src/nonebot_plugins/liteyuki_crt_utils/__init__.py +++ b/src/nonebot_plugins/liteyuki_crt_utils/__init__.py @@ -1,8 +1,8 @@ from nonebot.plugin import PluginMetadata from liteyuki import get_bot -from .crt_matchers import * # type_: ignore -from .rt_guide import * # type_: ignore +from .crt_matchers import * # type: ignore +from .rt_guide import * # type: ignore __plugin_meta__ = PluginMetadata( diff --git a/src/nonebot_plugins/liteyuki_pacman/npm.py b/src/nonebot_plugins/liteyuki_pacman/npm.py index 0803a292..24e0dfad 100644 --- a/src/nonebot_plugins/liteyuki_pacman/npm.py +++ b/src/nonebot_plugins/liteyuki_pacman/npm.py @@ -339,7 +339,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, npm: Matcher): await md.send_md(f"{info}\n\n" f"```\n{log}\n```", bot, event=event) elif sc.get("uninstall") and perm_s: - plugin_name: str = result.subcommands["uninstall"].args.get("plugin_name") # type_: ignore + plugin_name: str = result.subcommands["uninstall"].args.get("plugin_name") # type: ignore found_installed_plugin: InstalledPlugin = plugin_db.where_one( InstalledPlugin(), "module_name = ?", plugin_name ) diff --git a/src/nonebot_plugins/liteyuki_statistics/data_source.py b/src/nonebot_plugins/liteyuki_statistics/data_source.py index ea1236c0..017d6605 100644 --- a/src/nonebot_plugins/liteyuki_statistics/data_source.py +++ b/src/nonebot_plugins/liteyuki_statistics/data_source.py @@ -69,6 +69,8 @@ async def get_stat_msg_image( condition, *condition_args ) + if not msg_rows: + msg_rows = [] timestamps = [] msg_count = [] msg_rows.sort(key=lambda x: x.time) diff --git a/src/nonebot_plugins/liteyuki_statistics/stat_matchers.py b/src/nonebot_plugins/liteyuki_statistics/stat_matchers.py index 780a83d1..48e4a4c9 100644 --- a/src/nonebot_plugins/liteyuki_statistics/stat_matchers.py +++ b/src/nonebot_plugins/liteyuki_statistics/stat_matchers.py @@ -98,8 +98,10 @@ async def _(result: Arparma, event: T_MessageEvent, bot: Bot): bot_id = result.other_args.get("bot_id") user_id = result.other_args.get("user_id") - if group_id in ["current", "c"]: + if group_id in ["current", "c"] and hasattr(event, "group_id"): group_id = str(event_utils.get_group_id(event)) + else: + group_id = "all" if group_id in ["all", "a"]: group_id = "all" diff --git a/src/nonebot_plugins/to_liteyuki.py b/src/nonebot_plugins/to_liteyuki.py new file mode 100644 index 00000000..85c3b2eb --- /dev/null +++ b/src/nonebot_plugins/to_liteyuki.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/20 上午5:10 +@Author : snowykami +@Email : snowykami@outlook.com +@File : to_liteyuki.py +@Software: PyCharm +""" +from nonebot import Bot, get_bot, on_message +from nonebot.plugin import PluginMetadata +from nonebot.adapters.onebot.v11 import MessageEvent, Bot +from liteyuki.comm.storage import shared_memory +from liteyuki.message.event import Event + +__plugin_meta__ = PluginMetadata( + name="轻雪物流", + description="把消息事件传递给轻雪", + usage="无需使用", +) + + +@on_message().handle() +async def _(bot: Bot, event: MessageEvent): + liteyuki_event = Event( + type=event.message_type, + data=event.dict(), + bot_id=bot.self_id, + session_id=str(event.user_id if event.message_type == "private" else event.group_id), + session_type=event.message_type, + receive_channel="event_to_nonebot" + ) + shared_memory.publish("event_to_liteyuki", liteyuki_event) + + +@shared_memory.on_subscriber_receive("event_to_nonebot") +async def _(event: Event): + bot: Bot = get_bot(event.bot_id) + await bot.send_msg(message_type=event.type, user_id=int(event.session_id), group_id=int(event.session_id), message=event.data["message"]) diff --git a/src/resources/liteyuki_crt/templates/js/crt_route.js b/src/resources/liteyuki_crt/templates/js/crt_route.js index 392ff689..aafb060a 100644 --- a/src/resources/liteyuki_crt/templates/js/crt_route.js +++ b/src/resources/liteyuki_crt/templates/js/crt_route.js @@ -2,7 +2,7 @@ /** - * @type_ {{ + * @type {{ * results: Array<{ * abstracts: string, * createdDt: string, diff --git a/src/resources/liteyuki_weather/templates/js/weather_now.js b/src/resources/liteyuki_weather/templates/js/weather_now.js index 71129324..c2cb7973 100644 --- a/src/resources/liteyuki_weather/templates/js/weather_now.js +++ b/src/resources/liteyuki_weather/templates/js/weather_now.js @@ -12,7 +12,7 @@ * @property {Weather} weather - The weather data. */ -/** @type_ {Data} */ +/** @type {Data} */ let data = JSON.parse(document.getElementById("data").innerText) diff --git a/src/utils/base/data.py b/src/utils/base/data.py index 7c632b7c..894ce54f 100644 --- a/src/utils/base/data.py +++ b/src/utils/base/data.py @@ -28,7 +28,7 @@ class Database: os.makedirs(os.path.dirname(db_name)) self.db_name = db_name - self.conn = sqlite3.connect(db_name) + self.conn = sqlite3.connect(db_name, check_same_thread=False) self.cursor = self.conn.cursor() self._on_save_callbacks = [] @@ -105,12 +105,12 @@ class Database: return [model_type(**self._load(dict(zip(fields, result)))) for result in results] def save(self, *args: LiteModel): - """增/改操作 + self.returns_ = """增/改操作 Args: *args: Returns: """ - table_list = [item[0] for item in self.cursor.execute("SELECT name FROM sqlite_master WHERE type_='table'").fetchall()] + table_list = [item[0] for item in self.cursor.execute("SELECT name FROM sqlite_master WHERE type ='table'").fetchall()] for model in args: logger.debug(f"Upserting {model}") if not model.TABLE_NAME: @@ -433,4 +433,4 @@ def check_sqlite_keyword(name): ] return True # if name.upper() in sqlite_keywords: - # raise ValueError(f"'{name}' 是SQLite保留字,不建议使用,请更换名称") + # raise ValueError(f"'{name}' 是SQLite保留字,不建议使用,请更换名称") \ No newline at end of file diff --git a/src/utils/base/data_manager.py b/src/utils/base/data_manager.py index f883d207..5ff09269 100644 --- a/src/utils/base/data_manager.py +++ b/src/utils/base/data_manager.py @@ -65,7 +65,7 @@ def auto_migrate(): user_db.auto_migrate(User()) group_db.auto_migrate(Group()) plugin_db.auto_migrate(InstalledPlugin(), GlobalPlugin()) - common_db.auto_migrate(GlobalPlugin(), StoredConfig(), TempConfig()) + common_db.auto_migrate(GlobalPlugin(), TempConfig()) auto_migrate() diff --git a/src/utils/event/get_info.py b/src/utils/event/get_info.py index 218b6e2b..d2d3607f 100644 --- a/src/utils/event/get_info.py +++ b/src/utils/event/get_info.py @@ -1,6 +1,6 @@ from nonebot.adapters import satori - -from src.utils.base.ly_typing import T_MessageEvent +from nonebot.adapters import onebot +from src.utils.base.ly_typing import T_MessageEvent, T_GroupMessageEvent def get_user_id(event: T_MessageEvent): @@ -10,11 +10,13 @@ def get_user_id(event: T_MessageEvent): return event.user_id -def get_group_id(event: T_MessageEvent): +def get_group_id(event: T_GroupMessageEvent): if isinstance(event, satori.event.Event): return event.guild.id - else: + elif isinstance(event, onebot.v11.GroupMessageEvent): return event.group_id + else: + return None def get_message_type(event: T_MessageEvent) -> str: diff --git a/src/utils/message/message.py b/src/utils/message/message.py index 270f8531..6fbefa66 100644 --- a/src/utils/message/message.py +++ b/src/utils/message/message.py @@ -83,14 +83,14 @@ class MarkdownMessage: "send_private_forward_msg", messages=[ { - "type_": "node", + "type": "node", "data": { "content": [ { "data": { "content": "{\"content\":\"%s\"}" % formatted_md, }, - "type_": "markdown" + "type": "markdown" } ], "name": "[]", @@ -107,7 +107,7 @@ class MarkdownMessage: message_type=message_type, message=[ { - "type_": "longmsg", + "type": "longmsg", "data": { "id": forward_id } @@ -156,7 +156,7 @@ class MarkdownMessage: Args: image: 图片字节流或图片本地路径,链接请使用Markdown.image_async方法获取后通过send_md发送 bot: bot instance - message_type: message type_ + message_type: message type session_id: session id event: event kwargs: other arguments