feat: 增加根据qq号/群号主动发送消息的封装, 增加事件构造 (#2629)

* feat: 增加根据qq号/群号主动发送消息的封装, 增加事件构造

* fix: 增加不支持平台提示, 修正文档字符串

* chore: lint

---------

Co-authored-by: Soulter <905617992@qq.com>
This commit is contained in:
anka
2025-09-07 17:04:29 +08:00
committed by GitHub
parent 2c3c3ae546
commit d98cf16a4c
+116 -21
View File
@@ -1,15 +1,37 @@
"""
插件开发工具集
封装了许多常用的操作,方便插件开发者使用
说明:
主动发送消息: send_message(session, message_chain)
根据 session (unified_msg_origin) 主动发送消息, 前提是需要提前获得或构造 session
根据id直接主动发送消息: send_message_by_id(type, id, message_chain, platform="aiocqhttp")
根据 id (例如 qq 号, 群号等) 直接, 主动地发送消息
以上两种方式需要构造消息链, 也就是消息组件的列表
构造事件:
首先需要构造一个 AstrBotMessage 对象, 使用 create_message 方法
然后使用 create_event 方法提交事件到指定平台
"""
import inspect
import os
import uuid
from pathlib import Path
from typing import Union, Awaitable, List, Optional, ClassVar
from astrbot.core.message.components import BaseMessageComponent
from astrbot.core.message.message_event_result import MessageChain
from astrbot.api.platform import MessageMember, AstrBotMessage
from astrbot.api.platform import MessageMember, AstrBotMessage, MessageType
from astrbot.core.platform.astr_message_event import MessageSesion
from astrbot.core.star.context import Context
from astrbot.core.star.star import star_map
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_message_event import AiocqhttpMessageEvent
from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_platform_adapter import AiocqhttpAdapter
class StarTools:
"""
@@ -49,42 +71,76 @@ class StarTools:
Note:
qq_official(QQ官方API平台)不支持此方法
"""
if cls._context is None:
raise ValueError("StarTools not initialized")
return await cls._context.send_message(session, message_chain)
@classmethod
async def send_message_by_id(
cls, type: str, id: str, message_chain: MessageChain, platform: str = "aiocqhttp"
):
"""
根据 id(例如qq号, 群号等) 直接, 主动地发送消息
Args:
type (str): 消息类型, 可选: PrivateMessage, GroupMessage
id (str): 目标ID, 例如QQ号, 群号等
message_chain (MessageChain): 消息链
platform (str): 可选的平台名称,默认平台(aiocqhttp), 目前只支持 aiocqhttp
"""
if cls._context is None:
raise ValueError("StarTools not initialized")
platforms = cls._context.platform_manager.get_insts()
if platform == "aiocqhttp":
adapter = next((p for p in platforms if isinstance(p, AiocqhttpAdapter)), None)
if adapter is None:
raise ValueError("未找到适配器: AiocqhttpAdapter")
await AiocqhttpMessageEvent.send_message(
bot=adapter.bot,
message_chain=message_chain,
is_group=(type == "GroupMessage"),
session_id=id,
)
else:
raise ValueError(f"不支持的平台: {platform}")
@classmethod
async def create_message(
cls,
type: str,
self_id: str,
session_id: str,
message_id: str,
sender: MessageMember,
message: List[BaseMessageComponent],
message_str: str,
raw_message: object,
group_id: str = "",
):
message_id: str = "",
raw_message: object = None,
group_id: str = ""
) -> AstrBotMessage:
"""
创建一个AstrBot消息对象
Args:
type (str): 消息类型
type (str): 消息类型, 例如 "GroupMessage" "FriendMessage" "OtherMessage"
self_id (str): 机器人自身ID
session_id (str): 会话ID(通常为用户ID)(QQ号, 群号等)
message_id (str): 消息ID
sender (MessageMember): 发送者信息
message (List[BaseMessageComponent]): 消息组件列表
message_str (str): 消息字符串
raw_message (object): 原始消息对象
sender (MessageMember): 发送者信息, 例如 MessageMember(user_id="123456", nickname="昵称")
message (List[BaseMessageComponent]): 消息组件列表, 也就是消息链, 这个不会发给 llm, 但是会经过其他处理
message_str (str): 消息字符串, 也就是纯文本消息, 也就是发送给 llm 的消息, 与消息链一致
message_id (str): 消息ID, 构造消息时可以随意填写也可不填
raw_message (object): 原始消息对象, 可以随意填写也可不填
group_id (str, optional): 群组ID, 如果为私聊则为空. Defaults to "".
Returns:
AstrBotMessage: 创建的消息对象
"""
abm = AstrBotMessage()
abm.type = type
abm.type = MessageType(type)
abm.self_id = self_id
abm.session_id = session_id
if message_id == "":
message_id = uuid.uuid4().hex
abm.message_id = message_id
abm.sender = sender
abm.message = message
@@ -93,13 +149,38 @@ class StarTools:
abm.group_id = group_id
return abm
# todo: 添加构造事件的方法
# async def create_event(
# self, platform: str, umo: str, sender_id: str, session_id: str
# ):
# platform = self._context.get_platform(platform)
@classmethod
async def create_event(
cls, abm: AstrBotMessage, platform: str = "aiocqhttp", is_wake: bool = True
# todo: 添加找到对应平台并提交对应事件的方法
) -> None:
"""
创建并提交事件到指定平台
当有需要创建一个事件, 触发某些处理流程时, 使用该方法
Args:
abm (AstrBotMessage): 要提交的消息对象, 请先使用 create_message 创建
platform (str): 可选的平台名称,默认平台(aiocqhttp), 目前只支持 aiocqhttp
is_wake (bool): 是否标记为唤醒事件, 默认为 True, 只有唤醒事件才会被 llm 响应
"""
if cls._context is None:
raise ValueError("StarTools not initialized")
platforms = cls._context.platform_manager.get_insts()
if platform == "aiocqhttp":
adapter = next((p for p in platforms if isinstance(p, AiocqhttpAdapter)), None)
if adapter is None:
raise ValueError("未找到适配器: AiocqhttpAdapter")
event = AiocqhttpMessageEvent(
message_str=abm.message_str,
message_obj=abm,
platform_meta=adapter.metadata,
session_id=abm.session_id,
bot=adapter.bot,
)
event.is_wake = is_wake
adapter.commit_event(event)
else:
raise ValueError(f"不支持的平台: {platform}")
@classmethod
def activate_llm_tool(cls, name: str) -> bool:
@@ -110,6 +191,8 @@ class StarTools:
Args:
name (str): 工具名称
"""
if cls._context is None:
raise ValueError("StarTools not initialized")
return cls._context.activate_llm_tool(name)
@classmethod
@@ -120,6 +203,8 @@ class StarTools:
Args:
name (str): 工具名称
"""
if cls._context is None:
raise ValueError("StarTools not initialized")
return cls._context.deactivate_llm_tool(name)
@classmethod
@@ -135,6 +220,8 @@ class StarTools:
desc (str): 工具描述
func_obj (Awaitable): 函数对象,必须是异步函数
"""
if cls._context is None:
raise ValueError("StarTools not initialized")
cls._context.register_llm_tool(name, func_args, desc, func_obj)
@classmethod
@@ -146,6 +233,8 @@ class StarTools:
Args:
name (str): 工具名称
"""
if cls._context is None:
raise ValueError("StarTools not initialized")
cls._context.unregister_llm_tool(name)
@classmethod
@@ -169,8 +258,11 @@ class StarTools:
- 创建目录失败(权限不足或其他IO错误)
"""
if not plugin_name:
frame = inspect.currentframe().f_back
module = inspect.getmodule(frame)
frame = inspect.currentframe()
module = None
if frame:
frame = frame.f_back
module = inspect.getmodule(frame)
if not module:
raise RuntimeError("无法获取调用者模块信息")
@@ -182,6 +274,9 @@ class StarTools:
plugin_name = metadata.name
if not plugin_name:
raise ValueError("无法获取插件名称")
data_dir = Path(os.path.join(get_astrbot_data_path(), "plugin_data", plugin_name))
try: