diff --git a/.gitignore b/.gitignore
index ce2db2b4..2fc9d462 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,4 +53,5 @@ dist
doc
-mkdoc2.py
\ No newline at end of file
+mkdoc2.py
+result.json
\ No newline at end of file
diff --git a/docs/.vuepress/components/PluginItemCard.vue b/docs/.vuepress/components/PluginItemCard.vue
index bd8906c9..d74e88fb 100644
--- a/docs/.vuepress/components/PluginItemCard.vue
+++ b/docs/.vuepress/components/PluginItemCard.vue
@@ -2,6 +2,9 @@
{{ props.item.name }}
{{ props.item.desc }}
+
+ {{ tag.label }}
+
@@ -45,6 +48,21 @@ const copyToClipboard = () => {
})
}
+const getTagStyle = (backgroundColor: string) => {
+ // 将颜色值转换为 RGB 格式
+ const rgb = backgroundColor.replace(/^#/, '');
+ const [r, g, b] = rgb.match(/.{2}/g).map(x => parseInt(x, 16));
+
+ // 计算亮度
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000;
+
+ // 根据亮度决定文字颜色
+ return {
+ backgroundColor: backgroundColor,
+ color: brightness > 128 ? '#000' : '#fff'
+ };
+};
+
// 复制到剪贴板的函数
@@ -123,4 +141,12 @@ button {
justify-content: space-between;
color: #00000055;
}
+
+.tag {
+ display: inline-block;
+ padding: 2px 5px;
+ margin-right: 5px;
+ border-radius: 5px;
+ font-size: 12px;
+}
\ No newline at end of file
diff --git a/docs/.vuepress/public/assets/plugins.json b/docs/.vuepress/public/assets/plugins.json
index 946cccbb..d24deb4a 100644
--- a/docs/.vuepress/public/assets/plugins.json
+++ b/docs/.vuepress/public/assets/plugins.json
@@ -3,12 +3,12 @@
"module_name": "liteyukibot-plugin-nonebot",
"project_link": "liteyukibot-plugin-nonebot",
"name": "NoneBot插件",
- "desc": "在轻雪中使用NoneBot,为NoneBot开发者提供了更多便捷功能(已内置)",
+ "desc": "在轻雪中使用NoneBot,内置轻雪--NoneBot会话控制器插件,为NoneBot开发者提供了更多便捷功能(已内置)",
"author": "snowykami",
"homepage": "https://github.com/LiteyukiStudio/liteyukibot-plugin-nonebot",
"tags": [
{
- "label": "server",
+ "label": "app",
"color": "#aeeaa8"
}
],
@@ -16,5 +16,27 @@
"type": "application",
"valid": true,
"version": "rolling"
+ },
+ {
+ "module_name": "liteyukibot-plugin-antidislink",
+ "project_link": "liteyukibot-plugin-antidislink",
+ "name": "防断联插件",
+ "desc": "防止你的群友断联化",
+ "author": "snowykami",
+ "homepage": "https://github.com/snowykami/liteyukibot-plugin-antidislink",
+ "tags": [
+ {
+ "label": "app",
+ "color": "#aeeaa8"
+ },
+ {
+ "label": "dislink",
+ "color": "#d0e9ff"
+ }
+ ],
+ "is_official": true,
+ "type": "application",
+ "valid": true,
+ "version": "rolling"
}
]
diff --git a/liteyuki/__init__.py b/liteyuki/__init__.py
index 3bde5ff4..c96f3ed1 100644
--- a/liteyuki/__init__.py
+++ b/liteyuki/__init__.py
@@ -33,7 +33,9 @@ __all__ = [
"logger",
]
-__version__ = "6.3.8" # 测试版本号
+__version__ = "6.3.9" # 测试版本号
+# 6.3.9
+# 更改了on语法
# 6.3.8
# 1. 初步添加对聊天的支持
diff --git a/liteyuki/comm/channel.py b/liteyuki/comm/channel.py
index 3355be08..6ab737b0 100644
--- a/liteyuki/comm/channel.py
+++ b/liteyuki/comm/channel.py
@@ -38,7 +38,7 @@ class Channel(Generic[T]):
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
"""
- def __init__(self, _id: str, type_check: Optional[bool] = None):
+ def __init__(self, _id: str = "", type_check: Optional[bool] = None):
"""
初始化通道
Args:
@@ -217,16 +217,6 @@ class Channel(Generic[T]):
data = self.conn_recv.recv()
self._run_on_sub_receive_funcs(data)
- def __iter__(self):
- return self
-
- def __next__(self) -> Any:
- return self.receive()
-
- def __del__(self):
- self.close()
- logger.debug(f"Channel {self.name} deleted.")
-
"""子进程可用的主动和被动通道"""
active_channel: Optional["Channel"] = None
diff --git a/liteyuki/comm/storage.py b/liteyuki/comm/storage.py
index e3f34aa5..97c3de76 100644
--- a/liteyuki/comm/storage.py
+++ b/liteyuki/comm/storage.py
@@ -8,7 +8,7 @@ from typing import Any, Coroutine, Optional, TypeAlias, Callable
from liteyuki.comm import channel
from liteyuki.comm.channel import Channel, ON_RECEIVE_FUNC, ASYNC_ON_RECEIVE_FUNC
-from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable, run_coroutine
+from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable, run_coroutine, run_coroutine_in_thread
if IS_MAIN_PROCESS:
_locks = {}
@@ -220,10 +220,10 @@ class KeyValueStore:
"""
if IS_MAIN_PROCESS:
if channel_ in _on_main_subscriber_receive_funcs and _on_main_subscriber_receive_funcs[channel_]:
- run_coroutine(*[func(data) for func in _on_main_subscriber_receive_funcs[channel_]])
+ run_coroutine_in_thread(*[func(data) for func in _on_main_subscriber_receive_funcs[channel_]])
else:
if channel_ in _on_sub_subscriber_receive_funcs and _on_sub_subscriber_receive_funcs[channel_]:
- run_coroutine(*[func(data) for func in _on_sub_subscriber_receive_funcs[channel_]])
+ run_coroutine_in_thread(*[func(data) for func in _on_sub_subscriber_receive_funcs[channel_]])
def _start_receive_loop(self):
"""
diff --git a/liteyuki/message/matcher.py b/liteyuki/message/matcher.py
index 5585fa98..d2add762 100644
--- a/liteyuki/message/matcher.py
+++ b/liteyuki/message/matcher.py
@@ -34,16 +34,17 @@ class Matcher:
def __str__(self):
return f"Matcher(rule={self.rule}, priority={self.priority}, block={self.block})"
- def handle(self, handler: EventHandler) -> EventHandler:
+ def handle(self) -> Callable[[EventHandler], EventHandler]:
"""
添加处理函数,装饰器
- Args:
- handler:
Returns:
- EventHandler
+ 装饰器 handler
"""
- self.handlers.append(handler)
- return handler
+ def decorator(handler: EventHandler) -> EventHandler:
+ self.handlers.append(handler)
+ return handler
+
+ return decorator
async def run(self, event: MessageEvent) -> None:
"""
diff --git a/liteyuki/message/on.py b/liteyuki/message/on.py
index 37ce13dc..595e3c6e 100644
--- a/liteyuki/message/on.py
+++ b/liteyuki/message/on.py
@@ -15,7 +15,7 @@ from liteyuki.comm.storage import shared_memory
from liteyuki.log import logger
from liteyuki.message.event import MessageEvent
from liteyuki.message.matcher import Matcher
-from liteyuki.message.rule import Rule
+from liteyuki.message.rule import Rule, empty_rule
_matcher_list: list[Matcher] = []
_queue: Queue = Queue()
@@ -36,7 +36,7 @@ async def _(event: MessageEvent):
break
-def on_message(rule: Rule = Rule(), priority: int = 0, block: bool = True) -> Matcher:
+def on_message(rule: Rule = empty_rule, priority: int = 0, block: bool = False) -> Matcher:
matcher = Matcher(rule, priority, block)
# 按照优先级插入
for i, m in enumerate(_matcher_list):
@@ -46,3 +46,10 @@ def on_message(rule: Rule = Rule(), priority: int = 0, block: bool = True) -> Ma
else:
_matcher_list.append(matcher)
return matcher
+
+
+def on_keywords(keywords: list[str], rule=empty_rule, priority: int = 0, block: bool = False) -> Matcher:
+ @Rule
+ async def on_keywords_rule(event: MessageEvent):
+ return any(keyword in event.raw_message for keyword in keywords)
+ return on_message(on_keywords_rule & rule, priority, block)
diff --git a/liteyuki/message/rule.py b/liteyuki/message/rule.py
index 31af299e..56c4c541 100644
--- a/liteyuki/message/rule.py
+++ b/liteyuki/message/rule.py
@@ -8,26 +8,37 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@File : rule.py
@Software: PyCharm
"""
-
+import inspect
from typing import Optional, TypeAlias, Callable, Coroutine
from liteyuki.message.event import MessageEvent
-RuleHandler: TypeAlias = Callable[[MessageEvent], Coroutine[None, None, bool]]
+RuleHandlerFunc: TypeAlias = Callable[[MessageEvent], Coroutine[None, None, bool]]
"""规则函数签名"""
class Rule:
- def __init__(self, handler: Optional[RuleHandler] = None):
+ def __init__(self, handler: RuleHandlerFunc):
self.handler = handler
def __or__(self, other: "Rule") -> "Rule":
- return Rule(lambda event: self.handler(event) or other.handler(event))
+ async def combined_handler(event: MessageEvent) -> bool:
+ return await self.handler(event) or await other.handler(event)
+
+ return Rule(combined_handler)
def __and__(self, other: "Rule") -> "Rule":
- return Rule(lambda event: self.handler(event) and other.handler(event))
+ async def combined_handler(event: MessageEvent) -> bool:
+ return await self.handler(event) and await other.handler(event)
+
+ return Rule(combined_handler)
async def __call__(self, event: MessageEvent) -> bool:
if self.handler is None:
return True
return await self.handler(event)
+
+
+@Rule
+async def empty_rule(event: MessageEvent) -> bool:
+ return True
diff --git a/liteyuki/mkdoc.py b/liteyuki/mkdoc.py
index fb179473..7463eca2 100644
--- a/liteyuki/mkdoc.py
+++ b/liteyuki/mkdoc.py
@@ -209,13 +209,14 @@ def get_module_info_normal(file_path: str, ignore_private: bool = True) -> Modul
return module_info
-def generate_markdown(module_info: ModuleInfo, front_matter=None) -> str:
+def generate_markdown(module_info: ModuleInfo, front_matter=None, lang: str = "zh-CN") -> str:
"""
生成模块的Markdown
你可在此自定义生成的Markdown格式
Args:
module_info: 模块信息
front_matter: 自定义选项title, index, icon, category
+ lang: 语言
Returns:
Markdown 字符串
"""
@@ -261,7 +262,13 @@ def generate_markdown(module_info: ModuleInfo, front_matter=None) -> str:
method.docstring = method.docstring.replace("\n", "\n\n")
content += f" {method.docstring}\n\n"
# 函数源代码可展开区域
- content += f"\n源代码
\n\n```python\n{method.source_code}\n```\n \n\n"
+
+ if lang == "zh-CN":
+ TEXT_SOURCE_CODE = "源代码"
+ else:
+ TEXT_SOURCE_CODE = "Source Code"
+
+ content += f"\n{TEXT_SOURCE_CODE}
\n\n```python\n{method.source_code}\n```\n \n\n"
for attr in cls.attributes:
content += f"### ***attr*** `{attr.name}: {attr.type}`\n\n"
@@ -278,7 +285,7 @@ def generate_markdown(module_info: ModuleInfo, front_matter=None) -> str:
return content
-def generate_docs(module_folder: str, output_dir: str, with_top: bool = False, ignored_paths=None):
+def generate_docs(module_folder: str, output_dir: str, with_top: bool = False, lang: str = "zh-CN", ignored_paths=None):
"""
生成文档
Args:
@@ -286,6 +293,7 @@ def generate_docs(module_folder: str, output_dir: str, with_top: bool = False, i
output_dir: 输出文件夹
with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b, True时例如docs/api/module/module_a.md, docs/api/module/module_b.md
ignored_paths: 忽略的路径
+ lang: 语言
"""
if ignored_paths is None:
ignored_paths = []
@@ -345,5 +353,5 @@ def generate_docs(module_folder: str, output_dir: str, with_top: bool = False, i
# 入口脚本
if __name__ == '__main__':
# 这里填入你的模块路径
- generate_docs('liteyuki', 'docs/dev/api', with_top=False, ignored_paths=["liteyuki/plugins"])
- generate_docs('liteyuki', 'docs/en/dev/api', with_top=False, ignored_paths=["liteyuki/plugins"])
+ generate_docs('liteyuki', 'docs/dev/api', with_top=False, ignored_paths=["liteyuki/plugins"], lang="zh-CN")
+ generate_docs('liteyuki', 'docs/en/dev/api', with_top=False, ignored_paths=["liteyuki/plugins"], lang="en")
diff --git a/liteyuki/utils.py b/liteyuki/utils.py
index 90a2048f..6eb646a1 100644
--- a/liteyuki/utils.py
+++ b/liteyuki/utils.py
@@ -5,6 +5,7 @@
import asyncio
import inspect
import multiprocessing
+import threading
from pathlib import Path
from typing import Any, Callable, Coroutine
@@ -61,6 +62,16 @@ def run_coroutine(*coro: Coroutine):
# 捕获其他异常,防止协程被重复等待
logger.error(f"Exception occurred: {e}")
+def run_coroutine_in_thread(*coro: Coroutine):
+ """
+ 在新线程中运行协程
+ Args:
+ coro:
+
+ Returns:
+
+ """
+ threading.Thread(target=run_coroutine, args=coro, daemon=True).start()
def path_to_module_name(path: Path) -> str:
"""
diff --git a/src/liteyuki_plugins/anti_dislink.py b/src/liteyuki_plugins/anti_dislink.py
new file mode 100644
index 00000000..7ef8387d
--- /dev/null
+++ b/src/liteyuki_plugins/anti_dislink.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/8/22 上午9:06
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : anti_dislink.py
+@Software: PyCharm
+"""
+import random
+from liteyuki.plugin import PluginMetadata, PluginType
+
+from liteyuki.message.on import on_keywords
+
+__plugin_meta__ = PluginMetadata(
+ name="严禁断联化",
+ type=PluginType.APPLICATION
+)
+
+
+@on_keywords(["看看你的", "看看j", "给我看看"]).handle()
+async def _(event):
+ event.reply(random.choice(["No dislink", "严禁断联化"]))
diff --git a/src/liteyuki_plugins/hello_liteyuki.py b/src/liteyuki_plugins/hello_liteyuki.py
index 72d1ac42..86314c34 100644
--- a/src/liteyuki_plugins/hello_liteyuki.py
+++ b/src/liteyuki_plugins/hello_liteyuki.py
@@ -14,11 +14,11 @@ from liteyuki.message.event import MessageEvent
__plugin_meta__ = PluginMetadata(
name="你好轻雪",
- type=PluginType.TEST
+ type=PluginType.APPLICATION
)
-@on_message().handle
+@on_message().handle()
async def _(event: MessageEvent):
if str(event.raw_message) == "你好轻雪":
event.reply("你好呀")
diff --git a/src/liteyuki_plugins/ts_chan_main.py b/src/liteyuki_plugins/ts_chan_main.py
new file mode 100644
index 00000000..4b5f3280
--- /dev/null
+++ b/src/liteyuki_plugins/ts_chan_main.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/8/22 上午8:37
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : ts_chan_main.py
+@Software: PyCharm
+"""
+import asyncio
+
+from liteyuki.comm import Channel, set_channel, get_channel
+from liteyuki import get_bot
+
+set_channel("chan-main", Channel("chan-main"))
+set_channel("chan-sub", Channel("chan-sub"))
+
+chan_main = get_channel("chan-main")
+
+
+# @get_bot().on_after_start
+# async def _():
+# while True:
+# chan_main.send("Hello, World!")
+# await asyncio.sleep(5)
diff --git a/src/nonebot_plugins/to_liteyuki.py b/src/nonebot_plugins/to_liteyuki.py
index 8358db5c..f8410385 100644
--- a/src/nonebot_plugins/to_liteyuki.py
+++ b/src/nonebot_plugins/to_liteyuki.py
@@ -8,6 +8,8 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
@File : to_liteyuki.py
@Software: PyCharm
"""
+import asyncio
+
from nonebot import Bot, get_bot, on_message
from nonebot.plugin import PluginMetadata
from nonebot.adapters.onebot.v11 import MessageEvent, Bot
@@ -37,6 +39,9 @@ async def _(bot: Bot, event: MessageEvent):
@shared_memory.on_subscriber_receive("event_to_nonebot")
-async def _(event: MessageEvent):
+async def _(event: LiteyukiMessageEvent):
bot: Bot = get_bot(event.bot_id)
- await bot.send_msg(message_type=event.message_type, user_id=int(event.session_id), group_id=int(event.session_id), message=event.data["message"])
+ if event.message_type == "private":
+ await bot.send_private_msg(user_id=int(event.session_id), message=event.data["message"])
+ elif event.message_type == "group":
+ await bot.send_group_msg(group_id=int(event.session_id), message=event.data["message"])
diff --git a/src/nonebot_plugins/ts_chan_sub.py b/src/nonebot_plugins/ts_chan_sub.py
new file mode 100644
index 00000000..3a2c6665
--- /dev/null
+++ b/src/nonebot_plugins/ts_chan_sub.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/8/22 上午8:39
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : ts_chan_sub.py
+@Software: PyCharm
+"""
+import asyncio
+
+from liteyuki.comm import Channel, get_channel
+from nonebot import get_bot
+from nonebot.adapters.onebot.v11 import Bot
+chan_main = get_channel("chan-main")
+
+
+# @chan_main.on_receive()
+# async def _(data: str):
+# print("Received data from chan-main:", data)
+# try:
+# bot: Bot = get_bot("2443429204") # type: ignore
+#
+# def send_msg():
+#
+# bot.send_msg(message_type="private", user_id=2443429204, message=data)
+#
+# print("tsA")
+# print("tsA1")
+# await asyncio.ensure_future(c)
+# print("tsB")
+# except Exception as e:
+# print(e)
+# pass