Merge branch 'dev' into dev
This commit is contained in:
@@ -5,147 +5,126 @@
|
|||||||
## 导入方式
|
## 导入方式
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import chat_api
|
from src.plugin_system import chat_api
|
||||||
# 或者
|
# 或者
|
||||||
from src.plugin_system.apis.chat_api import ChatManager as chat
|
from src.plugin_system.apis import chat_api
|
||||||
|
```
|
||||||
|
|
||||||
|
一种**Deprecated**方式:
|
||||||
|
```python
|
||||||
|
from src.plugin_system.apis.chat_api import ChatManager
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主要功能
|
## 主要功能
|
||||||
|
|
||||||
### 1. 获取聊天流
|
### 1. 获取所有的聊天流
|
||||||
|
|
||||||
#### `get_all_streams(platform: str = "qq") -> List[ChatStream]`
|
```python
|
||||||
获取所有聊天流
|
def get_all_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]:
|
||||||
|
```
|
||||||
|
|
||||||
**参数:**
|
**Args**:
|
||||||
- `platform`:平台筛选,默认为"qq"
|
- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的聊天流。
|
||||||
|
|
||||||
**返回:**
|
**Returns**:
|
||||||
- `List[ChatStream]`:聊天流列表
|
- `List[ChatStream]`:聊天流列表
|
||||||
|
|
||||||
**示例:**
|
### 2. 获取群聊聊天流
|
||||||
|
|
||||||
```python
|
```python
|
||||||
streams = chat_api.get_all_streams()
|
def get_group_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]:
|
||||||
for stream in streams:
|
|
||||||
print(f"聊天流ID: {stream.stream_id}")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_group_streams(platform: str = "qq") -> List[ChatStream]`
|
**Args**:
|
||||||
获取所有群聊聊天流
|
- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的群聊流。
|
||||||
|
|
||||||
**参数:**
|
**Returns**:
|
||||||
- `platform`:平台筛选,默认为"qq"
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `List[ChatStream]`:群聊聊天流列表
|
- `List[ChatStream]`:群聊聊天流列表
|
||||||
|
|
||||||
#### `get_private_streams(platform: str = "qq") -> List[ChatStream]`
|
### 3. 获取私聊聊天流
|
||||||
获取所有私聊聊天流
|
|
||||||
|
|
||||||
**参数:**
|
```python
|
||||||
- `platform`:平台筛选,默认为"qq"
|
def get_private_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]:
|
||||||
|
```
|
||||||
|
|
||||||
**返回:**
|
**Args**:
|
||||||
|
- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的私聊流。
|
||||||
|
|
||||||
|
**Returns**:
|
||||||
- `List[ChatStream]`:私聊聊天流列表
|
- `List[ChatStream]`:私聊聊天流列表
|
||||||
|
|
||||||
### 2. 查找特定聊天流
|
### 4. 根据群ID获取聊天流
|
||||||
|
|
||||||
#### `get_stream_by_group_id(group_id: str, platform: str = "qq") -> Optional[ChatStream]`
|
```python
|
||||||
根据群ID获取聊天流
|
def get_stream_by_group_id(group_id: str, platform: Optional[str] | SpecialTypes = "qq") -> Optional[ChatStream]:
|
||||||
|
```
|
||||||
|
|
||||||
**参数:**
|
**Args**:
|
||||||
- `group_id`:群聊ID
|
- `group_id`:群聊ID
|
||||||
- `platform`:平台,默认为"qq"
|
- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的群聊流。
|
||||||
|
|
||||||
**返回:**
|
**Returns**:
|
||||||
- `Optional[ChatStream]`:聊天流对象,如果未找到返回None
|
- `Optional[ChatStream]`:聊天流对象,如果未找到返回None
|
||||||
|
|
||||||
**示例:**
|
### 5. 根据用户ID获取私聊流
|
||||||
|
|
||||||
```python
|
```python
|
||||||
chat_stream = chat_api.get_stream_by_group_id("123456789")
|
def get_stream_by_user_id(user_id: str, platform: Optional[str] | SpecialTypes = "qq") -> Optional[ChatStream]:
|
||||||
if chat_stream:
|
|
||||||
print(f"找到群聊: {chat_stream.group_info.group_name}")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_stream_by_user_id(user_id: str, platform: str = "qq") -> Optional[ChatStream]`
|
**Args**:
|
||||||
根据用户ID获取私聊流
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `user_id`:用户ID
|
- `user_id`:用户ID
|
||||||
- `platform`:平台,默认为"qq"
|
- `platform`:平台筛选,默认为"qq",可以使用`SpecialTypes`枚举类中的`SpecialTypes.ALL_PLATFORMS`来获取所有平台的私聊流。
|
||||||
|
|
||||||
**返回:**
|
**Returns**:
|
||||||
- `Optional[ChatStream]`:聊天流对象,如果未找到返回None
|
- `Optional[ChatStream]`:聊天流对象,如果未找到返回None
|
||||||
|
|
||||||
### 3. 聊天流信息查询
|
### 6. 获取聊天流类型
|
||||||
|
|
||||||
#### `get_stream_type(chat_stream: ChatStream) -> str`
|
```python
|
||||||
获取聊天流类型
|
def get_stream_type(chat_stream: ChatStream) -> str:
|
||||||
|
```
|
||||||
|
|
||||||
**参数:**
|
**Args**:
|
||||||
- `chat_stream`:聊天流对象
|
- `chat_stream`:聊天流对象
|
||||||
|
|
||||||
**返回:**
|
**Returns**:
|
||||||
- `str`:聊天类型 ("group", "private", "unknown")
|
- `str`:聊天流类型,可能的值包括`private`(私聊流),`group`(群聊流)以及`unknown`(未知类型)。
|
||||||
|
|
||||||
#### `get_stream_info(chat_stream: ChatStream) -> Dict[str, Any]`
|
### 7. 获取聊天流信息
|
||||||
获取聊天流详细信息
|
|
||||||
|
|
||||||
**参数:**
|
```python
|
||||||
|
def get_stream_info(chat_stream: ChatStream) -> Dict[str, Any]:
|
||||||
|
```
|
||||||
|
|
||||||
|
**Args**:
|
||||||
- `chat_stream`:聊天流对象
|
- `chat_stream`:聊天流对象
|
||||||
|
|
||||||
**返回:**
|
**Returns**:
|
||||||
- `Dict[str, Any]`:聊天流信息字典,包含stream_id、platform、type等信息
|
- `Dict[str, Any]`:聊天流的详细信息,包括但不限于:
|
||||||
|
- `stream_id`:聊天流ID
|
||||||
|
- `platform`:平台名称
|
||||||
|
- `type`:聊天流类型
|
||||||
|
- `group_id`:群聊ID
|
||||||
|
- `group_name`:群聊名称
|
||||||
|
- `user_id`:用户ID
|
||||||
|
- `user_name`:用户名称
|
||||||
|
|
||||||
|
### 8. 获取聊天流统计摘要
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
info = chat_api.get_stream_info(chat_stream)
|
def get_streams_summary() -> Dict[str, int]:
|
||||||
print(f"聊天类型: {info['type']}")
|
|
||||||
print(f"平台: {info['platform']}")
|
|
||||||
if info['type'] == 'group':
|
|
||||||
print(f"群ID: {info['group_id']}")
|
|
||||||
print(f"群名: {info['group_name']}")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_streams_summary() -> Dict[str, int]`
|
**Returns**:
|
||||||
获取聊天流统计信息
|
- `Dict[str, int]`:聊天流统计信息摘要,包含以下键:
|
||||||
|
- `total_streams`:总聊天流数量
|
||||||
|
- `group_streams`:群聊流数量
|
||||||
|
- `private_streams`:私聊流数量
|
||||||
|
- `qq_streams`:QQ平台流数量
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Dict[str, int]`:包含各平台群聊和私聊数量的统计字典
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 基础用法
|
|
||||||
```python
|
|
||||||
from src.plugin_system.apis import chat_api
|
|
||||||
|
|
||||||
# 获取所有群聊
|
|
||||||
group_streams = chat_api.get_group_streams()
|
|
||||||
print(f"共有 {len(group_streams)} 个群聊")
|
|
||||||
|
|
||||||
# 查找特定群聊
|
|
||||||
target_group = chat_api.get_stream_by_group_id("123456789")
|
|
||||||
if target_group:
|
|
||||||
group_info = chat_api.get_stream_info(target_group)
|
|
||||||
print(f"群名: {group_info['group_name']}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 遍历所有聊天流
|
|
||||||
```python
|
|
||||||
# 获取所有聊天流并分类处理
|
|
||||||
all_streams = chat_api.get_all_streams()
|
|
||||||
|
|
||||||
for stream in all_streams:
|
|
||||||
stream_type = chat_api.get_stream_type(stream)
|
|
||||||
if stream_type == "group":
|
|
||||||
print(f"群聊: {stream.group_info.group_name}")
|
|
||||||
elif stream_type == "private":
|
|
||||||
print(f"私聊: {stream.user_info.user_nickname}")
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. 所有函数都有错误处理,失败时会记录日志
|
1. 大部分函数在参数不合法时候会抛出异常,请确保你的程序进行了捕获。
|
||||||
2. 查询函数返回None或空列表时表示未找到结果
|
2. `ChatStream`对象包含了聊天的完整信息,包括用户信息、群信息等。
|
||||||
3. `platform`参数通常为"qq",也可能支持其他平台
|
|
||||||
4. `ChatStream`对象包含了聊天的完整信息,包括用户信息、群信息等
|
|
||||||
180
docs/plugins/api/component-manage-api.md
Normal file
180
docs/plugins/api/component-manage-api.md
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# 组件管理API
|
||||||
|
|
||||||
|
组件管理API模块提供了对插件组件的查询和管理功能,使得插件能够获取和使用组件相关的信息。
|
||||||
|
|
||||||
|
## 导入方式
|
||||||
|
```python
|
||||||
|
from src.plugin_system.apis import component_manage_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import component_manage_api
|
||||||
|
```
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
|
||||||
|
组件管理API主要提供以下功能:
|
||||||
|
- **插件信息查询** - 获取所有插件或指定插件的信息。
|
||||||
|
- **组件查询** - 按名称或类型查询组件信息。
|
||||||
|
- **组件管理** - 启用或禁用组件,支持全局和局部操作。
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
|
||||||
|
### 1. 获取所有插件信息
|
||||||
|
```python
|
||||||
|
def get_all_plugin_info() -> Dict[str, PluginInfo]:
|
||||||
|
```
|
||||||
|
获取所有插件的信息。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Dict[str, PluginInfo]` - 包含所有插件信息的字典,键为插件名称,值为 `PluginInfo` 对象。
|
||||||
|
|
||||||
|
### 2. 获取指定插件信息
|
||||||
|
```python
|
||||||
|
def get_plugin_info(plugin_name: str) -> Optional[PluginInfo]:
|
||||||
|
```
|
||||||
|
获取指定插件的信息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `plugin_name` (str): 插件名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Optional[PluginInfo]`: 插件信息对象,如果插件不存在则返回 `None`。
|
||||||
|
|
||||||
|
### 3. 获取指定组件信息
|
||||||
|
```python
|
||||||
|
def get_component_info(component_name: str, component_type: ComponentType) -> Optional[Union[CommandInfo, ActionInfo, EventHandlerInfo]]:
|
||||||
|
```
|
||||||
|
获取指定组件的信息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `component_name` (str): 组件名称。
|
||||||
|
- `component_type` (ComponentType): 组件类型。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Optional[Union[CommandInfo, ActionInfo, EventHandlerInfo]]`: 组件信息对象,如果组件不存在则返回 `None`。
|
||||||
|
|
||||||
|
### 4. 获取指定类型的所有组件信息
|
||||||
|
```python
|
||||||
|
def get_components_info_by_type(component_type: ComponentType) -> Dict[str, Union[CommandInfo, ActionInfo, EventHandlerInfo]]:
|
||||||
|
```
|
||||||
|
获取指定类型的所有组件信息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `component_type` (ComponentType): 组件类型。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Dict[str, Union[CommandInfo, ActionInfo, EventHandlerInfo]]`: 包含指定类型组件信息的字典,键为组件名称,值为对应的组件信息对象。
|
||||||
|
|
||||||
|
### 5. 获取指定类型的所有启用的组件信息
|
||||||
|
```python
|
||||||
|
def get_enabled_components_info_by_type(component_type: ComponentType) -> Dict[str, Union[CommandInfo, ActionInfo, EventHandlerInfo]]:
|
||||||
|
```
|
||||||
|
获取指定类型的所有启用的组件信息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `component_type` (ComponentType): 组件类型。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Dict[str, Union[CommandInfo, ActionInfo, EventHandlerInfo]]`: 包含指定类型启用组件信息的字典,键为组件名称,值为对应的组件信息对象。
|
||||||
|
|
||||||
|
### 6. 获取指定 Action 的注册信息
|
||||||
|
```python
|
||||||
|
def get_registered_action_info(action_name: str) -> Optional[ActionInfo]:
|
||||||
|
```
|
||||||
|
获取指定 Action 的注册信息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `action_name` (str): Action 名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Optional[ActionInfo]` - Action 信息对象,如果 Action 不存在则返回 `None`。
|
||||||
|
|
||||||
|
### 7. 获取指定 Command 的注册信息
|
||||||
|
```python
|
||||||
|
def get_registered_command_info(command_name: str) -> Optional[CommandInfo]:
|
||||||
|
```
|
||||||
|
获取指定 Command 的注册信息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `command_name` (str): Command 名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Optional[CommandInfo]` - Command 信息对象,如果 Command 不存在则返回 `None`。
|
||||||
|
|
||||||
|
### 8. 获取指定 EventHandler 的注册信息
|
||||||
|
```python
|
||||||
|
def get_registered_event_handler_info(event_handler_name: str) -> Optional[EventHandlerInfo]:
|
||||||
|
```
|
||||||
|
获取指定 EventHandler 的注册信息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `event_handler_name` (str): EventHandler 名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Optional[EventHandlerInfo]` - EventHandler 信息对象,如果 EventHandler 不存在则返回 `None`。
|
||||||
|
|
||||||
|
### 9. 全局启用指定组件
|
||||||
|
```python
|
||||||
|
def globally_enable_component(component_name: str, component_type: ComponentType) -> bool:
|
||||||
|
```
|
||||||
|
全局启用指定组件。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `component_name` (str): 组件名称。
|
||||||
|
- `component_type` (ComponentType): 组件类型。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `bool` - 启用成功返回 `True`,否则返回 `False`。
|
||||||
|
|
||||||
|
### 10. 全局禁用指定组件
|
||||||
|
```python
|
||||||
|
async def globally_disable_component(component_name: str, component_type: ComponentType) -> bool:
|
||||||
|
```
|
||||||
|
全局禁用指定组件。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `component_name` (str): 组件名称。
|
||||||
|
- `component_type` (ComponentType): 组件类型。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `bool` - 禁用成功返回 `True`,否则返回 `False`。
|
||||||
|
|
||||||
|
### 11. 局部启用指定组件
|
||||||
|
```python
|
||||||
|
def locally_enable_component(component_name: str, component_type: ComponentType, stream_id: str) -> bool:
|
||||||
|
```
|
||||||
|
局部启用指定组件。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `component_name` (str): 组件名称。
|
||||||
|
- `component_type` (ComponentType): 组件类型。
|
||||||
|
- `stream_id` (str): 消息流 ID。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `bool` - 启用成功返回 `True`,否则返回 `False`。
|
||||||
|
|
||||||
|
### 12. 局部禁用指定组件
|
||||||
|
```python
|
||||||
|
def locally_disable_component(component_name: str, component_type: ComponentType, stream_id: str) -> bool:
|
||||||
|
```
|
||||||
|
局部禁用指定组件。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `component_name` (str): 组件名称。
|
||||||
|
- `component_type` (ComponentType): 组件类型。
|
||||||
|
- `stream_id` (str): 消息流 ID。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `bool` - 禁用成功返回 `True`,否则返回 `False`。
|
||||||
|
|
||||||
|
### 13. 获取指定消息流中禁用的组件列表
|
||||||
|
```python
|
||||||
|
def get_locally_disabled_components(stream_id: str, component_type: ComponentType) -> list[str]:
|
||||||
|
```
|
||||||
|
获取指定消息流中禁用的组件列表。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `stream_id` (str): 消息流 ID。
|
||||||
|
- `component_type` (ComponentType): 组件类型。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `list[str]` - 禁用的组件名称列表。
|
||||||
@@ -6,178 +6,47 @@
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import config_api
|
from src.plugin_system.apis import config_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import config_api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主要功能
|
## 主要功能
|
||||||
|
|
||||||
### 1. 配置访问
|
### 1. 访问全局配置
|
||||||
|
|
||||||
#### `get_global_config(key: str, default: Any = None) -> Any`
|
|
||||||
安全地从全局配置中获取一个值
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `key`:配置键名,支持嵌套访问如 "section.subsection.key"
|
|
||||||
- `default`:如果配置不存在时返回的默认值
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Any`:配置值或默认值
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
# 获取机器人昵称
|
def get_global_config(key: str, default: Any = None) -> Any:
|
||||||
|
```
|
||||||
|
|
||||||
|
**Args**:
|
||||||
|
- `key`: 命名空间式配置键名,使用嵌套访问,如 "section.subsection.key",大小写敏感
|
||||||
|
- `default`: 如果配置不存在时返回的默认值
|
||||||
|
|
||||||
|
**Returns**:
|
||||||
|
- `Any`: 配置值或默认值
|
||||||
|
|
||||||
|
#### 示例:
|
||||||
|
获取机器人昵称
|
||||||
|
```python
|
||||||
bot_name = config_api.get_global_config("bot.nickname", "MaiBot")
|
bot_name = config_api.get_global_config("bot.nickname", "MaiBot")
|
||||||
|
|
||||||
# 获取嵌套配置
|
|
||||||
llm_model = config_api.get_global_config("model.default.model_name", "gpt-3.5-turbo")
|
|
||||||
|
|
||||||
# 获取不存在的配置
|
|
||||||
unknown_config = config_api.get_global_config("unknown.config", "默认值")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_plugin_config(plugin_config: dict, key: str, default: Any = None) -> Any`
|
### 2. 获取插件配置
|
||||||
从插件配置中获取值,支持嵌套键访问
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `plugin_config`:插件配置字典
|
|
||||||
- `key`:配置键名,支持嵌套访问如 "section.subsection.key"
|
|
||||||
- `default`:如果配置不存在时返回的默认值
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Any`:配置值或默认值
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
# 在插件中使用
|
def get_plugin_config(plugin_config: dict, key: str, default: Any = None) -> Any:
|
||||||
class MyPlugin(BasePlugin):
|
|
||||||
async def handle_action(self, action_data, chat_stream):
|
|
||||||
# 获取插件配置
|
|
||||||
api_key = config_api.get_plugin_config(self.config, "api.key", "")
|
|
||||||
timeout = config_api.get_plugin_config(self.config, "timeout", 30)
|
|
||||||
|
|
||||||
if not api_key:
|
|
||||||
logger.warning("API密钥未配置")
|
|
||||||
return False
|
|
||||||
```
|
```
|
||||||
|
**Args**:
|
||||||
|
- `plugin_config`: 插件配置字典
|
||||||
|
- `key`: 配置键名,支持嵌套访问如 "section.subsection.key",大小写敏感
|
||||||
|
- `default`: 如果配置不存在时返回的默认值
|
||||||
|
|
||||||
### 2. 用户信息API
|
**Returns**:
|
||||||
|
- `Any`: 配置值或默认值
|
||||||
#### `get_user_id_by_person_name(person_name: str) -> tuple[str, str]`
|
|
||||||
根据用户名获取用户ID
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `person_name`:用户名
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `tuple[str, str]`:(平台, 用户ID)
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
|
||||||
platform, user_id = await config_api.get_user_id_by_person_name("张三")
|
|
||||||
if platform and user_id:
|
|
||||||
print(f"用户张三在{platform}平台的ID是{user_id}")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `get_person_info(person_id: str, key: str, default: Any = None) -> Any`
|
|
||||||
获取用户信息
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `person_id`:用户ID
|
|
||||||
- `key`:信息键名
|
|
||||||
- `default`:默认值
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Any`:用户信息值或默认值
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
|
||||||
# 获取用户昵称
|
|
||||||
nickname = await config_api.get_person_info(person_id, "nickname", "未知用户")
|
|
||||||
|
|
||||||
# 获取用户印象
|
|
||||||
impression = await config_api.get_person_info(person_id, "impression", "")
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 配置驱动的插件开发
|
|
||||||
```python
|
|
||||||
from src.plugin_system.apis import config_api
|
|
||||||
from src.plugin_system.base import BasePlugin
|
|
||||||
|
|
||||||
class WeatherPlugin(BasePlugin):
|
|
||||||
async def handle_action(self, action_data, chat_stream):
|
|
||||||
# 从全局配置获取API配置
|
|
||||||
api_endpoint = config_api.get_global_config("weather.api_endpoint", "")
|
|
||||||
default_city = config_api.get_global_config("weather.default_city", "北京")
|
|
||||||
|
|
||||||
# 从插件配置获取特定设置
|
|
||||||
api_key = config_api.get_plugin_config(self.config, "api_key", "")
|
|
||||||
timeout = config_api.get_plugin_config(self.config, "timeout", 10)
|
|
||||||
|
|
||||||
if not api_key:
|
|
||||||
return {"success": False, "message": "Weather API密钥未配置"}
|
|
||||||
|
|
||||||
# 使用配置进行天气查询...
|
|
||||||
return {"success": True, "message": f"{default_city}今天天气晴朗"}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 用户信息查询
|
|
||||||
```python
|
|
||||||
async def get_user_by_name(user_name: str):
|
|
||||||
"""根据用户名获取完整的用户信息"""
|
|
||||||
|
|
||||||
# 获取用户的平台和ID
|
|
||||||
platform, user_id = await config_api.get_user_id_by_person_name(user_name)
|
|
||||||
|
|
||||||
if not platform or not user_id:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 构建person_id
|
|
||||||
from src.person_info.person_info import PersonInfoManager
|
|
||||||
person_id = PersonInfoManager.get_person_id(platform, user_id)
|
|
||||||
|
|
||||||
# 获取用户详细信息
|
|
||||||
nickname = await config_api.get_person_info(person_id, "nickname", user_name)
|
|
||||||
impression = await config_api.get_person_info(person_id, "impression", "")
|
|
||||||
|
|
||||||
return {
|
|
||||||
"platform": platform,
|
|
||||||
"user_id": user_id,
|
|
||||||
"nickname": nickname,
|
|
||||||
"impression": impression
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 配置键名说明
|
|
||||||
|
|
||||||
### 常用全局配置键
|
|
||||||
- `bot.nickname`:机器人昵称
|
|
||||||
- `bot.qq_account`:机器人QQ号
|
|
||||||
- `model.default`:默认LLM模型配置
|
|
||||||
- `database.path`:数据库路径
|
|
||||||
|
|
||||||
### 嵌套配置访问
|
|
||||||
配置支持点号分隔的嵌套访问:
|
|
||||||
```python
|
|
||||||
# config.toml 中的配置:
|
|
||||||
# [bot]
|
|
||||||
# nickname = "MaiBot"
|
|
||||||
# qq_account = "123456"
|
|
||||||
#
|
|
||||||
# [model.default]
|
|
||||||
# model_name = "gpt-3.5-turbo"
|
|
||||||
# temperature = 0.7
|
|
||||||
|
|
||||||
# API调用:
|
|
||||||
bot_name = config_api.get_global_config("bot.nickname")
|
|
||||||
model_name = config_api.get_global_config("model.default.model_name")
|
|
||||||
temperature = config_api.get_global_config("model.default.temperature")
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **只读访问**:配置API只提供读取功能,插件不能修改全局配置
|
1. **只读访问**:配置API只提供读取功能,插件不能修改全局配置
|
||||||
2. **异步函数**:用户信息相关的函数是异步的,需要使用`await`
|
2. **错误处理**:所有函数都有错误处理,失败时会记录日志并返回默认值
|
||||||
3. **错误处理**:所有函数都有错误处理,失败时会记录日志并返回默认值
|
3. **安全性**:插件通过此API访问配置是安全和隔离的
|
||||||
4. **安全性**:插件通过此API访问配置是安全和隔离的
|
4. **性能**:频繁访问的配置建议在插件初始化时获取并缓存
|
||||||
5. **性能**:频繁访问的配置建议在插件初始化时获取并缓存
|
|
||||||
@@ -6,72 +6,51 @@
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import database_api
|
from src.plugin_system.apis import database_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import database_api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主要功能
|
## 主要功能
|
||||||
|
|
||||||
### 1. 通用数据库查询
|
### 1. 通用数据库操作
|
||||||
|
|
||||||
#### `db_query(model_class, query_type="get", filters=None, data=None, limit=None, order_by=None, single_result=False)`
|
|
||||||
执行数据库查询操作的通用接口
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `model_class`:Peewee模型类,如ActionRecords、Messages等
|
|
||||||
- `query_type`:查询类型,可选值: "get", "create", "update", "delete", "count"
|
|
||||||
- `filters`:过滤条件字典,键为字段名,值为要匹配的值
|
|
||||||
- `data`:用于创建或更新的数据字典
|
|
||||||
- `limit`:限制结果数量
|
|
||||||
- `order_by`:排序字段列表,使用字段名,前缀'-'表示降序
|
|
||||||
- `single_result`:是否只返回单个结果
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
根据查询类型返回不同的结果:
|
|
||||||
- "get":返回查询结果列表或单个结果
|
|
||||||
- "create":返回创建的记录
|
|
||||||
- "update":返回受影响的行数
|
|
||||||
- "delete":返回受影响的行数
|
|
||||||
- "count":返回记录数量
|
|
||||||
|
|
||||||
### 2. 便捷查询函数
|
|
||||||
|
|
||||||
#### `db_save(model_class, data, key_field=None, key_value=None)`
|
|
||||||
保存数据到数据库(创建或更新)
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `model_class`:Peewee模型类
|
|
||||||
- `data`:要保存的数据字典
|
|
||||||
- `key_field`:用于查找现有记录的字段名
|
|
||||||
- `key_value`:用于查找现有记录的字段值
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Dict[str, Any]`:保存后的记录数据,失败时返回None
|
|
||||||
|
|
||||||
#### `db_get(model_class, filters=None, order_by=None, limit=None)`
|
|
||||||
简化的查询函数
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `model_class`:Peewee模型类
|
|
||||||
- `filters`:过滤条件字典
|
|
||||||
- `order_by`:排序字段
|
|
||||||
- `limit`:限制结果数量
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Union[List[Dict], Dict, None]`:查询结果
|
|
||||||
|
|
||||||
### 3. 专用函数
|
|
||||||
|
|
||||||
#### `store_action_info(...)`
|
|
||||||
存储动作信息的专用函数
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 1. 基本查询操作
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import database_api
|
async def db_query(
|
||||||
from src.common.database.database_model import Messages, ActionRecords
|
model_class: Type[Model],
|
||||||
|
data: Optional[Dict[str, Any]] = None,
|
||||||
|
query_type: Optional[str] = "get",
|
||||||
|
filters: Optional[Dict[str, Any]] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
order_by: Optional[List[str]] = None,
|
||||||
|
single_result: Optional[bool] = False,
|
||||||
|
) -> Union[List[Dict[str, Any]], Dict[str, Any], None]:
|
||||||
|
```
|
||||||
|
执行数据库查询操作的通用接口。
|
||||||
|
|
||||||
# 查询最近10条消息
|
**Args:**
|
||||||
|
- `model_class`: Peewee模型类。
|
||||||
|
- Peewee模型类可以在`src.common.database.database_model`模块中找到,如`ActionRecords`、`Messages`等。
|
||||||
|
- `data`: 用于创建或更新的数据
|
||||||
|
- `query_type`: 查询类型
|
||||||
|
- 可选值: `get`, `create`, `update`, `delete`, `count`。
|
||||||
|
- `filters`: 过滤条件字典,键为字段名,值为要匹配的值。
|
||||||
|
- `limit`: 限制结果数量。
|
||||||
|
- `order_by`: 排序字段列表,使用字段名,前缀'-'表示降序。
|
||||||
|
- 排序字段,前缀`-`表示降序,例如`-time`表示按时间字段(即`time`字段)降序
|
||||||
|
- `single_result`: 是否只返回单个结果。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- 根据查询类型返回不同的结果:
|
||||||
|
- `get`: 返回查询结果列表或单个结果。(如果 `single_result=True`)
|
||||||
|
- `create`: 返回创建的记录。
|
||||||
|
- `update`: 返回受影响的行数。
|
||||||
|
- `delete`: 返回受影响的行数。
|
||||||
|
- `count`: 返回记录数量。
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
|
1. 查询最近10条消息
|
||||||
|
```python
|
||||||
messages = await database_api.db_query(
|
messages = await database_api.db_query(
|
||||||
Messages,
|
Messages,
|
||||||
query_type="get",
|
query_type="get",
|
||||||
@@ -79,180 +58,159 @@ messages = await database_api.db_query(
|
|||||||
limit=10,
|
limit=10,
|
||||||
order_by=["-time"]
|
order_by=["-time"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# 查询单条记录
|
|
||||||
message = await database_api.db_query(
|
|
||||||
Messages,
|
|
||||||
query_type="get",
|
|
||||||
filters={"message_id": "msg_123"},
|
|
||||||
single_result=True
|
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
2. 创建一条记录
|
||||||
### 2. 创建记录
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 创建新的动作记录
|
|
||||||
new_record = await database_api.db_query(
|
new_record = await database_api.db_query(
|
||||||
ActionRecords,
|
ActionRecords,
|
||||||
|
data={"action_id": "123", "time": time.time(), "action_name": "TestAction"},
|
||||||
query_type="create",
|
query_type="create",
|
||||||
data={
|
|
||||||
"action_id": "action_123",
|
|
||||||
"time": time.time(),
|
|
||||||
"action_name": "TestAction",
|
|
||||||
"action_done": True
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"创建了记录: {new_record['id']}")
|
|
||||||
```
|
```
|
||||||
|
3. 更新记录
|
||||||
### 3. 更新记录
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 更新动作状态
|
|
||||||
updated_count = await database_api.db_query(
|
updated_count = await database_api.db_query(
|
||||||
ActionRecords,
|
ActionRecords,
|
||||||
|
data={"action_done": True},
|
||||||
query_type="update",
|
query_type="update",
|
||||||
filters={"action_id": "action_123"},
|
filters={"action_id": "123"},
|
||||||
data={"action_done": True, "completion_time": time.time()}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"更新了 {updated_count} 条记录")
|
|
||||||
```
|
```
|
||||||
|
4. 删除记录
|
||||||
### 4. 删除记录
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 删除过期记录
|
|
||||||
deleted_count = await database_api.db_query(
|
deleted_count = await database_api.db_query(
|
||||||
ActionRecords,
|
ActionRecords,
|
||||||
query_type="delete",
|
query_type="delete",
|
||||||
filters={"time__lt": time.time() - 86400} # 删除24小时前的记录
|
filters={"action_id": "123"}
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"删除了 {deleted_count} 条过期记录")
|
|
||||||
```
|
```
|
||||||
|
5. 计数
|
||||||
### 5. 统计查询
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 统计消息数量
|
count = await database_api.db_query(
|
||||||
message_count = await database_api.db_query(
|
|
||||||
Messages,
|
Messages,
|
||||||
query_type="count",
|
query_type="count",
|
||||||
filters={"chat_id": chat_stream.stream_id}
|
filters={"chat_id": chat_stream.stream_id}
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"该聊天有 {message_count} 条消息")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. 使用便捷函数
|
### 2. 数据库保存
|
||||||
|
```python
|
||||||
|
async def db_save(
|
||||||
|
model_class: Type[Model], data: Dict[str, Any], key_field: Optional[str] = None, key_value: Optional[Any] = None
|
||||||
|
) -> Optional[Dict[str, Any]]:
|
||||||
|
```
|
||||||
|
保存数据到数据库(创建或更新)
|
||||||
|
|
||||||
|
如果提供了key_field和key_value,会先尝试查找匹配的记录进行更新;
|
||||||
|
|
||||||
|
如果没有找到匹配记录,或未提供key_field和key_value,则创建新记录。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `model_class`: Peewee模型类。
|
||||||
|
- `data`: 要保存的数据字典。
|
||||||
|
- `key_field`: 用于查找现有记录的字段名,例如"action_id"。
|
||||||
|
- `key_value`: 用于查找现有记录的字段值。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Optional[Dict[str, Any]]`: 保存后的记录数据,失败时返回None。
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
创建或更新一条记录
|
||||||
```python
|
```python
|
||||||
# 使用db_save进行创建或更新
|
|
||||||
record = await database_api.db_save(
|
record = await database_api.db_save(
|
||||||
ActionRecords,
|
ActionRecords,
|
||||||
{
|
{
|
||||||
"action_id": "action_123",
|
"action_id": "123",
|
||||||
"time": time.time(),
|
"time": time.time(),
|
||||||
"action_name": "TestAction",
|
"action_name": "TestAction",
|
||||||
"action_done": True
|
"action_done": True
|
||||||
},
|
},
|
||||||
key_field="action_id",
|
key_field="action_id",
|
||||||
key_value="action_123"
|
key_value="123"
|
||||||
)
|
)
|
||||||
|
```
|
||||||
|
|
||||||
# 使用db_get进行简单查询
|
### 3. 数据库获取
|
||||||
recent_messages = await database_api.db_get(
|
```python
|
||||||
|
async def db_get(
|
||||||
|
model_class: Type[Model],
|
||||||
|
filters: Optional[Dict[str, Any]] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
order_by: Optional[str] = None,
|
||||||
|
single_result: Optional[bool] = False,
|
||||||
|
) -> Union[List[Dict[str, Any]], Dict[str, Any], None]:
|
||||||
|
```
|
||||||
|
|
||||||
|
从数据库获取记录
|
||||||
|
|
||||||
|
这是db_query方法的简化版本,专注于数据检索操作。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `model_class`: Peewee模型类。
|
||||||
|
- `filters`: 过滤条件字典,键为字段名,值为要匹配的值。
|
||||||
|
- `limit`: 限制结果数量。
|
||||||
|
- `order_by`: 排序字段,使用字段名,前缀'-'表示降序。
|
||||||
|
- `single_result`: 是否只返回单个结果,如果为True,则返回单个记录字典或None;否则返回记录字典列表或空列表
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Union[List[Dict], Dict, None]`: 查询结果列表或单个结果(如果`single_result=True`),失败时返回None。
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
1. 获取单个记录
|
||||||
|
```python
|
||||||
|
record = await database_api.db_get(
|
||||||
|
ActionRecords,
|
||||||
|
filters={"action_id": "123"},
|
||||||
|
limit=1
|
||||||
|
)
|
||||||
|
```
|
||||||
|
2. 获取最近10条记录
|
||||||
|
```python
|
||||||
|
records = await database_api.db_get(
|
||||||
Messages,
|
Messages,
|
||||||
filters={"chat_id": chat_stream.stream_id},
|
filters={"chat_id": chat_stream.stream_id},
|
||||||
|
limit=10,
|
||||||
order_by="-time",
|
order_by="-time",
|
||||||
limit=5
|
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 高级用法
|
### 4. 动作信息存储
|
||||||
|
|
||||||
### 复杂查询示例
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 查询特定用户在特定时间段的消息
|
async def store_action_info(
|
||||||
user_messages = await database_api.db_query(
|
chat_stream=None,
|
||||||
Messages,
|
action_build_into_prompt: bool = False,
|
||||||
query_type="get",
|
action_prompt_display: str = "",
|
||||||
filters={
|
action_done: bool = True,
|
||||||
"user_id": "123456",
|
thinking_id: str = "",
|
||||||
"time__gte": start_time, # 大于等于开始时间
|
action_data: Optional[dict] = None,
|
||||||
"time__lt": end_time # 小于结束时间
|
action_name: str = "",
|
||||||
},
|
) -> Optional[Dict[str, Any]]:
|
||||||
order_by=["-time"],
|
```
|
||||||
limit=50
|
存储动作信息到数据库,是一种针对 Action 的 `db_save()` 的封装函数。
|
||||||
|
|
||||||
|
将Action执行的相关信息保存到ActionRecords表中,用于后续的记忆和上下文构建。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `chat_stream`: 聊天流对象,包含聊天ID等信息。
|
||||||
|
- `action_build_into_prompt`: 是否将动作信息构建到提示中。
|
||||||
|
- `action_prompt_display`: 动作提示的显示文本。
|
||||||
|
- `action_done`: 动作是否完成。
|
||||||
|
- `thinking_id`: 思考过程的ID。
|
||||||
|
- `action_data`: 动作的数据字典。
|
||||||
|
- `action_name`: 动作的名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Optional[Dict[str, Any]]`: 存储后的记录数据,失败时返回None。
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
```python
|
||||||
|
record = await database_api.store_action_info(
|
||||||
|
chat_stream=chat_stream,
|
||||||
|
action_build_into_prompt=True,
|
||||||
|
action_prompt_display="执行了回复动作",
|
||||||
|
action_done=True,
|
||||||
|
thinking_id="thinking_123",
|
||||||
|
action_data={"content": "Hello"},
|
||||||
|
action_name="reply_action"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 批量处理
|
|
||||||
for message in user_messages:
|
|
||||||
print(f"消息内容: {message['plain_text']}")
|
|
||||||
print(f"发送时间: {message['time']}")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 插件中的数据持久化
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.base import BasePlugin
|
|
||||||
from src.plugin_system.apis import database_api
|
|
||||||
|
|
||||||
class DataPlugin(BasePlugin):
|
|
||||||
async def handle_action(self, action_data, chat_stream):
|
|
||||||
# 保存插件数据
|
|
||||||
plugin_data = {
|
|
||||||
"plugin_name": self.plugin_name,
|
|
||||||
"chat_id": chat_stream.stream_id,
|
|
||||||
"data": json.dumps(action_data),
|
|
||||||
"created_time": time.time()
|
|
||||||
}
|
|
||||||
|
|
||||||
# 使用自定义表模型(需要先定义)
|
|
||||||
record = await database_api.db_save(
|
|
||||||
PluginData, # 假设的插件数据模型
|
|
||||||
plugin_data,
|
|
||||||
key_field="plugin_name",
|
|
||||||
key_value=self.plugin_name
|
|
||||||
)
|
|
||||||
|
|
||||||
return {"success": True, "record_id": record["id"]}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 数据模型
|
|
||||||
|
|
||||||
### 常用模型类
|
|
||||||
系统提供了以下常用的数据模型:
|
|
||||||
|
|
||||||
- `Messages`:消息记录
|
|
||||||
- `ActionRecords`:动作记录
|
|
||||||
- `UserInfo`:用户信息
|
|
||||||
- `GroupInfo`:群组信息
|
|
||||||
|
|
||||||
### 字段说明
|
|
||||||
|
|
||||||
#### Messages模型主要字段
|
|
||||||
- `message_id`:消息ID
|
|
||||||
- `chat_id`:聊天ID
|
|
||||||
- `user_id`:用户ID
|
|
||||||
- `plain_text`:纯文本内容
|
|
||||||
- `time`:时间戳
|
|
||||||
|
|
||||||
#### ActionRecords模型主要字段
|
|
||||||
- `action_id`:动作ID
|
|
||||||
- `action_name`:动作名称
|
|
||||||
- `action_done`:是否完成
|
|
||||||
- `time`:创建时间
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **异步操作**:所有数据库API都是异步的,必须使用`await`
|
|
||||||
2. **错误处理**:函数内置错误处理,失败时返回None或空列表
|
|
||||||
3. **数据类型**:返回的都是字典格式的数据,不是模型对象
|
|
||||||
4. **性能考虑**:使用`limit`参数避免查询大量数据
|
|
||||||
5. **过滤条件**:支持简单的等值过滤,复杂查询需要使用原生Peewee语法
|
|
||||||
6. **事务**:如需事务支持,建议直接使用Peewee的事务功能
|
|
||||||
@@ -6,11 +6,13 @@
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import emoji_api
|
from src.plugin_system.apis import emoji_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import emoji_api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🆕 **二步走识别优化**
|
## 二步走识别优化
|
||||||
|
|
||||||
从最新版本开始,表情包识别系统采用了**二步走识别 + 智能缓存**的优化方案:
|
从新版本开始,表情包识别系统采用了**二步走识别 + 智能缓存**的优化方案:
|
||||||
|
|
||||||
### **收到表情包时的识别流程**
|
### **收到表情包时的识别流程**
|
||||||
1. **第一步**:VLM视觉分析 - 生成详细描述
|
1. **第一步**:VLM视觉分析 - 生成详细描述
|
||||||
@@ -30,217 +32,84 @@ from src.plugin_system.apis import emoji_api
|
|||||||
## 主要功能
|
## 主要功能
|
||||||
|
|
||||||
### 1. 表情包获取
|
### 1. 表情包获取
|
||||||
|
```python
|
||||||
#### `get_by_description(description: str) -> Optional[Tuple[str, str, str]]`
|
async def get_by_description(description: str) -> Optional[Tuple[str, str, str]]:
|
||||||
|
```
|
||||||
根据场景描述选择表情包
|
根据场景描述选择表情包
|
||||||
|
|
||||||
**参数:**
|
**Args:**
|
||||||
- `description`:场景描述文本,例如"开心的大笑"、"轻微的讽刺"、"表示无奈和沮丧"等
|
- `description`:表情包的描述文本,例如"开心"、"难过"、"愤怒"等
|
||||||
|
|
||||||
**返回:**
|
**Returns:**
|
||||||
- `Optional[Tuple[str, str, str]]`:(base64编码, 表情包描述, 匹配的场景) 或 None
|
- `Optional[Tuple[str, str, str]]`:一个元组: (表情包的base64编码, 描述, 情感标签),如果未找到匹配的表情包则返回None
|
||||||
|
|
||||||
**示例:**
|
#### 示例
|
||||||
```python
|
```python
|
||||||
emoji_result = await emoji_api.get_by_description("开心的大笑")
|
emoji_result = await emoji_api.get_by_description("大笑")
|
||||||
if emoji_result:
|
if emoji_result:
|
||||||
emoji_base64, description, matched_scene = emoji_result
|
emoji_base64, description, matched_scene = emoji_result
|
||||||
print(f"获取到表情包: {description}, 场景: {matched_scene}")
|
print(f"获取到表情包: {description}, 场景: {matched_scene}")
|
||||||
# 可以将emoji_base64用于发送表情包
|
# 可以将emoji_base64用于发送表情包
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_random() -> Optional[Tuple[str, str, str]]`
|
### 2. 随机获取表情包
|
||||||
随机获取表情包
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Optional[Tuple[str, str, str]]`:(base64编码, 表情包描述, 随机场景) 或 None
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
random_emoji = await emoji_api.get_random()
|
async def get_random(count: Optional[int] = 1) -> List[Tuple[str, str, str]]:
|
||||||
if random_emoji:
|
|
||||||
emoji_base64, description, scene = random_emoji
|
|
||||||
print(f"随机表情包: {description}")
|
|
||||||
```
|
```
|
||||||
|
随机获取指定数量的表情包
|
||||||
|
|
||||||
#### `get_by_emotion(emotion: str) -> Optional[Tuple[str, str, str]]`
|
**Args:**
|
||||||
根据场景关键词获取表情包
|
- `count`:要获取的表情包数量,默认为1
|
||||||
|
|
||||||
**参数:**
|
**Returns:**
|
||||||
- `emotion`:场景关键词,如"大笑"、"讽刺"、"无奈"等
|
- `List[Tuple[str, str, str]]`:一个包含多个表情包的列表,每个元素是一个元组: (表情包的base64编码, 描述, 情感标签),如果未找到或出错则返回空列表
|
||||||
|
|
||||||
**返回:**
|
### 3. 根据情感获取表情包
|
||||||
- `Optional[Tuple[str, str, str]]`:(base64编码, 表情包描述, 匹配的场景) 或 None
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
emoji_result = await emoji_api.get_by_emotion("讽刺")
|
async def get_by_emotion(emotion: str) -> Optional[Tuple[str, str, str]]:
|
||||||
if emoji_result:
|
|
||||||
emoji_base64, description, scene = emoji_result
|
|
||||||
# 发送讽刺表情包
|
|
||||||
```
|
```
|
||||||
|
根据情感标签获取表情包
|
||||||
|
|
||||||
### 2. 表情包信息查询
|
**Args:**
|
||||||
|
- `emotion`:情感标签,例如"开心"、"悲伤"、"愤怒"等
|
||||||
|
|
||||||
#### `get_count() -> int`
|
**Returns:**
|
||||||
获取表情包数量
|
- `Optional[Tuple[str, str, str]]`:一个元组: (表情包的base64编码, 描述, 情感标签),如果未找到则返回None
|
||||||
|
|
||||||
**返回:**
|
### 4. 获取表情包数量
|
||||||
- `int`:当前可用的表情包数量
|
```python
|
||||||
|
def get_count() -> int:
|
||||||
|
```
|
||||||
|
获取当前可用表情包的数量
|
||||||
|
|
||||||
#### `get_info() -> dict`
|
### 5. 获取表情包系统信息
|
||||||
获取表情包系统信息
|
```python
|
||||||
|
def get_info() -> Dict[str, Any]:
|
||||||
|
```
|
||||||
|
获取表情包系统的基本信息
|
||||||
|
|
||||||
**返回:**
|
**Returns:**
|
||||||
- `dict`:包含表情包数量、最大数量等信息
|
- `Dict[str, Any]`:包含表情包数量、描述等信息的字典,包含以下键:
|
||||||
|
- `current_count`:当前表情包数量
|
||||||
|
- `max_count`:最大表情包数量
|
||||||
|
- `available_emojis`:当前可用的表情包数量
|
||||||
|
|
||||||
**返回字典包含:**
|
### 6. 获取所有可用的情感标签
|
||||||
- `current_count`:当前表情包数量
|
```python
|
||||||
- `max_count`:最大表情包数量
|
def get_emotions() -> List[str]:
|
||||||
- `available_emojis`:可用表情包数量
|
```
|
||||||
|
获取所有可用的情感标签 **(已经去重)**
|
||||||
|
|
||||||
#### `get_emotions() -> list`
|
### 7. 获取所有表情包描述
|
||||||
获取所有可用的场景关键词
|
```python
|
||||||
|
def get_descriptions() -> List[str]:
|
||||||
**返回:**
|
```
|
||||||
- `list`:所有表情包的场景关键词列表(去重)
|
|
||||||
|
|
||||||
#### `get_descriptions() -> list`
|
|
||||||
获取所有表情包的描述列表
|
获取所有表情包的描述列表
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `list`:所有表情包的描述文本列表
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 1. 智能表情包选择
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.apis import emoji_api
|
|
||||||
|
|
||||||
async def send_emotion_response(message_text: str, chat_stream):
|
|
||||||
"""根据消息内容智能选择表情包回复"""
|
|
||||||
|
|
||||||
# 分析消息场景
|
|
||||||
if "哈哈" in message_text or "好笑" in message_text:
|
|
||||||
emoji_result = await emoji_api.get_by_description("开心的大笑")
|
|
||||||
elif "无语" in message_text or "算了" in message_text:
|
|
||||||
emoji_result = await emoji_api.get_by_description("表示无奈和沮丧")
|
|
||||||
elif "呵呵" in message_text or "是吗" in message_text:
|
|
||||||
emoji_result = await emoji_api.get_by_description("轻微的讽刺")
|
|
||||||
elif "生气" in message_text or "愤怒" in message_text:
|
|
||||||
emoji_result = await emoji_api.get_by_description("愤怒和不满")
|
|
||||||
else:
|
|
||||||
# 随机选择一个表情包
|
|
||||||
emoji_result = await emoji_api.get_random()
|
|
||||||
|
|
||||||
if emoji_result:
|
|
||||||
emoji_base64, description, scene = emoji_result
|
|
||||||
# 使用send_api发送表情包
|
|
||||||
from src.plugin_system.apis import send_api
|
|
||||||
success = await send_api.emoji_to_group(emoji_base64, chat_stream.group_info.group_id)
|
|
||||||
return success
|
|
||||||
|
|
||||||
return False
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 表情包管理功能
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def show_emoji_stats():
|
|
||||||
"""显示表情包统计信息"""
|
|
||||||
|
|
||||||
# 获取基本信息
|
|
||||||
count = emoji_api.get_count()
|
|
||||||
info = emoji_api.get_info()
|
|
||||||
scenes = emoji_api.get_emotions() # 实际返回的是场景关键词
|
|
||||||
|
|
||||||
stats = f"""
|
|
||||||
📊 表情包统计信息:
|
|
||||||
- 总数量: {count}
|
|
||||||
- 可用数量: {info['available_emojis']}
|
|
||||||
- 最大容量: {info['max_count']}
|
|
||||||
- 支持场景: {len(scenes)}种
|
|
||||||
|
|
||||||
🎭 支持的场景关键词: {', '.join(scenes[:10])}{'...' if len(scenes) > 10 else ''}
|
|
||||||
"""
|
|
||||||
|
|
||||||
return stats
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 表情包测试功能
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def test_emoji_system():
|
|
||||||
"""测试表情包系统的各种功能"""
|
|
||||||
|
|
||||||
print("=== 表情包系统测试 ===")
|
|
||||||
|
|
||||||
# 测试场景描述查找
|
|
||||||
test_descriptions = ["开心的大笑", "轻微的讽刺", "表示无奈和沮丧", "愤怒和不满"]
|
|
||||||
for desc in test_descriptions:
|
|
||||||
result = await emoji_api.get_by_description(desc)
|
|
||||||
if result:
|
|
||||||
_, description, scene = result
|
|
||||||
print(f"✅ 场景'{desc}' -> {description} ({scene})")
|
|
||||||
else:
|
|
||||||
print(f"❌ 场景'{desc}' -> 未找到")
|
|
||||||
|
|
||||||
# 测试关键词查找
|
|
||||||
scenes = emoji_api.get_emotions()
|
|
||||||
if scenes:
|
|
||||||
test_scene = scenes[0]
|
|
||||||
result = await emoji_api.get_by_emotion(test_scene)
|
|
||||||
if result:
|
|
||||||
print(f"✅ 关键词'{test_scene}' -> 找到匹配表情包")
|
|
||||||
|
|
||||||
# 测试随机获取
|
|
||||||
random_result = await emoji_api.get_random()
|
|
||||||
if random_result:
|
|
||||||
print("✅ 随机获取 -> 成功")
|
|
||||||
|
|
||||||
print(f"📊 系统信息: {emoji_api.get_info()}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 在Action中使用表情包
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.base import BaseAction
|
|
||||||
|
|
||||||
class EmojiAction(BaseAction):
|
|
||||||
async def execute(self, action_data, chat_stream):
|
|
||||||
# 从action_data获取场景描述或关键词
|
|
||||||
scene_keyword = action_data.get("scene", "")
|
|
||||||
scene_description = action_data.get("description", "")
|
|
||||||
|
|
||||||
emoji_result = None
|
|
||||||
|
|
||||||
# 优先使用具体的场景描述
|
|
||||||
if scene_description:
|
|
||||||
emoji_result = await emoji_api.get_by_description(scene_description)
|
|
||||||
# 其次使用场景关键词
|
|
||||||
elif scene_keyword:
|
|
||||||
emoji_result = await emoji_api.get_by_emotion(scene_keyword)
|
|
||||||
# 最后随机选择
|
|
||||||
else:
|
|
||||||
emoji_result = await emoji_api.get_random()
|
|
||||||
|
|
||||||
if emoji_result:
|
|
||||||
emoji_base64, description, scene = emoji_result
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"emoji_base64": emoji_base64,
|
|
||||||
"description": description,
|
|
||||||
"scene": scene
|
|
||||||
}
|
|
||||||
|
|
||||||
return {"success": False, "message": "未找到合适的表情包"}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 场景描述说明
|
## 场景描述说明
|
||||||
|
|
||||||
### 常用场景描述
|
### 常用场景描述
|
||||||
表情包系统支持多种具体的场景描述,常见的包括:
|
表情包系统支持多种具体的场景描述,举例如下:
|
||||||
|
|
||||||
- **开心类场景**:开心的大笑、满意的微笑、兴奋的手舞足蹈
|
- **开心类场景**:开心的大笑、满意的微笑、兴奋的手舞足蹈
|
||||||
- **无奈类场景**:表示无奈和沮丧、轻微的讽刺、无语的摇头
|
- **无奈类场景**:表示无奈和沮丧、轻微的讽刺、无语的摇头
|
||||||
@@ -248,8 +117,8 @@ class EmojiAction(BaseAction):
|
|||||||
- **惊讶类场景**:震惊的表情、意外的发现、困惑的思考
|
- **惊讶类场景**:震惊的表情、意外的发现、困惑的思考
|
||||||
- **可爱类场景**:卖萌的表情、撒娇的动作、害羞的样子
|
- **可爱类场景**:卖萌的表情、撒娇的动作、害羞的样子
|
||||||
|
|
||||||
### 场景关键词示例
|
### 情感关键词示例
|
||||||
系统支持的场景关键词包括:
|
系统支持的情感关键词举例如下:
|
||||||
- 大笑、微笑、兴奋、手舞足蹈
|
- 大笑、微笑、兴奋、手舞足蹈
|
||||||
- 无奈、沮丧、讽刺、无语、摇头
|
- 无奈、沮丧、讽刺、无语、摇头
|
||||||
- 愤怒、不满、生气、瞪视、抓狂
|
- 愤怒、不满、生气、瞪视、抓狂
|
||||||
@@ -263,9 +132,9 @@ class EmojiAction(BaseAction):
|
|||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **异步函数**:获取表情包的函数都是异步的,需要使用 `await`
|
1. **异步函数**:部分函数是异步的,需要使用 `await`
|
||||||
2. **返回格式**:表情包以base64编码返回,可直接用于发送
|
2. **返回格式**:表情包以base64编码返回,可直接用于发送
|
||||||
3. **错误处理**:所有函数都有错误处理,失败时返回None或默认值
|
3. **错误处理**:所有函数都有错误处理,失败时返回None,空列表或默认值
|
||||||
4. **使用统计**:系统会记录表情包的使用次数
|
4. **使用统计**:系统会记录表情包的使用次数
|
||||||
5. **文件依赖**:表情包依赖于本地文件,确保表情包文件存在
|
5. **文件依赖**:表情包依赖于本地文件,确保表情包文件存在
|
||||||
6. **编码格式**:返回的是base64编码的图片数据,可直接用于网络传输
|
6. **编码格式**:返回的是base64编码的图片数据,可直接用于网络传输
|
||||||
|
|||||||
@@ -6,241 +6,150 @@
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import generator_api
|
from src.plugin_system.apis import generator_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import generator_api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主要功能
|
## 主要功能
|
||||||
|
|
||||||
### 1. 回复器获取
|
### 1. 回复器获取
|
||||||
|
```python
|
||||||
#### `get_replyer(chat_stream=None, platform=None, chat_id=None, is_group=True)`
|
def get_replyer(
|
||||||
|
chat_stream: Optional[ChatStream] = None,
|
||||||
|
chat_id: Optional[str] = None,
|
||||||
|
model_configs: Optional[List[Dict[str, Any]]] = None,
|
||||||
|
request_type: str = "replyer",
|
||||||
|
) -> Optional[DefaultReplyer]:
|
||||||
|
```
|
||||||
获取回复器对象
|
获取回复器对象
|
||||||
|
|
||||||
**参数:**
|
优先使用chat_stream,如果没有则使用chat_id直接查找。
|
||||||
- `chat_stream`:聊天流对象(优先)
|
|
||||||
- `platform`:平台名称,如"qq"
|
|
||||||
- `chat_id`:聊天ID(群ID或用户ID)
|
|
||||||
- `is_group`:是否为群聊
|
|
||||||
|
|
||||||
**返回:**
|
使用 ReplyerManager 来管理实例,避免重复创建。
|
||||||
- `DefaultReplyer`:回复器对象,如果获取失败则返回None
|
|
||||||
|
|
||||||
**示例:**
|
**Args:**
|
||||||
|
- `chat_stream`: 聊天流对象
|
||||||
|
- `chat_id`: 聊天ID(实际上就是`stream_id`)
|
||||||
|
- `model_configs`: 模型配置
|
||||||
|
- `request_type`: 请求类型,用于记录LLM使用情况,可以不写
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `DefaultReplyer`: 回复器对象,如果获取失败则返回None
|
||||||
|
|
||||||
|
#### 示例
|
||||||
```python
|
```python
|
||||||
# 使用聊天流获取回复器
|
# 使用聊天流获取回复器
|
||||||
replyer = generator_api.get_replyer(chat_stream=chat_stream)
|
replyer = generator_api.get_replyer(chat_stream=chat_stream)
|
||||||
|
|
||||||
# 使用平台和ID获取回复器
|
# 使用平台和ID获取回复器
|
||||||
replyer = generator_api.get_replyer(
|
replyer = generator_api.get_replyer(chat_id="123456789")
|
||||||
platform="qq",
|
|
||||||
chat_id="123456789",
|
|
||||||
is_group=True
|
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 回复生成
|
### 2. 回复生成
|
||||||
|
```python
|
||||||
#### `generate_reply(chat_stream=None, action_data=None, platform=None, chat_id=None, is_group=True)`
|
async def generate_reply(
|
||||||
|
chat_stream: Optional[ChatStream] = None,
|
||||||
|
chat_id: Optional[str] = None,
|
||||||
|
action_data: Optional[Dict[str, Any]] = None,
|
||||||
|
reply_to: str = "",
|
||||||
|
extra_info: str = "",
|
||||||
|
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
||||||
|
enable_tool: bool = False,
|
||||||
|
enable_splitter: bool = True,
|
||||||
|
enable_chinese_typo: bool = True,
|
||||||
|
return_prompt: bool = False,
|
||||||
|
model_configs: Optional[List[Dict[str, Any]]] = None,
|
||||||
|
request_type: str = "",
|
||||||
|
) -> Tuple[bool, List[Tuple[str, Any]], Optional[str]]:
|
||||||
|
```
|
||||||
生成回复
|
生成回复
|
||||||
|
|
||||||
**参数:**
|
优先使用chat_stream,如果没有则使用chat_id直接查找。
|
||||||
- `chat_stream`:聊天流对象(优先)
|
|
||||||
- `action_data`:动作数据
|
|
||||||
- `platform`:平台名称(备用)
|
|
||||||
- `chat_id`:聊天ID(备用)
|
|
||||||
- `is_group`:是否为群聊(备用)
|
|
||||||
|
|
||||||
**返回:**
|
**Args:**
|
||||||
- `Tuple[bool, List[Tuple[str, Any]]]`:(是否成功, 回复集合)
|
- `chat_stream`: 聊天流对象
|
||||||
|
- `chat_id`: 聊天ID(实际上就是`stream_id`)
|
||||||
|
- `action_data`: 动作数据(向下兼容,包含`reply_to`和`extra_info`)
|
||||||
|
- `reply_to`: 回复目标,格式为 `{发送者的person_name:消息内容}`
|
||||||
|
- `extra_info`: 附加信息
|
||||||
|
- `available_actions`: 可用动作字典,格式为 `{"action_name": ActionInfo}`
|
||||||
|
- `enable_tool`: 是否启用工具
|
||||||
|
- `enable_splitter`: 是否启用分割器
|
||||||
|
- `enable_chinese_typo`: 是否启用中文错别字
|
||||||
|
- `return_prompt`: 是否返回提示词
|
||||||
|
- `model_configs`: 模型配置,可选
|
||||||
|
- `request_type`: 请求类型,用于记录LLM使用情况
|
||||||
|
|
||||||
**示例:**
|
**Returns:**
|
||||||
|
- `Tuple[bool, List[Tuple[str, Any]], Optional[str]]`: (是否成功, 回复集合, 提示词)
|
||||||
|
|
||||||
|
#### 示例
|
||||||
```python
|
```python
|
||||||
success, reply_set = await generator_api.generate_reply(
|
success, reply_set, prompt = await generator_api.generate_reply(
|
||||||
chat_stream=chat_stream,
|
chat_stream=chat_stream,
|
||||||
action_data={"message": "你好", "intent": "greeting"}
|
action_data=action_data,
|
||||||
|
reply_to="麦麦:你好",
|
||||||
|
available_actions=action_info,
|
||||||
|
enable_tool=True,
|
||||||
|
return_prompt=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
for reply_type, reply_content in reply_set:
|
for reply_type, reply_content in reply_set:
|
||||||
print(f"回复类型: {reply_type}, 内容: {reply_content}")
|
print(f"回复类型: {reply_type}, 内容: {reply_content}")
|
||||||
|
if prompt:
|
||||||
|
print(f"使用的提示词: {prompt}")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `rewrite_reply(chat_stream=None, reply_data=None, platform=None, chat_id=None, is_group=True)`
|
### 3. 回复重写
|
||||||
重写回复
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `chat_stream`:聊天流对象(优先)
|
|
||||||
- `reply_data`:回复数据
|
|
||||||
- `platform`:平台名称(备用)
|
|
||||||
- `chat_id`:聊天ID(备用)
|
|
||||||
- `is_group`:是否为群聊(备用)
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Tuple[bool, List[Tuple[str, Any]]]`:(是否成功, 回复集合)
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
success, reply_set = await generator_api.rewrite_reply(
|
async def rewrite_reply(
|
||||||
|
chat_stream: Optional[ChatStream] = None,
|
||||||
|
reply_data: Optional[Dict[str, Any]] = None,
|
||||||
|
chat_id: Optional[str] = None,
|
||||||
|
enable_splitter: bool = True,
|
||||||
|
enable_chinese_typo: bool = True,
|
||||||
|
model_configs: Optional[List[Dict[str, Any]]] = None,
|
||||||
|
raw_reply: str = "",
|
||||||
|
reason: str = "",
|
||||||
|
reply_to: str = "",
|
||||||
|
return_prompt: bool = False,
|
||||||
|
) -> Tuple[bool, List[Tuple[str, Any]], Optional[str]]:
|
||||||
|
```
|
||||||
|
重写回复,使用新的内容替换旧的回复内容。
|
||||||
|
|
||||||
|
优先使用chat_stream,如果没有则使用chat_id直接查找。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `chat_stream`: 聊天流对象
|
||||||
|
- `reply_data`: 回复数据,包含`raw_reply`, `reason`和`reply_to`,**(向下兼容备用,当其他参数缺失时从此获取)**
|
||||||
|
- `chat_id`: 聊天ID(实际上就是`stream_id`)
|
||||||
|
- `enable_splitter`: 是否启用分割器
|
||||||
|
- `enable_chinese_typo`: 是否启用中文错别字
|
||||||
|
- `model_configs`: 模型配置,可选
|
||||||
|
- `raw_reply`: 原始回复内容
|
||||||
|
- `reason`: 重写原因
|
||||||
|
- `reply_to`: 回复目标,格式为 `{发送者的person_name:消息内容}`
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Tuple[bool, List[Tuple[str, Any]], Optional[str]]`: (是否成功, 回复集合, 提示词)
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
```python
|
||||||
|
success, reply_set, prompt = await generator_api.rewrite_reply(
|
||||||
chat_stream=chat_stream,
|
chat_stream=chat_stream,
|
||||||
reply_data={"original_text": "原始回复", "style": "more_friendly"}
|
raw_reply="原始回复内容",
|
||||||
|
reason="重写原因",
|
||||||
|
reply_to="麦麦:你好",
|
||||||
|
return_prompt=True
|
||||||
)
|
)
|
||||||
|
if success:
|
||||||
|
for reply_type, reply_content in reply_set:
|
||||||
|
print(f"回复类型: {reply_type}, 内容: {reply_content}")
|
||||||
|
if prompt:
|
||||||
|
print(f"使用的提示词: {prompt}")
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用示例
|
## 回复集合`reply_set`格式
|
||||||
|
|
||||||
### 1. 基础回复生成
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.apis import generator_api
|
|
||||||
|
|
||||||
async def generate_greeting_reply(chat_stream, user_name):
|
|
||||||
"""生成问候回复"""
|
|
||||||
|
|
||||||
action_data = {
|
|
||||||
"intent": "greeting",
|
|
||||||
"user_name": user_name,
|
|
||||||
"context": "morning_greeting"
|
|
||||||
}
|
|
||||||
|
|
||||||
success, reply_set = await generator_api.generate_reply(
|
|
||||||
chat_stream=chat_stream,
|
|
||||||
action_data=action_data
|
|
||||||
)
|
|
||||||
|
|
||||||
if success and reply_set:
|
|
||||||
# 获取第一个回复
|
|
||||||
reply_type, reply_content = reply_set[0]
|
|
||||||
return reply_content
|
|
||||||
|
|
||||||
return "你好!" # 默认回复
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 在Action中使用回复生成器
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.base import BaseAction
|
|
||||||
|
|
||||||
class ChatAction(BaseAction):
|
|
||||||
async def execute(self, action_data, chat_stream):
|
|
||||||
# 准备回复数据
|
|
||||||
reply_context = {
|
|
||||||
"message_type": "response",
|
|
||||||
"user_input": action_data.get("user_message", ""),
|
|
||||||
"intent": action_data.get("intent", ""),
|
|
||||||
"entities": action_data.get("entities", {}),
|
|
||||||
"context": self.get_conversation_context(chat_stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
# 生成回复
|
|
||||||
success, reply_set = await generator_api.generate_reply(
|
|
||||||
chat_stream=chat_stream,
|
|
||||||
action_data=reply_context
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"replies": reply_set,
|
|
||||||
"generated_count": len(reply_set)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": "回复生成失败",
|
|
||||||
"fallback_reply": "抱歉,我现在无法理解您的消息。"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 多样化回复生成
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def generate_diverse_replies(chat_stream, topic, count=3):
|
|
||||||
"""生成多个不同风格的回复"""
|
|
||||||
|
|
||||||
styles = ["formal", "casual", "humorous"]
|
|
||||||
all_replies = []
|
|
||||||
|
|
||||||
for i, style in enumerate(styles[:count]):
|
|
||||||
action_data = {
|
|
||||||
"topic": topic,
|
|
||||||
"style": style,
|
|
||||||
"variation": i
|
|
||||||
}
|
|
||||||
|
|
||||||
success, reply_set = await generator_api.generate_reply(
|
|
||||||
chat_stream=chat_stream,
|
|
||||||
action_data=action_data
|
|
||||||
)
|
|
||||||
|
|
||||||
if success and reply_set:
|
|
||||||
all_replies.extend(reply_set)
|
|
||||||
|
|
||||||
return all_replies
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 回复重写功能
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def improve_reply(chat_stream, original_reply, improvement_type="more_friendly"):
|
|
||||||
"""改进原始回复"""
|
|
||||||
|
|
||||||
reply_data = {
|
|
||||||
"original_text": original_reply,
|
|
||||||
"improvement_type": improvement_type,
|
|
||||||
"target_audience": "young_users",
|
|
||||||
"tone": "positive"
|
|
||||||
}
|
|
||||||
|
|
||||||
success, improved_replies = await generator_api.rewrite_reply(
|
|
||||||
chat_stream=chat_stream,
|
|
||||||
reply_data=reply_data
|
|
||||||
)
|
|
||||||
|
|
||||||
if success and improved_replies:
|
|
||||||
# 返回改进后的第一个回复
|
|
||||||
_, improved_content = improved_replies[0]
|
|
||||||
return improved_content
|
|
||||||
|
|
||||||
return original_reply # 如果改进失败,返回原始回复
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 条件回复生成
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def conditional_reply_generation(chat_stream, user_message, user_emotion):
|
|
||||||
"""根据用户情感生成条件回复"""
|
|
||||||
|
|
||||||
# 根据情感调整回复策略
|
|
||||||
if user_emotion == "sad":
|
|
||||||
action_data = {
|
|
||||||
"intent": "comfort",
|
|
||||||
"tone": "empathetic",
|
|
||||||
"style": "supportive"
|
|
||||||
}
|
|
||||||
elif user_emotion == "angry":
|
|
||||||
action_data = {
|
|
||||||
"intent": "calm",
|
|
||||||
"tone": "peaceful",
|
|
||||||
"style": "understanding"
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
action_data = {
|
|
||||||
"intent": "respond",
|
|
||||||
"tone": "neutral",
|
|
||||||
"style": "helpful"
|
|
||||||
}
|
|
||||||
|
|
||||||
action_data["user_message"] = user_message
|
|
||||||
action_data["user_emotion"] = user_emotion
|
|
||||||
|
|
||||||
success, reply_set = await generator_api.generate_reply(
|
|
||||||
chat_stream=chat_stream,
|
|
||||||
action_data=action_data
|
|
||||||
)
|
|
||||||
|
|
||||||
return reply_set if success else []
|
|
||||||
```
|
|
||||||
|
|
||||||
## 回复集合格式
|
|
||||||
|
|
||||||
### 回复类型
|
### 回复类型
|
||||||
生成的回复集合包含多种类型的回复:
|
生成的回复集合包含多种类型的回复:
|
||||||
@@ -260,82 +169,32 @@ reply_set = [
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
## 高级用法
|
### 4. 自定义提示词回复
|
||||||
|
|
||||||
### 1. 自定义回复器配置
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def generate_with_custom_config(chat_stream, action_data):
|
async def generate_response_custom(
|
||||||
"""使用自定义配置生成回复"""
|
chat_stream: Optional[ChatStream] = None,
|
||||||
|
chat_id: Optional[str] = None,
|
||||||
# 获取回复器
|
model_configs: Optional[List[Dict[str, Any]]] = None,
|
||||||
replyer = generator_api.get_replyer(chat_stream=chat_stream)
|
prompt: str = "",
|
||||||
|
) -> Optional[str]:
|
||||||
if replyer:
|
|
||||||
# 可以访问回复器的内部方法
|
|
||||||
success, reply_set = await replyer.generate_reply_with_context(
|
|
||||||
reply_data=action_data,
|
|
||||||
# 可以传递额外的配置参数
|
|
||||||
)
|
|
||||||
return success, reply_set
|
|
||||||
|
|
||||||
return False, []
|
|
||||||
```
|
```
|
||||||
|
生成自定义提示词回复
|
||||||
|
|
||||||
### 2. 回复质量评估
|
优先使用chat_stream,如果没有则使用chat_id直接查找。
|
||||||
|
|
||||||
```python
|
**Args:**
|
||||||
async def generate_and_evaluate_replies(chat_stream, action_data):
|
- `chat_stream`: 聊天流对象
|
||||||
"""生成回复并评估质量"""
|
- `chat_id`: 聊天ID(备用)
|
||||||
|
- `model_configs`: 模型配置列表
|
||||||
|
- `prompt`: 自定义提示词
|
||||||
|
|
||||||
success, reply_set = await generator_api.generate_reply(
|
**Returns:**
|
||||||
chat_stream=chat_stream,
|
- `Optional[str]`: 生成的自定义回复内容,如果生成失败则返回None
|
||||||
action_data=action_data
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
evaluated_replies = []
|
|
||||||
for reply_type, reply_content in reply_set:
|
|
||||||
# 简单的质量评估
|
|
||||||
quality_score = evaluate_reply_quality(reply_content)
|
|
||||||
evaluated_replies.append({
|
|
||||||
"type": reply_type,
|
|
||||||
"content": reply_content,
|
|
||||||
"quality": quality_score
|
|
||||||
})
|
|
||||||
|
|
||||||
# 按质量排序
|
|
||||||
evaluated_replies.sort(key=lambda x: x["quality"], reverse=True)
|
|
||||||
return evaluated_replies
|
|
||||||
|
|
||||||
return []
|
|
||||||
|
|
||||||
def evaluate_reply_quality(reply_content):
|
|
||||||
"""简单的回复质量评估"""
|
|
||||||
if not reply_content:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
score = 50 # 基础分
|
|
||||||
|
|
||||||
# 长度适中加分
|
|
||||||
if 5 <= len(reply_content) <= 100:
|
|
||||||
score += 20
|
|
||||||
|
|
||||||
# 包含积极词汇加分
|
|
||||||
positive_words = ["好", "棒", "不错", "感谢", "开心"]
|
|
||||||
for word in positive_words:
|
|
||||||
if word in reply_content:
|
|
||||||
score += 10
|
|
||||||
break
|
|
||||||
|
|
||||||
return min(score, 100)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **异步操作**:所有生成函数都是异步的,必须使用`await`
|
1. **异步操作**:部分函数是异步的,须使用`await`
|
||||||
2. **错误处理**:函数内置错误处理,失败时返回False和空列表
|
2. **聊天流依赖**:需要有效的聊天流对象才能正常工作
|
||||||
3. **聊天流依赖**:需要有效的聊天流对象才能正常工作
|
3. **性能考虑**:回复生成可能需要一些时间,特别是使用LLM时
|
||||||
4. **性能考虑**:回复生成可能需要一些时间,特别是使用LLM时
|
4. **回复格式**:返回的回复集合是元组列表,包含类型和内容
|
||||||
5. **回复格式**:返回的回复集合是元组列表,包含类型和内容
|
5. **上下文感知**:生成器会考虑聊天上下文和历史消息,除非你用的是自定义提示词。
|
||||||
6. **上下文感知**:生成器会考虑聊天上下文和历史消息
|
|
||||||
@@ -6,239 +6,34 @@ LLM API模块提供与大语言模型交互的功能,让插件能够使用系
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import llm_api
|
from src.plugin_system.apis import llm_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import llm_api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主要功能
|
## 主要功能
|
||||||
|
|
||||||
### 1. 模型管理
|
### 1. 查询可用模型
|
||||||
|
|
||||||
#### `get_available_models() -> Dict[str, Any]`
|
|
||||||
获取所有可用的模型配置
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Dict[str, Any]`:模型配置字典,key为模型名称,value为模型配置
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
models = llm_api.get_available_models()
|
def get_available_models() -> Dict[str, Any]:
|
||||||
for model_name, model_config in models.items():
|
|
||||||
print(f"模型: {model_name}")
|
|
||||||
print(f"配置: {model_config}")
|
|
||||||
```
|
```
|
||||||
|
获取所有可用的模型配置。
|
||||||
|
|
||||||
### 2. 内容生成
|
**Return:**
|
||||||
|
- `Dict[str, Any]`:模型配置字典,key为模型名称,value为模型配置。
|
||||||
|
|
||||||
#### `generate_with_model(prompt, model_config, request_type="plugin.generate", **kwargs)`
|
### 2. 使用模型生成内容
|
||||||
使用指定模型生成内容
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `prompt`:提示词
|
|
||||||
- `model_config`:模型配置(从 get_available_models 获取)
|
|
||||||
- `request_type`:请求类型标识
|
|
||||||
- `**kwargs`:其他模型特定参数,如temperature、max_tokens等
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Tuple[bool, str, str, str]`:(是否成功, 生成的内容, 推理过程, 模型名称)
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
models = llm_api.get_available_models()
|
async def generate_with_model(
|
||||||
default_model = models.get("default")
|
prompt: str, model_config: Dict[str, Any], request_type: str = "plugin.generate", **kwargs
|
||||||
|
) -> Tuple[bool, str]:
|
||||||
if default_model:
|
|
||||||
success, response, reasoning, model_name = await llm_api.generate_with_model(
|
|
||||||
prompt="请写一首关于春天的诗",
|
|
||||||
model_config=default_model,
|
|
||||||
temperature=0.7,
|
|
||||||
max_tokens=200
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
print(f"生成内容: {response}")
|
|
||||||
print(f"使用模型: {model_name}")
|
|
||||||
```
|
```
|
||||||
|
使用指定模型生成内容。
|
||||||
|
|
||||||
## 使用示例
|
**Args:**
|
||||||
|
- `prompt`:提示词。
|
||||||
|
- `model_config`:模型配置(从 `get_available_models` 获取)。
|
||||||
|
- `request_type`:请求类型标识,默认为 `"plugin.generate"`。
|
||||||
|
- `**kwargs`:其他模型特定参数,如 `temperature`、`max_tokens` 等。
|
||||||
|
|
||||||
### 1. 基础文本生成
|
**Return:**
|
||||||
|
- `Tuple[bool, str]`:返回一个元组,第一个元素表示是否成功,第二个元素为生成的内容或错误信息。
|
||||||
```python
|
|
||||||
from src.plugin_system.apis import llm_api
|
|
||||||
|
|
||||||
async def generate_story(topic: str):
|
|
||||||
"""生成故事"""
|
|
||||||
models = llm_api.get_available_models()
|
|
||||||
model = models.get("default")
|
|
||||||
|
|
||||||
if not model:
|
|
||||||
return "未找到可用模型"
|
|
||||||
|
|
||||||
prompt = f"请写一个关于{topic}的短故事,大约100字左右。"
|
|
||||||
|
|
||||||
success, story, reasoning, model_name = await llm_api.generate_with_model(
|
|
||||||
prompt=prompt,
|
|
||||||
model_config=model,
|
|
||||||
request_type="story.generate",
|
|
||||||
temperature=0.8,
|
|
||||||
max_tokens=150
|
|
||||||
)
|
|
||||||
|
|
||||||
return story if success else "故事生成失败"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 在Action中使用LLM
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.base import BaseAction
|
|
||||||
|
|
||||||
class LLMAction(BaseAction):
|
|
||||||
async def execute(self, action_data, chat_stream):
|
|
||||||
# 获取用户输入
|
|
||||||
user_input = action_data.get("user_message", "")
|
|
||||||
intent = action_data.get("intent", "chat")
|
|
||||||
|
|
||||||
# 获取模型配置
|
|
||||||
models = llm_api.get_available_models()
|
|
||||||
model = models.get("default")
|
|
||||||
|
|
||||||
if not model:
|
|
||||||
return {"success": False, "error": "未配置LLM模型"}
|
|
||||||
|
|
||||||
# 构建提示词
|
|
||||||
prompt = self.build_prompt(user_input, intent)
|
|
||||||
|
|
||||||
# 生成回复
|
|
||||||
success, response, reasoning, model_name = await llm_api.generate_with_model(
|
|
||||||
prompt=prompt,
|
|
||||||
model_config=model,
|
|
||||||
request_type=f"plugin.{self.plugin_name}",
|
|
||||||
temperature=0.7
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"response": response,
|
|
||||||
"model_used": model_name,
|
|
||||||
"reasoning": reasoning
|
|
||||||
}
|
|
||||||
|
|
||||||
return {"success": False, "error": response}
|
|
||||||
|
|
||||||
def build_prompt(self, user_input: str, intent: str) -> str:
|
|
||||||
"""构建提示词"""
|
|
||||||
base_prompt = "你是一个友善的AI助手。"
|
|
||||||
|
|
||||||
if intent == "question":
|
|
||||||
return f"{base_prompt}\n\n用户问题:{user_input}\n\n请提供准确、有用的回答:"
|
|
||||||
elif intent == "chat":
|
|
||||||
return f"{base_prompt}\n\n用户说:{user_input}\n\n请进行自然的对话:"
|
|
||||||
else:
|
|
||||||
return f"{base_prompt}\n\n用户输入:{user_input}\n\n请回复:"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 多模型对比
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def compare_models(prompt: str):
|
|
||||||
"""使用多个模型生成内容并对比"""
|
|
||||||
models = llm_api.get_available_models()
|
|
||||||
results = {}
|
|
||||||
|
|
||||||
for model_name, model_config in models.items():
|
|
||||||
success, response, reasoning, actual_model = await llm_api.generate_with_model(
|
|
||||||
prompt=prompt,
|
|
||||||
model_config=model_config,
|
|
||||||
request_type="comparison.test"
|
|
||||||
)
|
|
||||||
|
|
||||||
results[model_name] = {
|
|
||||||
"success": success,
|
|
||||||
"response": response,
|
|
||||||
"model": actual_model,
|
|
||||||
"reasoning": reasoning
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 智能对话插件
|
|
||||||
|
|
||||||
```python
|
|
||||||
class ChatbotPlugin(BasePlugin):
|
|
||||||
async def handle_action(self, action_data, chat_stream):
|
|
||||||
user_message = action_data.get("message", "")
|
|
||||||
|
|
||||||
# 获取历史对话上下文
|
|
||||||
context = self.get_conversation_context(chat_stream)
|
|
||||||
|
|
||||||
# 构建对话提示词
|
|
||||||
prompt = self.build_conversation_prompt(user_message, context)
|
|
||||||
|
|
||||||
# 获取模型配置
|
|
||||||
models = llm_api.get_available_models()
|
|
||||||
chat_model = models.get("chat", models.get("default"))
|
|
||||||
|
|
||||||
if not chat_model:
|
|
||||||
return {"success": False, "message": "聊天模型未配置"}
|
|
||||||
|
|
||||||
# 生成回复
|
|
||||||
success, response, reasoning, model_name = await llm_api.generate_with_model(
|
|
||||||
prompt=prompt,
|
|
||||||
model_config=chat_model,
|
|
||||||
request_type="chat.conversation",
|
|
||||||
temperature=0.8,
|
|
||||||
max_tokens=500
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
# 保存对话历史
|
|
||||||
self.save_conversation(chat_stream, user_message, response)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"reply": response,
|
|
||||||
"model": model_name
|
|
||||||
}
|
|
||||||
|
|
||||||
return {"success": False, "message": "回复生成失败"}
|
|
||||||
|
|
||||||
def build_conversation_prompt(self, user_message: str, context: list) -> str:
|
|
||||||
"""构建对话提示词"""
|
|
||||||
prompt = "你是一个有趣、友善的聊天机器人。请自然地回复用户的消息。\n\n"
|
|
||||||
|
|
||||||
# 添加历史对话
|
|
||||||
if context:
|
|
||||||
prompt += "对话历史:\n"
|
|
||||||
for msg in context[-5:]: # 只保留最近5条
|
|
||||||
prompt += f"用户: {msg['user']}\n机器人: {msg['bot']}\n"
|
|
||||||
prompt += "\n"
|
|
||||||
|
|
||||||
prompt += f"用户: {user_message}\n机器人: "
|
|
||||||
return prompt
|
|
||||||
```
|
|
||||||
|
|
||||||
## 模型配置说明
|
|
||||||
|
|
||||||
### 常用模型类型
|
|
||||||
- `default`:默认模型
|
|
||||||
- `chat`:聊天专用模型
|
|
||||||
- `creative`:创意生成模型
|
|
||||||
- `code`:代码生成模型
|
|
||||||
|
|
||||||
### 配置参数
|
|
||||||
LLM模型支持的常用参数:
|
|
||||||
- `temperature`:控制输出随机性(0.0-1.0)
|
|
||||||
- `max_tokens`:最大生成长度
|
|
||||||
- `top_p`:核采样参数
|
|
||||||
- `frequency_penalty`:频率惩罚
|
|
||||||
- `presence_penalty`:存在惩罚
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **异步操作**:LLM生成是异步的,必须使用`await`
|
|
||||||
2. **错误处理**:生成失败时返回False和错误信息
|
|
||||||
3. **配置依赖**:需要正确配置模型才能使用
|
|
||||||
4. **请求类型**:建议为不同用途设置不同的request_type
|
|
||||||
5. **性能考虑**:LLM调用可能较慢,考虑超时和缓存
|
|
||||||
6. **成本控制**:注意控制max_tokens以控制成本
|
|
||||||
29
docs/plugins/api/logging-api.md
Normal file
29
docs/plugins/api/logging-api.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Logging API
|
||||||
|
|
||||||
|
Logging API模块提供了获取本体logger的功能,允许插件记录日志信息。
|
||||||
|
|
||||||
|
## 导入方式
|
||||||
|
|
||||||
|
```python
|
||||||
|
from src.plugin_system.apis import get_logger
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import get_logger
|
||||||
|
```
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
### 1. 获取本体logger
|
||||||
|
```python
|
||||||
|
def get_logger(name: str) -> structlog.stdlib.BoundLogger:
|
||||||
|
```
|
||||||
|
获取本体logger实例。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `name` (str): 日志记录器的名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- 一个logger实例,有以下方法:
|
||||||
|
- `debug`
|
||||||
|
- `info`
|
||||||
|
- `warning`
|
||||||
|
- `error`
|
||||||
|
- `critical`
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
# 消息API
|
# 消息API
|
||||||
|
|
||||||
> 消息API提供了强大的消息查询、计数和格式化功能,让你轻松处理聊天消息数据。
|
消息API提供了强大的消息查询、计数和格式化功能,让你轻松处理聊天消息数据。
|
||||||
|
|
||||||
## 导入方式
|
## 导入方式
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import message_api
|
from src.plugin_system.apis import message_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import message_api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 功能概述
|
## 功能概述
|
||||||
@@ -15,297 +17,356 @@ from src.plugin_system.apis import message_api
|
|||||||
- **消息计数** - 统计新消息数量
|
- **消息计数** - 统计新消息数量
|
||||||
- **消息格式化** - 将消息转换为可读格式
|
- **消息格式化** - 将消息转换为可读格式
|
||||||
|
|
||||||
---
|
## 主要功能
|
||||||
|
|
||||||
## 消息查询API
|
### 1. 按照事件查询消息
|
||||||
|
```python
|
||||||
|
def get_messages_by_time(
|
||||||
|
start_time: float, end_time: float, limit: int = 0, limit_mode: str = "latest", filter_mai: bool = False
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
```
|
||||||
|
获取指定时间范围内的消息。
|
||||||
|
|
||||||
### 按时间查询消息
|
**Args:**
|
||||||
|
|
||||||
#### `get_messages_by_time(start_time, end_time, limit=0, limit_mode="latest")`
|
|
||||||
|
|
||||||
获取指定时间范围内的消息
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `start_time` (float): 开始时间戳
|
- `start_time` (float): 开始时间戳
|
||||||
- `end_time` (float): 结束时间戳
|
- `end_time` (float): 结束时间戳
|
||||||
- `limit` (int): 限制返回消息数量,0为不限制
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录
|
- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录
|
||||||
|
- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False
|
||||||
|
|
||||||
**返回:** `List[Dict[str, Any]]` - 消息列表
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
**示例:**
|
消息列表中包含的键与`Messages`类的属性一致。(位于`src.common.database.database_model`)
|
||||||
|
|
||||||
|
### 2. 获取指定聊天中指定时间范围内的信息
|
||||||
```python
|
```python
|
||||||
import time
|
def get_messages_by_time_in_chat(
|
||||||
|
chat_id: str,
|
||||||
# 获取最近24小时的消息
|
start_time: float,
|
||||||
now = time.time()
|
end_time: float,
|
||||||
yesterday = now - 24 * 3600
|
limit: int = 0,
|
||||||
messages = message_api.get_messages_by_time(yesterday, now, limit=50)
|
limit_mode: str = "latest",
|
||||||
|
filter_mai: bool = False,
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
```
|
```
|
||||||
|
获取指定聊天中指定时间范围内的消息。
|
||||||
|
|
||||||
### 按聊天查询消息
|
**Args:**
|
||||||
|
|
||||||
#### `get_messages_by_time_in_chat(chat_id, start_time, end_time, limit=0, limit_mode="latest")`
|
|
||||||
|
|
||||||
获取指定聊天中指定时间范围内的消息
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `chat_id` (str): 聊天ID
|
|
||||||
- 其他参数同上
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
|
||||||
# 获取某个群聊最近的100条消息
|
|
||||||
messages = message_api.get_messages_by_time_in_chat(
|
|
||||||
chat_id="123456789",
|
|
||||||
start_time=yesterday,
|
|
||||||
end_time=now,
|
|
||||||
limit=100
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `get_messages_by_time_in_chat_inclusive(chat_id, start_time, end_time, limit=0, limit_mode="latest")`
|
|
||||||
|
|
||||||
获取指定聊天中指定时间范围内的消息(包含边界时间点)
|
|
||||||
|
|
||||||
与 `get_messages_by_time_in_chat` 类似,但包含边界时间戳的消息。
|
|
||||||
|
|
||||||
#### `get_recent_messages(chat_id, hours=24.0, limit=100, limit_mode="latest")`
|
|
||||||
|
|
||||||
获取指定聊天中最近一段时间的消息(便捷方法)
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `chat_id` (str): 聊天ID
|
|
||||||
- `hours` (float): 最近多少小时,默认24小时
|
|
||||||
- `limit` (int): 限制返回消息数量,默认100条
|
|
||||||
- `limit_mode` (str): 限制模式
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
|
||||||
# 获取最近6小时的消息
|
|
||||||
recent_messages = message_api.get_recent_messages(
|
|
||||||
chat_id="123456789",
|
|
||||||
hours=6.0,
|
|
||||||
limit=50
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 按用户查询消息
|
|
||||||
|
|
||||||
#### `get_messages_by_time_in_chat_for_users(chat_id, start_time, end_time, person_ids, limit=0, limit_mode="latest")`
|
|
||||||
|
|
||||||
获取指定聊天中指定用户在指定时间范围内的消息
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `chat_id` (str): 聊天ID
|
- `chat_id` (str): 聊天ID
|
||||||
- `start_time` (float): 开始时间戳
|
- `start_time` (float): 开始时间戳
|
||||||
- `end_time` (float): 结束时间戳
|
- `end_time` (float): 结束时间戳
|
||||||
- `person_ids` (list): 用户ID列表
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
- `limit` (int): 限制返回消息数量
|
- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录
|
||||||
- `limit_mode` (str): 限制模式
|
- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False
|
||||||
|
|
||||||
**示例:**
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
|
|
||||||
|
### 3. 获取指定聊天中指定时间范围内的信息(包含边界)
|
||||||
```python
|
```python
|
||||||
# 获取特定用户的消息
|
def get_messages_by_time_in_chat_inclusive(
|
||||||
user_messages = message_api.get_messages_by_time_in_chat_for_users(
|
chat_id: str,
|
||||||
chat_id="123456789",
|
start_time: float,
|
||||||
start_time=yesterday,
|
end_time: float,
|
||||||
end_time=now,
|
limit: int = 0,
|
||||||
person_ids=["user1", "user2"]
|
limit_mode: str = "latest",
|
||||||
)
|
filter_mai: bool = False,
|
||||||
|
filter_command: bool = False,
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
```
|
```
|
||||||
|
获取指定聊天中指定时间范围内的消息(包含边界)。
|
||||||
|
|
||||||
#### `get_messages_by_time_for_users(start_time, end_time, person_ids, limit=0, limit_mode="latest")`
|
**Args:**
|
||||||
|
- `chat_id` (str): 聊天ID
|
||||||
|
- `start_time` (float): 开始时间戳(包含)
|
||||||
|
- `end_time` (float): 结束时间戳(包含)
|
||||||
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
|
- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录
|
||||||
|
- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False
|
||||||
|
- `filter_command` (bool): 是否过滤命令消息,默认False
|
||||||
|
|
||||||
获取指定用户在所有聊天中指定时间范围内的消息
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
### 其他查询方法
|
|
||||||
|
|
||||||
#### `get_random_chat_messages(start_time, end_time, limit=0, limit_mode="latest")`
|
### 4. 获取指定聊天中指定用户在指定时间范围内的消息
|
||||||
|
```python
|
||||||
|
def get_messages_by_time_in_chat_for_users(
|
||||||
|
chat_id: str,
|
||||||
|
start_time: float,
|
||||||
|
end_time: float,
|
||||||
|
person_ids: List[str],
|
||||||
|
limit: int = 0,
|
||||||
|
limit_mode: str = "latest",
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
```
|
||||||
|
获取指定聊天中指定用户在指定时间范围内的消息。
|
||||||
|
|
||||||
随机选择一个聊天,返回该聊天在指定时间范围内的消息
|
**Args:**
|
||||||
|
|
||||||
#### `get_messages_before_time(timestamp, limit=0)`
|
|
||||||
|
|
||||||
获取指定时间戳之前的消息
|
|
||||||
|
|
||||||
#### `get_messages_before_time_in_chat(chat_id, timestamp, limit=0)`
|
|
||||||
|
|
||||||
获取指定聊天中指定时间戳之前的消息
|
|
||||||
|
|
||||||
#### `get_messages_before_time_for_users(timestamp, person_ids, limit=0)`
|
|
||||||
|
|
||||||
获取指定用户在指定时间戳之前的消息
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 消息计数API
|
|
||||||
|
|
||||||
### `count_new_messages(chat_id, start_time=0.0, end_time=None)`
|
|
||||||
|
|
||||||
计算指定聊天中从开始时间到结束时间的新消息数量
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `chat_id` (str): 聊天ID
|
- `chat_id` (str): 聊天ID
|
||||||
- `start_time` (float): 开始时间戳
|
- `start_time` (float): 开始时间戳
|
||||||
- `end_time` (float): 结束时间戳,如果为None则使用当前时间
|
- `end_time` (float): 结束时间戳
|
||||||
|
- `person_ids` (List[str]): 用户ID列表
|
||||||
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
|
- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录
|
||||||
|
|
||||||
**返回:** `int` - 新消息数量
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
**示例:**
|
|
||||||
|
### 5. 随机选择一个聊天,返回该聊天在指定时间范围内的消息
|
||||||
```python
|
```python
|
||||||
# 计算最近1小时的新消息数
|
def get_random_chat_messages(
|
||||||
import time
|
start_time: float,
|
||||||
now = time.time()
|
end_time: float,
|
||||||
hour_ago = now - 3600
|
limit: int = 0,
|
||||||
new_count = message_api.count_new_messages("123456789", hour_ago, now)
|
limit_mode: str = "latest",
|
||||||
print(f"最近1小时有{new_count}条新消息")
|
filter_mai: bool = False,
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
```
|
```
|
||||||
|
随机选择一个聊天,返回该聊天在指定时间范围内的消息。
|
||||||
|
|
||||||
### `count_new_messages_for_users(chat_id, start_time, end_time, person_ids)`
|
**Args:**
|
||||||
|
- `start_time` (float): 开始时间戳
|
||||||
|
- `end_time` (float): 结束时间戳
|
||||||
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
|
- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录
|
||||||
|
- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False
|
||||||
|
|
||||||
计算指定聊天中指定用户从开始时间到结束时间的新消息数量
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 消息格式化API
|
### 6. 获取指定用户在所有聊天中指定时间范围内的消息
|
||||||
|
```python
|
||||||
|
def get_messages_by_time_for_users(
|
||||||
|
start_time: float,
|
||||||
|
end_time: float,
|
||||||
|
person_ids: List[str],
|
||||||
|
limit: int = 0,
|
||||||
|
limit_mode: str = "latest",
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
```
|
||||||
|
获取指定用户在所有聊天中指定时间范围内的消息。
|
||||||
|
|
||||||
### `build_readable_messages_to_str(messages, **options)`
|
**Args:**
|
||||||
|
- `start_time` (float): 开始时间戳
|
||||||
|
- `end_time` (float): 结束时间戳
|
||||||
|
- `person_ids` (List[str]): 用户ID列表
|
||||||
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
|
- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录
|
||||||
|
|
||||||
将消息列表构建成可读的字符串
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
**参数:**
|
|
||||||
|
### 7. 获取指定时间戳之前的消息
|
||||||
|
```python
|
||||||
|
def get_messages_before_time(
|
||||||
|
timestamp: float,
|
||||||
|
limit: int = 0,
|
||||||
|
filter_mai: bool = False,
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
```
|
||||||
|
获取指定时间戳之前的消息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `timestamp` (float): 时间戳
|
||||||
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
|
- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
|
|
||||||
|
### 8. 获取指定聊天中指定时间戳之前的消息
|
||||||
|
```python
|
||||||
|
def get_messages_before_time_in_chat(
|
||||||
|
chat_id: str,
|
||||||
|
timestamp: float,
|
||||||
|
limit: int = 0,
|
||||||
|
filter_mai: bool = False,
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
```
|
||||||
|
获取指定聊天中指定时间戳之前的消息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `chat_id` (str): 聊天ID
|
||||||
|
- `timestamp` (float): 时间戳
|
||||||
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
|
- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
|
|
||||||
|
### 9. 获取指定用户在指定时间戳之前的消息
|
||||||
|
```python
|
||||||
|
def get_messages_before_time_for_users(
|
||||||
|
timestamp: float,
|
||||||
|
person_ids: List[str],
|
||||||
|
limit: int = 0,
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
```
|
||||||
|
获取指定用户在指定时间戳之前的消息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `timestamp` (float): 时间戳
|
||||||
|
- `person_ids` (List[str]): 用户ID列表
|
||||||
|
- `limit` (int): 限制返回消息数量,0为不限制
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
|
|
||||||
|
### 10. 获取指定聊天中最近一段时间的消息
|
||||||
|
```python
|
||||||
|
def get_recent_messages(
|
||||||
|
chat_id: str,
|
||||||
|
hours: float = 24.0,
|
||||||
|
limit: int = 100,
|
||||||
|
limit_mode: str = "latest",
|
||||||
|
filter_mai: bool = False,
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
```
|
||||||
|
获取指定聊天中最近一段时间的消息。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `chat_id` (str): 聊天ID
|
||||||
|
- `hours` (float): 最近多少小时,默认24小时
|
||||||
|
- `limit` (int): 限制返回消息数量,默认100条
|
||||||
|
- `limit_mode` (str): 限制模式,`"earliest"`获取最早记录,`"latest"`获取最新记录
|
||||||
|
- `filter_mai` (bool): 是否过滤掉机器人的消息,默认False
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 消息列表
|
||||||
|
|
||||||
|
|
||||||
|
### 11. 计算指定聊天中从开始时间到结束时间的新消息数量
|
||||||
|
```python
|
||||||
|
def count_new_messages(
|
||||||
|
chat_id: str,
|
||||||
|
start_time: float = 0.0,
|
||||||
|
end_time: Optional[float] = None,
|
||||||
|
) -> int:
|
||||||
|
```
|
||||||
|
计算指定聊天中从开始时间到结束时间的新消息数量。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `chat_id` (str): 聊天ID
|
||||||
|
- `start_time` (float): 开始时间戳
|
||||||
|
- `end_time` (Optional[float]): 结束时间戳,如果为None则使用当前时间
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `int` - 新消息数量
|
||||||
|
|
||||||
|
|
||||||
|
### 12. 计算指定聊天中指定用户从开始时间到结束时间的新消息数量
|
||||||
|
```python
|
||||||
|
def count_new_messages_for_users(
|
||||||
|
chat_id: str,
|
||||||
|
start_time: float,
|
||||||
|
end_time: float,
|
||||||
|
person_ids: List[str],
|
||||||
|
) -> int:
|
||||||
|
```
|
||||||
|
计算指定聊天中指定用户从开始时间到结束时间的新消息数量。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `chat_id` (str): 聊天ID
|
||||||
|
- `start_time` (float): 开始时间戳
|
||||||
|
- `end_time` (float): 结束时间戳
|
||||||
|
- `person_ids` (List[str]): 用户ID列表
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `int` - 新消息数量
|
||||||
|
|
||||||
|
|
||||||
|
### 13. 将消息列表构建成可读的字符串
|
||||||
|
```python
|
||||||
|
def build_readable_messages_to_str(
|
||||||
|
messages: List[Dict[str, Any]],
|
||||||
|
replace_bot_name: bool = True,
|
||||||
|
merge_messages: bool = False,
|
||||||
|
timestamp_mode: str = "relative",
|
||||||
|
read_mark: float = 0.0,
|
||||||
|
truncate: bool = False,
|
||||||
|
show_actions: bool = False,
|
||||||
|
) -> str:
|
||||||
|
```
|
||||||
|
将消息列表构建成可读的字符串。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
- `messages` (List[Dict[str, Any]]): 消息列表
|
- `messages` (List[Dict[str, Any]]): 消息列表
|
||||||
- `replace_bot_name` (bool): 是否将机器人的名称替换为"你",默认True
|
- `replace_bot_name` (bool): 是否将机器人的名称替换为"你"
|
||||||
- `merge_messages` (bool): 是否合并连续消息,默认False
|
- `merge_messages` (bool): 是否合并连续消息
|
||||||
- `timestamp_mode` (str): 时间戳显示模式,`"relative"`或`"absolute"`,默认`"relative"`
|
- `timestamp_mode` (str): 时间戳显示模式,`"relative"`或`"absolute"`
|
||||||
- `read_mark` (float): 已读标记时间戳,用于分割已读和未读消息,默认0.0
|
- `read_mark` (float): 已读标记时间戳,用于分割已读和未读消息
|
||||||
- `truncate` (bool): 是否截断长消息,默认False
|
- `truncate` (bool): 是否截断长消息
|
||||||
- `show_actions` (bool): 是否显示动作记录,默认False
|
- `show_actions` (bool): 是否显示动作记录
|
||||||
|
|
||||||
**返回:** `str` - 格式化后的可读字符串
|
**Returns:**
|
||||||
|
- `str` - 格式化后的可读字符串
|
||||||
|
|
||||||
**示例:**
|
|
||||||
|
### 14. 将消息列表构建成可读的字符串,并返回详细信息
|
||||||
```python
|
```python
|
||||||
# 获取消息并格式化为可读文本
|
async def build_readable_messages_with_details(
|
||||||
messages = message_api.get_recent_messages("123456789", hours=2)
|
messages: List[Dict[str, Any]],
|
||||||
readable_text = message_api.build_readable_messages_to_str(
|
replace_bot_name: bool = True,
|
||||||
messages,
|
merge_messages: bool = False,
|
||||||
replace_bot_name=True,
|
timestamp_mode: str = "relative",
|
||||||
merge_messages=True,
|
truncate: bool = False,
|
||||||
timestamp_mode="relative"
|
) -> Tuple[str, List[Tuple[float, str, str]]]:
|
||||||
)
|
|
||||||
print(readable_text)
|
|
||||||
```
|
```
|
||||||
|
将消息列表构建成可读的字符串,并返回详细信息。
|
||||||
|
|
||||||
### `build_readable_messages_with_details(messages, **options)` 异步
|
**Args:**
|
||||||
|
- `messages` (List[Dict[str, Any]]): 消息列表
|
||||||
|
- `replace_bot_name` (bool): 是否将机器人的名称替换为"你"
|
||||||
|
- `merge_messages` (bool): 是否合并连续消息
|
||||||
|
- `timestamp_mode` (str): 时间戳显示模式,`"relative"`或`"absolute"`
|
||||||
|
- `truncate` (bool): 是否截断长消息
|
||||||
|
|
||||||
将消息列表构建成可读的字符串,并返回详细信息
|
**Returns:**
|
||||||
|
- `Tuple[str, List[Tuple[float, str, str]]]` - 格式化后的可读字符串和详细信息元组列表(时间戳, 昵称, 内容)
|
||||||
|
|
||||||
**参数:** 与 `build_readable_messages_to_str` 类似,但不包含 `read_mark` 和 `show_actions`
|
|
||||||
|
|
||||||
**返回:** `Tuple[str, List[Tuple[float, str, str]]]` - 格式化字符串和详细信息元组列表(时间戳, 昵称, 内容)
|
### 15. 从消息列表中提取不重复的用户ID列表
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
```python
|
||||||
# 异步获取详细格式化信息
|
async def get_person_ids_from_messages(
|
||||||
readable_text, details = await message_api.build_readable_messages_with_details(
|
messages: List[Dict[str, Any]],
|
||||||
messages,
|
) -> List[str]:
|
||||||
timestamp_mode="absolute"
|
|
||||||
)
|
|
||||||
|
|
||||||
for timestamp, nickname, content in details:
|
|
||||||
print(f"{timestamp}: {nickname} 说: {content}")
|
|
||||||
```
|
```
|
||||||
|
从消息列表中提取不重复的用户ID列表。
|
||||||
|
|
||||||
### `get_person_ids_from_messages(messages)` 异步
|
**Args:**
|
||||||
|
|
||||||
从消息列表中提取不重复的用户ID列表
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `messages` (List[Dict[str, Any]]): 消息列表
|
- `messages` (List[Dict[str, Any]]): 消息列表
|
||||||
|
|
||||||
**返回:** `List[str]` - 用户ID列表
|
**Returns:**
|
||||||
|
- `List[str]` - 用户ID列表
|
||||||
|
|
||||||
**示例:**
|
|
||||||
|
### 16. 从消息列表中移除机器人的消息
|
||||||
```python
|
```python
|
||||||
# 获取参与对话的所有用户ID
|
def filter_mai_messages(
|
||||||
messages = message_api.get_recent_messages("123456789")
|
messages: List[Dict[str, Any]],
|
||||||
person_ids = await message_api.get_person_ids_from_messages(messages)
|
) -> List[Dict[str, Any]]:
|
||||||
print(f"参与对话的用户: {person_ids}")
|
|
||||||
```
|
```
|
||||||
|
从消息列表中移除机器人的消息。
|
||||||
|
|
||||||
---
|
**Args:**
|
||||||
|
- `messages` (List[Dict[str, Any]]): 消息列表,每个元素是消息字典
|
||||||
|
|
||||||
## 完整使用示例
|
**Returns:**
|
||||||
|
- `List[Dict[str, Any]]` - 过滤后的消息列表
|
||||||
### 场景1:统计活跃度
|
|
||||||
|
|
||||||
```python
|
|
||||||
import time
|
|
||||||
from src.plugin_system.apis import message_api
|
|
||||||
|
|
||||||
async def analyze_chat_activity(chat_id: str):
|
|
||||||
"""分析聊天活跃度"""
|
|
||||||
now = time.time()
|
|
||||||
day_ago = now - 24 * 3600
|
|
||||||
|
|
||||||
# 获取最近24小时的消息
|
|
||||||
messages = message_api.get_recent_messages(chat_id, hours=24)
|
|
||||||
|
|
||||||
# 统计消息数量
|
|
||||||
total_count = len(messages)
|
|
||||||
|
|
||||||
# 获取参与用户
|
|
||||||
person_ids = await message_api.get_person_ids_from_messages(messages)
|
|
||||||
|
|
||||||
# 格式化消息内容
|
|
||||||
readable_text = message_api.build_readable_messages_to_str(
|
|
||||||
messages[-10:], # 最后10条消息
|
|
||||||
merge_messages=True,
|
|
||||||
timestamp_mode="relative"
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"total_messages": total_count,
|
|
||||||
"active_users": len(person_ids),
|
|
||||||
"recent_chat": readable_text
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景2:查看特定用户的历史消息
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_user_history(chat_id: str, user_id: str, days: int = 7):
|
|
||||||
"""获取用户最近N天的消息历史"""
|
|
||||||
now = time.time()
|
|
||||||
start_time = now - days * 24 * 3600
|
|
||||||
|
|
||||||
# 获取特定用户的消息
|
|
||||||
user_messages = message_api.get_messages_by_time_in_chat_for_users(
|
|
||||||
chat_id=chat_id,
|
|
||||||
start_time=start_time,
|
|
||||||
end_time=now,
|
|
||||||
person_ids=[user_id],
|
|
||||||
limit=100
|
|
||||||
)
|
|
||||||
|
|
||||||
# 格式化为可读文本
|
|
||||||
readable_history = message_api.build_readable_messages_to_str(
|
|
||||||
user_messages,
|
|
||||||
replace_bot_name=False,
|
|
||||||
timestamp_mode="absolute"
|
|
||||||
)
|
|
||||||
|
|
||||||
return readable_history
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **时间戳格式**:所有时间参数都使用Unix时间戳(float类型)
|
1. **时间戳格式**:所有时间参数都使用Unix时间戳(float类型)
|
||||||
2. **异步函数**:`build_readable_messages_with_details` 和 `get_person_ids_from_messages` 是异步函数,需要使用 `await`
|
2. **异步函数**:部分函数是异步函数,需要使用 `await`
|
||||||
3. **性能考虑**:查询大量消息时建议设置合理的 `limit` 参数
|
3. **性能考虑**:查询大量消息时建议设置合理的 `limit` 参数
|
||||||
4. **消息格式**:返回的消息是字典格式,包含时间戳、发送者、内容等信息
|
4. **消息格式**:返回的消息是字典格式,包含时间戳、发送者、内容等信息
|
||||||
5. **用户ID**:`person_ids` 参数接受字符串列表,用于筛选特定用户的消息
|
5. **用户ID**:`person_ids` 参数接受字符串列表,用于筛选特定用户的消息
|
||||||
@@ -6,59 +6,65 @@
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import person_api
|
from src.plugin_system.apis import person_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import person_api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主要功能
|
## 主要功能
|
||||||
|
|
||||||
### 1. Person ID管理
|
### 1. Person ID 获取
|
||||||
|
```python
|
||||||
#### `get_person_id(platform: str, user_id: int) -> str`
|
def get_person_id(platform: str, user_id: int) -> str:
|
||||||
|
```
|
||||||
根据平台和用户ID获取person_id
|
根据平台和用户ID获取person_id
|
||||||
|
|
||||||
**参数:**
|
**Args:**
|
||||||
- `platform`:平台名称,如 "qq", "telegram" 等
|
- `platform`:平台名称,如 "qq", "telegram" 等
|
||||||
- `user_id`:用户ID
|
- `user_id`:用户ID
|
||||||
|
|
||||||
**返回:**
|
**Returns:**
|
||||||
- `str`:唯一的person_id(MD5哈希值)
|
- `str`:唯一的person_id(MD5哈希值)
|
||||||
|
|
||||||
**示例:**
|
#### 示例
|
||||||
```python
|
```python
|
||||||
person_id = person_api.get_person_id("qq", 123456)
|
person_id = person_api.get_person_id("qq", 123456)
|
||||||
print(f"Person ID: {person_id}")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 用户信息查询
|
### 2. 用户信息查询
|
||||||
|
```python
|
||||||
|
async def get_person_value(person_id: str, field_name: str, default: Any = None) -> Any:
|
||||||
|
```
|
||||||
|
查询单个用户信息字段值
|
||||||
|
|
||||||
#### `get_person_value(person_id: str, field_name: str, default: Any = None) -> Any`
|
**Args:**
|
||||||
根据person_id和字段名获取某个值
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `person_id`:用户的唯一标识ID
|
- `person_id`:用户的唯一标识ID
|
||||||
- `field_name`:要获取的字段名,如 "nickname", "impression" 等
|
- `field_name`:要获取的字段名
|
||||||
- `default`:当字段不存在或获取失败时返回的默认值
|
- `default`:字段值不存在时的默认值
|
||||||
|
|
||||||
**返回:**
|
**Returns:**
|
||||||
- `Any`:字段值或默认值
|
- `Any`:字段值或默认值
|
||||||
|
|
||||||
**示例:**
|
#### 示例
|
||||||
```python
|
```python
|
||||||
nickname = await person_api.get_person_value(person_id, "nickname", "未知用户")
|
nickname = await person_api.get_person_value(person_id, "nickname", "未知用户")
|
||||||
impression = await person_api.get_person_value(person_id, "impression")
|
impression = await person_api.get_person_value(person_id, "impression")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_person_values(person_id: str, field_names: list, default_dict: dict = None) -> dict`
|
### 3. 批量用户信息查询
|
||||||
|
```python
|
||||||
|
async def get_person_values(person_id: str, field_names: list, default_dict: Optional[dict] = None) -> dict:
|
||||||
|
```
|
||||||
批量获取用户信息字段值
|
批量获取用户信息字段值
|
||||||
|
|
||||||
**参数:**
|
**Args:**
|
||||||
- `person_id`:用户的唯一标识ID
|
- `person_id`:用户的唯一标识ID
|
||||||
- `field_names`:要获取的字段名列表
|
- `field_names`:要获取的字段名列表
|
||||||
- `default_dict`:默认值字典,键为字段名,值为默认值
|
- `default_dict`:默认值字典,键为字段名,值为默认值
|
||||||
|
|
||||||
**返回:**
|
**Returns:**
|
||||||
- `dict`:字段名到值的映射字典
|
- `dict`:字段名到值的映射字典
|
||||||
|
|
||||||
**示例:**
|
#### 示例
|
||||||
```python
|
```python
|
||||||
values = await person_api.get_person_values(
|
values = await person_api.get_person_values(
|
||||||
person_id,
|
person_id,
|
||||||
@@ -67,204 +73,31 @@ values = await person_api.get_person_values(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 用户状态查询
|
### 4. 判断用户是否已知
|
||||||
|
```python
|
||||||
#### `is_person_known(platform: str, user_id: int) -> bool`
|
async def is_person_known(platform: str, user_id: int) -> bool:
|
||||||
|
```
|
||||||
判断是否认识某个用户
|
判断是否认识某个用户
|
||||||
|
|
||||||
**参数:**
|
**Args:**
|
||||||
- `platform`:平台名称
|
- `platform`:平台名称
|
||||||
- `user_id`:用户ID
|
- `user_id`:用户ID
|
||||||
|
|
||||||
**返回:**
|
**Returns:**
|
||||||
- `bool`:是否认识该用户
|
- `bool`:是否认识该用户
|
||||||
|
|
||||||
**示例:**
|
### 5. 根据用户名获取Person ID
|
||||||
```python
|
```python
|
||||||
known = await person_api.is_person_known("qq", 123456)
|
def get_person_id_by_name(person_name: str) -> str:
|
||||||
if known:
|
|
||||||
print("这个用户我认识")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 用户名查询
|
|
||||||
|
|
||||||
#### `get_person_id_by_name(person_name: str) -> str`
|
|
||||||
根据用户名获取person_id
|
根据用户名获取person_id
|
||||||
|
|
||||||
**参数:**
|
**Args:**
|
||||||
- `person_name`:用户名
|
- `person_name`:用户名
|
||||||
|
|
||||||
**返回:**
|
**Returns:**
|
||||||
- `str`:person_id,如果未找到返回空字符串
|
- `str`:person_id,如果未找到返回空字符串
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
|
||||||
person_id = person_api.get_person_id_by_name("张三")
|
|
||||||
if person_id:
|
|
||||||
print(f"找到用户: {person_id}")
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 1. 基础用户信息获取
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.apis import person_api
|
|
||||||
|
|
||||||
async def get_user_info(platform: str, user_id: int):
|
|
||||||
"""获取用户基本信息"""
|
|
||||||
|
|
||||||
# 获取person_id
|
|
||||||
person_id = person_api.get_person_id(platform, user_id)
|
|
||||||
|
|
||||||
# 获取用户信息
|
|
||||||
user_info = await person_api.get_person_values(
|
|
||||||
person_id,
|
|
||||||
["nickname", "impression", "know_times", "last_seen"],
|
|
||||||
{
|
|
||||||
"nickname": "未知用户",
|
|
||||||
"impression": "",
|
|
||||||
"know_times": 0,
|
|
||||||
"last_seen": 0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"person_id": person_id,
|
|
||||||
"nickname": user_info["nickname"],
|
|
||||||
"impression": user_info["impression"],
|
|
||||||
"know_times": user_info["know_times"],
|
|
||||||
"last_seen": user_info["last_seen"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 在Action中使用用户信息
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.base import BaseAction
|
|
||||||
|
|
||||||
class PersonalizedAction(BaseAction):
|
|
||||||
async def execute(self, action_data, chat_stream):
|
|
||||||
# 获取发送者信息
|
|
||||||
user_id = chat_stream.user_info.user_id
|
|
||||||
platform = chat_stream.platform
|
|
||||||
|
|
||||||
# 获取person_id
|
|
||||||
person_id = person_api.get_person_id(platform, user_id)
|
|
||||||
|
|
||||||
# 获取用户昵称和印象
|
|
||||||
nickname = await person_api.get_person_value(person_id, "nickname", "朋友")
|
|
||||||
impression = await person_api.get_person_value(person_id, "impression", "")
|
|
||||||
|
|
||||||
# 根据用户信息个性化回复
|
|
||||||
if impression:
|
|
||||||
response = f"你好 {nickname}!根据我对你的了解:{impression}"
|
|
||||||
else:
|
|
||||||
response = f"你好 {nickname}!很高兴见到你。"
|
|
||||||
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"response": response,
|
|
||||||
"user_info": {
|
|
||||||
"nickname": nickname,
|
|
||||||
"impression": impression
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 用户识别和欢迎
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def welcome_user(chat_stream):
|
|
||||||
"""欢迎用户,区分新老用户"""
|
|
||||||
|
|
||||||
user_id = chat_stream.user_info.user_id
|
|
||||||
platform = chat_stream.platform
|
|
||||||
|
|
||||||
# 检查是否认识这个用户
|
|
||||||
is_known = await person_api.is_person_known(platform, user_id)
|
|
||||||
|
|
||||||
if is_known:
|
|
||||||
# 老用户,获取详细信息
|
|
||||||
person_id = person_api.get_person_id(platform, user_id)
|
|
||||||
nickname = await person_api.get_person_value(person_id, "nickname", "老朋友")
|
|
||||||
know_times = await person_api.get_person_value(person_id, "know_times", 0)
|
|
||||||
|
|
||||||
welcome_msg = f"欢迎回来,{nickname}!我们已经聊过 {know_times} 次了。"
|
|
||||||
else:
|
|
||||||
# 新用户
|
|
||||||
welcome_msg = "你好!很高兴认识你,我是MaiBot。"
|
|
||||||
|
|
||||||
return welcome_msg
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 用户搜索功能
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def find_user_by_name(name: str):
|
|
||||||
"""根据名字查找用户"""
|
|
||||||
|
|
||||||
person_id = person_api.get_person_id_by_name(name)
|
|
||||||
|
|
||||||
if not person_id:
|
|
||||||
return {"found": False, "message": f"未找到名为 '{name}' 的用户"}
|
|
||||||
|
|
||||||
# 获取用户详细信息
|
|
||||||
user_info = await person_api.get_person_values(
|
|
||||||
person_id,
|
|
||||||
["nickname", "platform", "user_id", "impression", "know_times"],
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"found": True,
|
|
||||||
"person_id": person_id,
|
|
||||||
"info": user_info
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 用户印象分析
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def analyze_user_relationship(chat_stream):
|
|
||||||
"""分析用户关系"""
|
|
||||||
|
|
||||||
user_id = chat_stream.user_info.user_id
|
|
||||||
platform = chat_stream.platform
|
|
||||||
person_id = person_api.get_person_id(platform, user_id)
|
|
||||||
|
|
||||||
# 获取关系相关信息
|
|
||||||
relationship_info = await person_api.get_person_values(
|
|
||||||
person_id,
|
|
||||||
["nickname", "impression", "know_times", "relationship_level", "last_interaction"],
|
|
||||||
{
|
|
||||||
"nickname": "未知",
|
|
||||||
"impression": "",
|
|
||||||
"know_times": 0,
|
|
||||||
"relationship_level": "stranger",
|
|
||||||
"last_interaction": 0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# 分析关系程度
|
|
||||||
know_times = relationship_info["know_times"]
|
|
||||||
if know_times == 0:
|
|
||||||
relationship = "陌生人"
|
|
||||||
elif know_times < 5:
|
|
||||||
relationship = "新朋友"
|
|
||||||
elif know_times < 20:
|
|
||||||
relationship = "熟人"
|
|
||||||
else:
|
|
||||||
relationship = "老朋友"
|
|
||||||
|
|
||||||
return {
|
|
||||||
"nickname": relationship_info["nickname"],
|
|
||||||
"relationship": relationship,
|
|
||||||
"impression": relationship_info["impression"],
|
|
||||||
"interaction_count": know_times
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 常用字段说明
|
## 常用字段说明
|
||||||
|
|
||||||
### 基础信息字段
|
### 基础信息字段
|
||||||
@@ -274,69 +107,13 @@ async def analyze_user_relationship(chat_stream):
|
|||||||
|
|
||||||
### 关系信息字段
|
### 关系信息字段
|
||||||
- `impression`:对用户的印象
|
- `impression`:对用户的印象
|
||||||
- `know_times`:交互次数
|
- `points`: 用户特征点
|
||||||
- `relationship_level`:关系等级
|
|
||||||
- `last_seen`:最后见面时间
|
|
||||||
- `last_interaction`:最后交互时间
|
|
||||||
|
|
||||||
### 个性化字段
|
其他字段可以参考`PersonInfo`类的属性(位于`src.common.database.database_model`)
|
||||||
- `preferences`:用户偏好
|
|
||||||
- `interests`:兴趣爱好
|
|
||||||
- `mood_history`:情绪历史
|
|
||||||
- `topic_interests`:话题兴趣
|
|
||||||
|
|
||||||
## 最佳实践
|
|
||||||
|
|
||||||
### 1. 错误处理
|
|
||||||
```python
|
|
||||||
async def safe_get_user_info(person_id: str, field: str):
|
|
||||||
"""安全获取用户信息"""
|
|
||||||
try:
|
|
||||||
value = await person_api.get_person_value(person_id, field)
|
|
||||||
return value if value is not None else "未设置"
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"获取用户信息失败: {e}")
|
|
||||||
return "获取失败"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 批量操作
|
|
||||||
```python
|
|
||||||
async def get_complete_user_profile(person_id: str):
|
|
||||||
"""获取完整用户档案"""
|
|
||||||
|
|
||||||
# 一次性获取所有需要的字段
|
|
||||||
fields = [
|
|
||||||
"nickname", "impression", "know_times",
|
|
||||||
"preferences", "interests", "relationship_level"
|
|
||||||
]
|
|
||||||
|
|
||||||
defaults = {
|
|
||||||
"nickname": "用户",
|
|
||||||
"impression": "",
|
|
||||||
"know_times": 0,
|
|
||||||
"preferences": "{}",
|
|
||||||
"interests": "[]",
|
|
||||||
"relationship_level": "stranger"
|
|
||||||
}
|
|
||||||
|
|
||||||
profile = await person_api.get_person_values(person_id, fields, defaults)
|
|
||||||
|
|
||||||
# 处理JSON字段
|
|
||||||
try:
|
|
||||||
profile["preferences"] = json.loads(profile["preferences"])
|
|
||||||
profile["interests"] = json.loads(profile["interests"])
|
|
||||||
except:
|
|
||||||
profile["preferences"] = {}
|
|
||||||
profile["interests"] = []
|
|
||||||
|
|
||||||
return profile
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **异步操作**:大部分查询函数都是异步的,需要使用`await`
|
1. **异步操作**:部分查询函数都是异步的,需要使用`await`
|
||||||
2. **错误处理**:所有函数都有错误处理,失败时记录日志并返回默认值
|
2. **性能考虑**:批量查询优于单个查询
|
||||||
3. **数据类型**:返回的数据可能是字符串、数字或JSON,需要适当处理
|
3. **隐私保护**:确保用户信息的使用符合隐私政策
|
||||||
4. **性能考虑**:批量查询优于单个查询
|
4. **数据一致性**:person_id是用户的唯一标识,应妥善保存和使用
|
||||||
5. **隐私保护**:确保用户信息的使用符合隐私政策
|
|
||||||
6. **数据一致性**:person_id是用户的唯一标识,应妥善保存和使用
|
|
||||||
105
docs/plugins/api/plugin-manage-api.md
Normal file
105
docs/plugins/api/plugin-manage-api.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# 插件管理API
|
||||||
|
|
||||||
|
插件管理API模块提供了对插件的加载、卸载、重新加载以及目录管理功能。
|
||||||
|
|
||||||
|
## 导入方式
|
||||||
|
```python
|
||||||
|
from src.plugin_system.apis import plugin_manage_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import plugin_manage_api
|
||||||
|
```
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
|
||||||
|
插件管理API主要提供以下功能:
|
||||||
|
- **插件查询** - 列出当前加载的插件或已注册的插件。
|
||||||
|
- **插件管理** - 加载、卸载、重新加载插件。
|
||||||
|
- **插件目录管理** - 添加插件目录并重新扫描。
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
|
||||||
|
### 1. 列出当前加载的插件
|
||||||
|
```python
|
||||||
|
def list_loaded_plugins() -> List[str]:
|
||||||
|
```
|
||||||
|
列出所有当前加载的插件。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `List[str]` - 当前加载的插件名称列表。
|
||||||
|
|
||||||
|
### 2. 列出所有已注册的插件
|
||||||
|
```python
|
||||||
|
def list_registered_plugins() -> List[str]:
|
||||||
|
```
|
||||||
|
列出所有已注册的插件。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `List[str]` - 已注册的插件名称列表。
|
||||||
|
|
||||||
|
### 3. 获取插件路径
|
||||||
|
```python
|
||||||
|
def get_plugin_path(plugin_name: str) -> str:
|
||||||
|
```
|
||||||
|
获取指定插件的路径。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `plugin_name` (str): 要查询的插件名称。
|
||||||
|
**Returns:**
|
||||||
|
- `str` - 插件的路径,如果插件不存在则 raise ValueError。
|
||||||
|
|
||||||
|
### 4. 卸载指定的插件
|
||||||
|
```python
|
||||||
|
async def remove_plugin(plugin_name: str) -> bool:
|
||||||
|
```
|
||||||
|
卸载指定的插件。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `plugin_name` (str): 要卸载的插件名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `bool` - 卸载是否成功。
|
||||||
|
|
||||||
|
### 5. 重新加载指定的插件
|
||||||
|
```python
|
||||||
|
async def reload_plugin(plugin_name: str) -> bool:
|
||||||
|
```
|
||||||
|
重新加载指定的插件。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `plugin_name` (str): 要重新加载的插件名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `bool` - 重新加载是否成功。
|
||||||
|
|
||||||
|
### 6. 加载指定的插件
|
||||||
|
```python
|
||||||
|
def load_plugin(plugin_name: str) -> Tuple[bool, int]:
|
||||||
|
```
|
||||||
|
加载指定的插件。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `plugin_name` (str): 要加载的插件名称。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Tuple[bool, int]` - 加载是否成功,成功或失败的个数。
|
||||||
|
|
||||||
|
### 7. 添加插件目录
|
||||||
|
```python
|
||||||
|
def add_plugin_directory(plugin_directory: str) -> bool:
|
||||||
|
```
|
||||||
|
添加插件目录。
|
||||||
|
|
||||||
|
**Args:**
|
||||||
|
- `plugin_directory` (str): 要添加的插件目录路径。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `bool` - 添加是否成功。
|
||||||
|
|
||||||
|
### 8. 重新扫描插件目录
|
||||||
|
```python
|
||||||
|
def rescan_plugin_directory() -> Tuple[int, int]:
|
||||||
|
```
|
||||||
|
重新扫描插件目录,加载新插件。
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
- `Tuple[int, int]` - 成功加载的插件数量和失败的插件数量。
|
||||||
@@ -6,86 +6,108 @@
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import send_api
|
from src.plugin_system.apis import send_api
|
||||||
|
# 或者
|
||||||
|
from src.plugin_system import send_api
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主要功能
|
## 主要功能
|
||||||
|
|
||||||
### 1. 文本消息发送
|
### 1. 发送文本消息
|
||||||
|
```python
|
||||||
|
async def text_to_stream(
|
||||||
|
text: str,
|
||||||
|
stream_id: str,
|
||||||
|
typing: bool = False,
|
||||||
|
reply_to: str = "",
|
||||||
|
storage_message: bool = True,
|
||||||
|
) -> bool:
|
||||||
|
```
|
||||||
|
发送文本消息到指定的流
|
||||||
|
|
||||||
#### `text_to_group(text, group_id, platform="qq", typing=False, reply_to="", storage_message=True)`
|
**Args:**
|
||||||
向群聊发送文本消息
|
- `text` (str): 要发送的文本内容
|
||||||
|
- `stream_id` (str): 聊天流ID
|
||||||
|
- `typing` (bool): 是否显示正在输入
|
||||||
|
- `reply_to` (str): 回复消息,格式为"发送者:消息内容"
|
||||||
|
- `storage_message` (bool): 是否存储消息到数据库
|
||||||
|
|
||||||
**参数:**
|
**Returns:**
|
||||||
- `text`:要发送的文本内容
|
- `bool` - 是否发送成功
|
||||||
- `group_id`:群聊ID
|
|
||||||
- `platform`:平台,默认为"qq"
|
|
||||||
- `typing`:是否显示正在输入
|
|
||||||
- `reply_to`:回复消息的格式,如"发送者:消息内容"
|
|
||||||
- `storage_message`:是否存储到数据库
|
|
||||||
|
|
||||||
**返回:**
|
### 2. 发送表情包
|
||||||
- `bool`:是否发送成功
|
```python
|
||||||
|
async def emoji_to_stream(emoji_base64: str, stream_id: str, storage_message: bool = True) -> bool:
|
||||||
|
```
|
||||||
|
向指定流发送表情包。
|
||||||
|
|
||||||
#### `text_to_user(text, user_id, platform="qq", typing=False, reply_to="", storage_message=True)`
|
**Args:**
|
||||||
向用户发送私聊文本消息
|
- `emoji_base64` (str): 表情包的base64编码
|
||||||
|
- `stream_id` (str): 聊天流ID
|
||||||
|
- `storage_message` (bool): 是否存储消息到数据库
|
||||||
|
|
||||||
**参数与返回值同上**
|
**Returns:**
|
||||||
|
- `bool` - 是否发送成功
|
||||||
|
|
||||||
### 2. 表情包发送
|
### 3. 发送图片
|
||||||
|
```python
|
||||||
|
async def image_to_stream(image_base64: str, stream_id: str, storage_message: bool = True) -> bool:
|
||||||
|
```
|
||||||
|
向指定流发送图片。
|
||||||
|
|
||||||
#### `emoji_to_group(emoji_base64, group_id, platform="qq", storage_message=True)`
|
**Args:**
|
||||||
向群聊发送表情包
|
- `image_base64` (str): 图片的base64编码
|
||||||
|
- `stream_id` (str): 聊天流ID
|
||||||
|
- `storage_message` (bool): 是否存储消息到数据库
|
||||||
|
|
||||||
**参数:**
|
**Returns:**
|
||||||
- `emoji_base64`:表情包的base64编码
|
- `bool` - 是否发送成功
|
||||||
- `group_id`:群聊ID
|
|
||||||
- `platform`:平台,默认为"qq"
|
|
||||||
- `storage_message`:是否存储到数据库
|
|
||||||
|
|
||||||
#### `emoji_to_user(emoji_base64, user_id, platform="qq", storage_message=True)`
|
### 4. 发送命令
|
||||||
向用户发送表情包
|
```python
|
||||||
|
async def command_to_stream(command: Union[str, dict], stream_id: str, storage_message: bool = True, display_message: str = "") -> bool:
|
||||||
|
```
|
||||||
|
向指定流发送命令。
|
||||||
|
|
||||||
### 3. 图片发送
|
**Args:**
|
||||||
|
- `command` (Union[str, dict]): 命令内容
|
||||||
|
- `stream_id` (str): 聊天流ID
|
||||||
|
- `storage_message` (bool): 是否存储消息到数据库
|
||||||
|
- `display_message` (str): 显示消息
|
||||||
|
|
||||||
#### `image_to_group(image_base64, group_id, platform="qq", storage_message=True)`
|
**Returns:**
|
||||||
向群聊发送图片
|
- `bool` - 是否发送成功
|
||||||
|
|
||||||
#### `image_to_user(image_base64, user_id, platform="qq", storage_message=True)`
|
### 5. 发送自定义类型消息
|
||||||
向用户发送图片
|
```python
|
||||||
|
async def custom_to_stream(
|
||||||
|
message_type: str,
|
||||||
|
content: str,
|
||||||
|
stream_id: str,
|
||||||
|
display_message: str = "",
|
||||||
|
typing: bool = False,
|
||||||
|
reply_to: str = "",
|
||||||
|
storage_message: bool = True,
|
||||||
|
show_log: bool = True,
|
||||||
|
) -> bool:
|
||||||
|
```
|
||||||
|
向指定流发送自定义类型消息。
|
||||||
|
|
||||||
### 4. 命令发送
|
**Args:**
|
||||||
|
- `message_type` (str): 消息类型,如"text"、"image"、"emoji"、"video"、"file"等
|
||||||
|
- `content` (str): 消息内容(通常是base64编码或文本)
|
||||||
|
- `stream_id` (str): 聊天流ID
|
||||||
|
- `display_message` (str): 显示消息
|
||||||
|
- `typing` (bool): 是否显示正在输入
|
||||||
|
- `reply_to` (str): 回复消息,格式为"发送者:消息内容"
|
||||||
|
- `storage_message` (bool): 是否存储消息到数据库
|
||||||
|
- `show_log` (bool): 是否显示日志
|
||||||
|
|
||||||
#### `command_to_group(command, group_id, platform="qq", storage_message=True)`
|
**Returns:**
|
||||||
向群聊发送命令
|
- `bool` - 是否发送成功
|
||||||
|
|
||||||
#### `command_to_user(command, user_id, platform="qq", storage_message=True)`
|
|
||||||
向用户发送命令
|
|
||||||
|
|
||||||
### 5. 自定义消息发送
|
|
||||||
|
|
||||||
#### `custom_to_group(message_type, content, group_id, platform="qq", display_message="", typing=False, reply_to="", storage_message=True)`
|
|
||||||
向群聊发送自定义类型消息
|
|
||||||
|
|
||||||
#### `custom_to_user(message_type, content, user_id, platform="qq", display_message="", typing=False, reply_to="", storage_message=True)`
|
|
||||||
向用户发送自定义类型消息
|
|
||||||
|
|
||||||
#### `custom_message(message_type, content, target_id, is_group=True, platform="qq", display_message="", typing=False, reply_to="", storage_message=True)`
|
|
||||||
通用的自定义消息发送
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `message_type`:消息类型,如"text"、"image"、"emoji"等
|
|
||||||
- `content`:消息内容
|
|
||||||
- `target_id`:目标ID(群ID或用户ID)
|
|
||||||
- `is_group`:是否为群聊
|
|
||||||
- `platform`:平台
|
|
||||||
- `display_message`:显示消息
|
|
||||||
- `typing`:是否显示正在输入
|
|
||||||
- `reply_to`:回复消息
|
|
||||||
- `storage_message`:是否存储
|
|
||||||
|
|
||||||
## 使用示例
|
## 使用示例
|
||||||
|
|
||||||
### 1. 基础文本发送
|
### 1. 基础文本发送,并回复消息
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from src.plugin_system.apis import send_api
|
from src.plugin_system.apis import send_api
|
||||||
@@ -93,57 +115,23 @@ from src.plugin_system.apis import send_api
|
|||||||
async def send_hello(chat_stream):
|
async def send_hello(chat_stream):
|
||||||
"""发送问候消息"""
|
"""发送问候消息"""
|
||||||
|
|
||||||
if chat_stream.group_info:
|
success = await send_api.text_to_stream(
|
||||||
# 群聊
|
text="Hello, world!",
|
||||||
success = await send_api.text_to_group(
|
stream_id=chat_stream.stream_id,
|
||||||
text="大家好!",
|
typing=True,
|
||||||
group_id=chat_stream.group_info.group_id,
|
reply_to="User:How are you?",
|
||||||
typing=True
|
storage_message=True
|
||||||
)
|
|
||||||
else:
|
|
||||||
# 私聊
|
|
||||||
success = await send_api.text_to_user(
|
|
||||||
text="你好!",
|
|
||||||
user_id=chat_stream.user_info.user_id,
|
|
||||||
typing=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return success
|
return success
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 回复特定消息
|
### 2. 发送表情包
|
||||||
|
|
||||||
```python
|
|
||||||
async def reply_to_message(chat_stream, reply_text, original_sender, original_message):
|
|
||||||
"""回复特定消息"""
|
|
||||||
|
|
||||||
# 构建回复格式
|
|
||||||
reply_to = f"{original_sender}:{original_message}"
|
|
||||||
|
|
||||||
if chat_stream.group_info:
|
|
||||||
success = await send_api.text_to_group(
|
|
||||||
text=reply_text,
|
|
||||||
group_id=chat_stream.group_info.group_id,
|
|
||||||
reply_to=reply_to
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
success = await send_api.text_to_user(
|
|
||||||
text=reply_text,
|
|
||||||
user_id=chat_stream.user_info.user_id,
|
|
||||||
reply_to=reply_to
|
|
||||||
)
|
|
||||||
|
|
||||||
return success
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 发送表情包
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
from src.plugin_system.apis import emoji_api
|
||||||
async def send_emoji_reaction(chat_stream, emotion):
|
async def send_emoji_reaction(chat_stream, emotion):
|
||||||
"""根据情感发送表情包"""
|
"""根据情感发送表情包"""
|
||||||
|
|
||||||
from src.plugin_system.apis import emoji_api
|
|
||||||
|
|
||||||
# 获取表情包
|
# 获取表情包
|
||||||
emoji_result = await emoji_api.get_by_emotion(emotion)
|
emoji_result = await emoji_api.get_by_emotion(emotion)
|
||||||
if not emoji_result:
|
if not emoji_result:
|
||||||
@@ -152,107 +140,10 @@ async def send_emoji_reaction(chat_stream, emotion):
|
|||||||
emoji_base64, description, matched_emotion = emoji_result
|
emoji_base64, description, matched_emotion = emoji_result
|
||||||
|
|
||||||
# 发送表情包
|
# 发送表情包
|
||||||
if chat_stream.group_info:
|
success = await send_api.emoji_to_stream(
|
||||||
success = await send_api.emoji_to_group(
|
|
||||||
emoji_base64=emoji_base64,
|
emoji_base64=emoji_base64,
|
||||||
group_id=chat_stream.group_info.group_id
|
stream_id=chat_stream.stream_id,
|
||||||
)
|
storage_message=False # 不存储到数据库
|
||||||
else:
|
|
||||||
success = await send_api.emoji_to_user(
|
|
||||||
emoji_base64=emoji_base64,
|
|
||||||
user_id=chat_stream.user_info.user_id
|
|
||||||
)
|
|
||||||
|
|
||||||
return success
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 在Action中发送消息
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.base import BaseAction
|
|
||||||
|
|
||||||
class MessageAction(BaseAction):
|
|
||||||
async def execute(self, action_data, chat_stream):
|
|
||||||
message_type = action_data.get("type", "text")
|
|
||||||
content = action_data.get("content", "")
|
|
||||||
|
|
||||||
if message_type == "text":
|
|
||||||
success = await self.send_text(chat_stream, content)
|
|
||||||
elif message_type == "emoji":
|
|
||||||
success = await self.send_emoji(chat_stream, content)
|
|
||||||
elif message_type == "image":
|
|
||||||
success = await self.send_image(chat_stream, content)
|
|
||||||
else:
|
|
||||||
success = False
|
|
||||||
|
|
||||||
return {"success": success}
|
|
||||||
|
|
||||||
async def send_text(self, chat_stream, text):
|
|
||||||
if chat_stream.group_info:
|
|
||||||
return await send_api.text_to_group(text, chat_stream.group_info.group_id)
|
|
||||||
else:
|
|
||||||
return await send_api.text_to_user(text, chat_stream.user_info.user_id)
|
|
||||||
|
|
||||||
async def send_emoji(self, chat_stream, emoji_base64):
|
|
||||||
if chat_stream.group_info:
|
|
||||||
return await send_api.emoji_to_group(emoji_base64, chat_stream.group_info.group_id)
|
|
||||||
else:
|
|
||||||
return await send_api.emoji_to_user(emoji_base64, chat_stream.user_info.user_id)
|
|
||||||
|
|
||||||
async def send_image(self, chat_stream, image_base64):
|
|
||||||
if chat_stream.group_info:
|
|
||||||
return await send_api.image_to_group(image_base64, chat_stream.group_info.group_id)
|
|
||||||
else:
|
|
||||||
return await send_api.image_to_user(image_base64, chat_stream.user_info.user_id)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 批量发送消息
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def broadcast_message(message: str, target_groups: list):
|
|
||||||
"""向多个群组广播消息"""
|
|
||||||
|
|
||||||
results = {}
|
|
||||||
|
|
||||||
for group_id in target_groups:
|
|
||||||
try:
|
|
||||||
success = await send_api.text_to_group(
|
|
||||||
text=message,
|
|
||||||
group_id=group_id,
|
|
||||||
typing=True
|
|
||||||
)
|
|
||||||
results[group_id] = success
|
|
||||||
except Exception as e:
|
|
||||||
results[group_id] = False
|
|
||||||
print(f"发送到群 {group_id} 失败: {e}")
|
|
||||||
|
|
||||||
return results
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. 智能消息发送
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def smart_send(chat_stream, message_data):
|
|
||||||
"""智能发送不同类型的消息"""
|
|
||||||
|
|
||||||
message_type = message_data.get("type", "text")
|
|
||||||
content = message_data.get("content", "")
|
|
||||||
options = message_data.get("options", {})
|
|
||||||
|
|
||||||
# 根据聊天流类型选择发送方法
|
|
||||||
target_id = (chat_stream.group_info.group_id if chat_stream.group_info
|
|
||||||
else chat_stream.user_info.user_id)
|
|
||||||
is_group = chat_stream.group_info is not None
|
|
||||||
|
|
||||||
# 使用通用发送方法
|
|
||||||
success = await send_api.custom_message(
|
|
||||||
message_type=message_type,
|
|
||||||
content=content,
|
|
||||||
target_id=target_id,
|
|
||||||
is_group=is_group,
|
|
||||||
typing=options.get("typing", False),
|
|
||||||
reply_to=options.get("reply_to", ""),
|
|
||||||
display_message=options.get("display_message", "")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return success
|
return success
|
||||||
@@ -273,90 +164,6 @@ async def smart_send(chat_stream, message_data):
|
|||||||
|
|
||||||
系统会自动查找匹配的原始消息并进行回复。
|
系统会自动查找匹配的原始消息并进行回复。
|
||||||
|
|
||||||
## 高级用法
|
|
||||||
|
|
||||||
### 1. 消息发送队列
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
class MessageQueue:
|
|
||||||
def __init__(self):
|
|
||||||
self.queue = asyncio.Queue()
|
|
||||||
self.running = False
|
|
||||||
|
|
||||||
async def add_message(self, chat_stream, message_type, content, options=None):
|
|
||||||
"""添加消息到队列"""
|
|
||||||
message_item = {
|
|
||||||
"chat_stream": chat_stream,
|
|
||||||
"type": message_type,
|
|
||||||
"content": content,
|
|
||||||
"options": options or {}
|
|
||||||
}
|
|
||||||
await self.queue.put(message_item)
|
|
||||||
|
|
||||||
async def process_queue(self):
|
|
||||||
"""处理消息队列"""
|
|
||||||
self.running = True
|
|
||||||
|
|
||||||
while self.running:
|
|
||||||
try:
|
|
||||||
message_item = await asyncio.wait_for(self.queue.get(), timeout=1.0)
|
|
||||||
|
|
||||||
# 发送消息
|
|
||||||
success = await smart_send(
|
|
||||||
message_item["chat_stream"],
|
|
||||||
{
|
|
||||||
"type": message_item["type"],
|
|
||||||
"content": message_item["content"],
|
|
||||||
"options": message_item["options"]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# 标记任务完成
|
|
||||||
self.queue.task_done()
|
|
||||||
|
|
||||||
# 发送间隔
|
|
||||||
await asyncio.sleep(0.5)
|
|
||||||
|
|
||||||
except asyncio.TimeoutError:
|
|
||||||
continue
|
|
||||||
except Exception as e:
|
|
||||||
print(f"处理消息队列出错: {e}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 消息模板系统
|
|
||||||
|
|
||||||
```python
|
|
||||||
class MessageTemplate:
|
|
||||||
def __init__(self):
|
|
||||||
self.templates = {
|
|
||||||
"welcome": "欢迎 {nickname} 加入群聊!",
|
|
||||||
"goodbye": "{nickname} 离开了群聊。",
|
|
||||||
"notification": "🔔 通知:{message}",
|
|
||||||
"error": "❌ 错误:{error_message}",
|
|
||||||
"success": "✅ 成功:{message}"
|
|
||||||
}
|
|
||||||
|
|
||||||
def format_message(self, template_name: str, **kwargs) -> str:
|
|
||||||
"""格式化消息模板"""
|
|
||||||
template = self.templates.get(template_name, "{message}")
|
|
||||||
return template.format(**kwargs)
|
|
||||||
|
|
||||||
async def send_template(self, chat_stream, template_name: str, **kwargs):
|
|
||||||
"""发送模板消息"""
|
|
||||||
message = self.format_message(template_name, **kwargs)
|
|
||||||
|
|
||||||
if chat_stream.group_info:
|
|
||||||
return await send_api.text_to_group(message, chat_stream.group_info.group_id)
|
|
||||||
else:
|
|
||||||
return await send_api.text_to_user(message, chat_stream.user_info.user_id)
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
template_system = MessageTemplate()
|
|
||||||
await template_system.send_template(chat_stream, "welcome", nickname="张三")
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **异步操作**:所有发送函数都是异步的,必须使用`await`
|
1. **异步操作**:所有发送函数都是异步的,必须使用`await`
|
||||||
|
|||||||
@@ -1,435 +0,0 @@
|
|||||||
# 工具API
|
|
||||||
|
|
||||||
工具API模块提供了各种辅助功能,包括文件操作、时间处理、唯一ID生成等常用工具函数。
|
|
||||||
|
|
||||||
## 导入方式
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.apis import utils_api
|
|
||||||
```
|
|
||||||
|
|
||||||
## 主要功能
|
|
||||||
|
|
||||||
### 1. 文件操作
|
|
||||||
|
|
||||||
#### `get_plugin_path(caller_frame=None) -> str`
|
|
||||||
获取调用者插件的路径
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `caller_frame`:调用者的栈帧,默认为None(自动获取)
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `str`:插件目录的绝对路径
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
|
||||||
plugin_path = utils_api.get_plugin_path()
|
|
||||||
print(f"插件路径: {plugin_path}")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `read_json_file(file_path: str, default: Any = None) -> Any`
|
|
||||||
读取JSON文件
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `file_path`:文件路径,可以是相对于插件目录的路径
|
|
||||||
- `default`:如果文件不存在或读取失败时返回的默认值
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `Any`:JSON数据或默认值
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
|
||||||
# 读取插件配置文件
|
|
||||||
config = utils_api.read_json_file("config.json", {})
|
|
||||||
settings = utils_api.read_json_file("data/settings.json", {"enabled": True})
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `write_json_file(file_path: str, data: Any, indent: int = 2) -> bool`
|
|
||||||
写入JSON文件
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `file_path`:文件路径,可以是相对于插件目录的路径
|
|
||||||
- `data`:要写入的数据
|
|
||||||
- `indent`:JSON缩进
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `bool`:是否写入成功
|
|
||||||
|
|
||||||
**示例:**
|
|
||||||
```python
|
|
||||||
data = {"name": "test", "value": 123}
|
|
||||||
success = utils_api.write_json_file("output.json", data)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 时间相关
|
|
||||||
|
|
||||||
#### `get_timestamp() -> int`
|
|
||||||
获取当前时间戳
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `int`:当前时间戳(秒)
|
|
||||||
|
|
||||||
#### `format_time(timestamp: Optional[int] = None, format_str: str = "%Y-%m-%d %H:%M:%S") -> str`
|
|
||||||
格式化时间
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `timestamp`:时间戳,如果为None则使用当前时间
|
|
||||||
- `format_str`:时间格式字符串
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `str`:格式化后的时间字符串
|
|
||||||
|
|
||||||
#### `parse_time(time_str: str, format_str: str = "%Y-%m-%d %H:%M:%S") -> int`
|
|
||||||
解析时间字符串为时间戳
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `time_str`:时间字符串
|
|
||||||
- `format_str`:时间格式字符串
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `int`:时间戳(秒)
|
|
||||||
|
|
||||||
### 3. 其他工具
|
|
||||||
|
|
||||||
#### `generate_unique_id() -> str`
|
|
||||||
生成唯一ID
|
|
||||||
|
|
||||||
**返回:**
|
|
||||||
- `str`:唯一ID
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 1. 插件数据管理
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system.apis import utils_api
|
|
||||||
|
|
||||||
class DataPlugin(BasePlugin):
|
|
||||||
def __init__(self):
|
|
||||||
self.plugin_path = utils_api.get_plugin_path()
|
|
||||||
self.data_file = "plugin_data.json"
|
|
||||||
self.load_data()
|
|
||||||
|
|
||||||
def load_data(self):
|
|
||||||
"""加载插件数据"""
|
|
||||||
default_data = {
|
|
||||||
"users": {},
|
|
||||||
"settings": {"enabled": True},
|
|
||||||
"stats": {"message_count": 0}
|
|
||||||
}
|
|
||||||
self.data = utils_api.read_json_file(self.data_file, default_data)
|
|
||||||
|
|
||||||
def save_data(self):
|
|
||||||
"""保存插件数据"""
|
|
||||||
return utils_api.write_json_file(self.data_file, self.data)
|
|
||||||
|
|
||||||
async def handle_action(self, action_data, chat_stream):
|
|
||||||
# 更新统计信息
|
|
||||||
self.data["stats"]["message_count"] += 1
|
|
||||||
self.data["stats"]["last_update"] = utils_api.get_timestamp()
|
|
||||||
|
|
||||||
# 保存数据
|
|
||||||
if self.save_data():
|
|
||||||
return {"success": True, "message": "数据已保存"}
|
|
||||||
else:
|
|
||||||
return {"success": False, "message": "数据保存失败"}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 日志记录系统
|
|
||||||
|
|
||||||
```python
|
|
||||||
class PluginLogger:
|
|
||||||
def __init__(self, plugin_name: str):
|
|
||||||
self.plugin_name = plugin_name
|
|
||||||
self.log_file = f"{plugin_name}_log.json"
|
|
||||||
self.logs = utils_api.read_json_file(self.log_file, [])
|
|
||||||
|
|
||||||
def log_event(self, event_type: str, message: str, data: dict = None):
|
|
||||||
"""记录事件"""
|
|
||||||
log_entry = {
|
|
||||||
"id": utils_api.generate_unique_id(),
|
|
||||||
"timestamp": utils_api.get_timestamp(),
|
|
||||||
"formatted_time": utils_api.format_time(),
|
|
||||||
"event_type": event_type,
|
|
||||||
"message": message,
|
|
||||||
"data": data or {}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.logs.append(log_entry)
|
|
||||||
|
|
||||||
# 保持最新的100条记录
|
|
||||||
if len(self.logs) > 100:
|
|
||||||
self.logs = self.logs[-100:]
|
|
||||||
|
|
||||||
# 保存到文件
|
|
||||||
utils_api.write_json_file(self.log_file, self.logs)
|
|
||||||
|
|
||||||
def get_logs_by_type(self, event_type: str) -> list:
|
|
||||||
"""获取指定类型的日志"""
|
|
||||||
return [log for log in self.logs if log["event_type"] == event_type]
|
|
||||||
|
|
||||||
def get_recent_logs(self, count: int = 10) -> list:
|
|
||||||
"""获取最近的日志"""
|
|
||||||
return self.logs[-count:]
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
logger = PluginLogger("my_plugin")
|
|
||||||
logger.log_event("user_action", "用户发送了消息", {"user_id": "123", "message": "hello"})
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 配置管理系统
|
|
||||||
|
|
||||||
```python
|
|
||||||
class ConfigManager:
|
|
||||||
def __init__(self, config_file: str = "plugin_config.json"):
|
|
||||||
self.config_file = config_file
|
|
||||||
self.default_config = {
|
|
||||||
"enabled": True,
|
|
||||||
"debug": False,
|
|
||||||
"max_users": 100,
|
|
||||||
"response_delay": 1.0,
|
|
||||||
"features": {
|
|
||||||
"auto_reply": True,
|
|
||||||
"logging": True
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.config = self.load_config()
|
|
||||||
|
|
||||||
def load_config(self) -> dict:
|
|
||||||
"""加载配置"""
|
|
||||||
return utils_api.read_json_file(self.config_file, self.default_config)
|
|
||||||
|
|
||||||
def save_config(self) -> bool:
|
|
||||||
"""保存配置"""
|
|
||||||
return utils_api.write_json_file(self.config_file, self.config, indent=4)
|
|
||||||
|
|
||||||
def get(self, key: str, default=None):
|
|
||||||
"""获取配置值,支持嵌套访问"""
|
|
||||||
keys = key.split('.')
|
|
||||||
value = self.config
|
|
||||||
|
|
||||||
for k in keys:
|
|
||||||
if isinstance(value, dict) and k in value:
|
|
||||||
value = value[k]
|
|
||||||
else:
|
|
||||||
return default
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def set(self, key: str, value):
|
|
||||||
"""设置配置值,支持嵌套设置"""
|
|
||||||
keys = key.split('.')
|
|
||||||
config = self.config
|
|
||||||
|
|
||||||
for k in keys[:-1]:
|
|
||||||
if k not in config:
|
|
||||||
config[k] = {}
|
|
||||||
config = config[k]
|
|
||||||
|
|
||||||
config[keys[-1]] = value
|
|
||||||
|
|
||||||
def update_config(self, updates: dict):
|
|
||||||
"""批量更新配置"""
|
|
||||||
def deep_update(base, updates):
|
|
||||||
for key, value in updates.items():
|
|
||||||
if isinstance(value, dict) and key in base and isinstance(base[key], dict):
|
|
||||||
deep_update(base[key], value)
|
|
||||||
else:
|
|
||||||
base[key] = value
|
|
||||||
|
|
||||||
deep_update(self.config, updates)
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
config = ConfigManager()
|
|
||||||
print(f"调试模式: {config.get('debug', False)}")
|
|
||||||
print(f"自动回复: {config.get('features.auto_reply', True)}")
|
|
||||||
|
|
||||||
config.set('features.new_feature', True)
|
|
||||||
config.save_config()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 缓存系统
|
|
||||||
|
|
||||||
```python
|
|
||||||
class PluginCache:
|
|
||||||
def __init__(self, cache_file: str = "plugin_cache.json", ttl: int = 3600):
|
|
||||||
self.cache_file = cache_file
|
|
||||||
self.ttl = ttl # 缓存过期时间(秒)
|
|
||||||
self.cache = self.load_cache()
|
|
||||||
|
|
||||||
def load_cache(self) -> dict:
|
|
||||||
"""加载缓存"""
|
|
||||||
return utils_api.read_json_file(self.cache_file, {})
|
|
||||||
|
|
||||||
def save_cache(self):
|
|
||||||
"""保存缓存"""
|
|
||||||
return utils_api.write_json_file(self.cache_file, self.cache)
|
|
||||||
|
|
||||||
def get(self, key: str):
|
|
||||||
"""获取缓存值"""
|
|
||||||
if key not in self.cache:
|
|
||||||
return None
|
|
||||||
|
|
||||||
item = self.cache[key]
|
|
||||||
current_time = utils_api.get_timestamp()
|
|
||||||
|
|
||||||
# 检查是否过期
|
|
||||||
if current_time - item["timestamp"] > self.ttl:
|
|
||||||
del self.cache[key]
|
|
||||||
return None
|
|
||||||
|
|
||||||
return item["value"]
|
|
||||||
|
|
||||||
def set(self, key: str, value):
|
|
||||||
"""设置缓存值"""
|
|
||||||
self.cache[key] = {
|
|
||||||
"value": value,
|
|
||||||
"timestamp": utils_api.get_timestamp()
|
|
||||||
}
|
|
||||||
self.save_cache()
|
|
||||||
|
|
||||||
def clear_expired(self):
|
|
||||||
"""清理过期缓存"""
|
|
||||||
current_time = utils_api.get_timestamp()
|
|
||||||
expired_keys = []
|
|
||||||
|
|
||||||
for key, item in self.cache.items():
|
|
||||||
if current_time - item["timestamp"] > self.ttl:
|
|
||||||
expired_keys.append(key)
|
|
||||||
|
|
||||||
for key in expired_keys:
|
|
||||||
del self.cache[key]
|
|
||||||
|
|
||||||
if expired_keys:
|
|
||||||
self.save_cache()
|
|
||||||
|
|
||||||
return len(expired_keys)
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
cache = PluginCache(ttl=1800) # 30分钟过期
|
|
||||||
cache.set("user_data_123", {"name": "张三", "score": 100})
|
|
||||||
user_data = cache.get("user_data_123")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 时间处理工具
|
|
||||||
|
|
||||||
```python
|
|
||||||
class TimeHelper:
|
|
||||||
@staticmethod
|
|
||||||
def get_time_info():
|
|
||||||
"""获取当前时间的详细信息"""
|
|
||||||
timestamp = utils_api.get_timestamp()
|
|
||||||
return {
|
|
||||||
"timestamp": timestamp,
|
|
||||||
"datetime": utils_api.format_time(timestamp),
|
|
||||||
"date": utils_api.format_time(timestamp, "%Y-%m-%d"),
|
|
||||||
"time": utils_api.format_time(timestamp, "%H:%M:%S"),
|
|
||||||
"year": utils_api.format_time(timestamp, "%Y"),
|
|
||||||
"month": utils_api.format_time(timestamp, "%m"),
|
|
||||||
"day": utils_api.format_time(timestamp, "%d"),
|
|
||||||
"weekday": utils_api.format_time(timestamp, "%A")
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def time_ago(timestamp: int) -> str:
|
|
||||||
"""计算时间差"""
|
|
||||||
current = utils_api.get_timestamp()
|
|
||||||
diff = current - timestamp
|
|
||||||
|
|
||||||
if diff < 60:
|
|
||||||
return f"{diff}秒前"
|
|
||||||
elif diff < 3600:
|
|
||||||
return f"{diff // 60}分钟前"
|
|
||||||
elif diff < 86400:
|
|
||||||
return f"{diff // 3600}小时前"
|
|
||||||
else:
|
|
||||||
return f"{diff // 86400}天前"
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse_duration(duration_str: str) -> int:
|
|
||||||
"""解析时间段字符串,返回秒数"""
|
|
||||||
import re
|
|
||||||
|
|
||||||
pattern = r'(\d+)([smhd])'
|
|
||||||
matches = re.findall(pattern, duration_str.lower())
|
|
||||||
|
|
||||||
total_seconds = 0
|
|
||||||
for value, unit in matches:
|
|
||||||
value = int(value)
|
|
||||||
if unit == 's':
|
|
||||||
total_seconds += value
|
|
||||||
elif unit == 'm':
|
|
||||||
total_seconds += value * 60
|
|
||||||
elif unit == 'h':
|
|
||||||
total_seconds += value * 3600
|
|
||||||
elif unit == 'd':
|
|
||||||
total_seconds += value * 86400
|
|
||||||
|
|
||||||
return total_seconds
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
time_info = TimeHelper.get_time_info()
|
|
||||||
print(f"当前时间: {time_info['datetime']}")
|
|
||||||
|
|
||||||
last_seen = 1699000000
|
|
||||||
print(f"最后见面: {TimeHelper.time_ago(last_seen)}")
|
|
||||||
|
|
||||||
duration = TimeHelper.parse_duration("1h30m") # 1小时30分钟 = 5400秒
|
|
||||||
```
|
|
||||||
|
|
||||||
## 最佳实践
|
|
||||||
|
|
||||||
### 1. 错误处理
|
|
||||||
```python
|
|
||||||
def safe_file_operation(file_path: str, data: dict):
|
|
||||||
"""安全的文件操作"""
|
|
||||||
try:
|
|
||||||
success = utils_api.write_json_file(file_path, data)
|
|
||||||
if not success:
|
|
||||||
logger.warning(f"文件写入失败: {file_path}")
|
|
||||||
return success
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"文件操作出错: {e}")
|
|
||||||
return False
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 路径处理
|
|
||||||
```python
|
|
||||||
import os
|
|
||||||
|
|
||||||
def get_data_path(filename: str) -> str:
|
|
||||||
"""获取数据文件的完整路径"""
|
|
||||||
plugin_path = utils_api.get_plugin_path()
|
|
||||||
data_dir = os.path.join(plugin_path, "data")
|
|
||||||
|
|
||||||
# 确保数据目录存在
|
|
||||||
os.makedirs(data_dir, exist_ok=True)
|
|
||||||
|
|
||||||
return os.path.join(data_dir, filename)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 定期清理
|
|
||||||
```python
|
|
||||||
async def cleanup_old_files():
|
|
||||||
"""清理旧文件"""
|
|
||||||
plugin_path = utils_api.get_plugin_path()
|
|
||||||
current_time = utils_api.get_timestamp()
|
|
||||||
|
|
||||||
for filename in os.listdir(plugin_path):
|
|
||||||
if filename.endswith('.tmp'):
|
|
||||||
file_path = os.path.join(plugin_path, filename)
|
|
||||||
file_time = os.path.getmtime(file_path)
|
|
||||||
|
|
||||||
# 删除超过24小时的临时文件
|
|
||||||
if current_time - file_time > 86400:
|
|
||||||
os.remove(file_path)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **相对路径**:文件路径支持相对于插件目录的路径
|
|
||||||
2. **自动创建目录**:写入文件时会自动创建必要的目录
|
|
||||||
3. **错误处理**:所有函数都有错误处理,失败时返回默认值
|
|
||||||
4. **编码格式**:文件读写使用UTF-8编码
|
|
||||||
5. **时间格式**:时间戳使用秒为单位
|
|
||||||
6. **JSON格式**:JSON文件使用可读性好的缩进格式
|
|
||||||
@@ -43,19 +43,22 @@ Command vs Action 选择指南
|
|||||||
- [LLM API](api/llm-api.md) - 大语言模型交互接口,可以使用内置LLM生成内容
|
- [LLM API](api/llm-api.md) - 大语言模型交互接口,可以使用内置LLM生成内容
|
||||||
- [✨ 回复生成器API](api/generator-api.md) - 智能回复生成接口,可以使用内置风格化生成器
|
- [✨ 回复生成器API](api/generator-api.md) - 智能回复生成接口,可以使用内置风格化生成器
|
||||||
|
|
||||||
### 表情包api
|
### 表情包API
|
||||||
- [😊 表情包API](api/emoji-api.md) - 表情包选择和管理接口
|
- [😊 表情包API](api/emoji-api.md) - 表情包选择和管理接口
|
||||||
|
|
||||||
### 关系系统api
|
### 关系系统API
|
||||||
- [人物信息API](api/person-api.md) - 用户信息,处理麦麦认识的人和关系的接口
|
- [人物信息API](api/person-api.md) - 用户信息,处理麦麦认识的人和关系的接口
|
||||||
|
|
||||||
### 数据与配置API
|
### 数据与配置API
|
||||||
- [🗄️ 数据库API](api/database-api.md) - 数据库操作接口
|
- [🗄️ 数据库API](api/database-api.md) - 数据库操作接口
|
||||||
- [⚙️ 配置API](api/config-api.md) - 配置读取和用户信息接口
|
- [⚙️ 配置API](api/config-api.md) - 配置读取和用户信息接口
|
||||||
|
|
||||||
### 工具API
|
### 插件和组件管理API
|
||||||
- [工具API](api/utils-api.md) - 文件操作、时间处理等工具函数
|
- [🔌 插件API](api/plugin-manage-api.md) - 插件加载和管理接口
|
||||||
|
- [🧩 组件API](api/component-manage-api.md) - 组件注册和管理接口
|
||||||
|
|
||||||
|
### 日志API
|
||||||
|
- [📜 日志API](api/logging-api.md) - logger实例获取接口
|
||||||
|
|
||||||
## 实验性
|
## 实验性
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import asyncio
|
|||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import random
|
import random
|
||||||
from typing import List, Optional, Dict, Any
|
from typing import List, Optional, Dict, Any, Tuple
|
||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
|
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
@@ -217,9 +217,11 @@ class HeartFChatting:
|
|||||||
filter_bot=True,
|
filter_bot=True,
|
||||||
)
|
)
|
||||||
if global_config.chat.focus_value != 0:
|
if global_config.chat.focus_value != 0:
|
||||||
if len(new_messages_data) > 3 / pow(global_config.chat.focus_value,0.5):
|
if len(new_messages_data) > 3 / pow(global_config.chat.focus_value, 0.5):
|
||||||
self.loop_mode = ChatMode.FOCUS
|
self.loop_mode = ChatMode.FOCUS
|
||||||
self.energy_value = 10 + (len(new_messages_data) / (3 / pow(global_config.chat.focus_value,0.5))) * 10
|
self.energy_value = (
|
||||||
|
10 + (len(new_messages_data) / (3 / pow(global_config.chat.focus_value, 0.5))) * 10
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self.energy_value >= 30:
|
if self.energy_value >= 30:
|
||||||
@@ -261,9 +263,10 @@ class HeartFChatting:
|
|||||||
reply_to_str,
|
reply_to_str,
|
||||||
loop_start_time,
|
loop_start_time,
|
||||||
action_message,
|
action_message,
|
||||||
cycle_timers,
|
cycle_timers: Dict[str, float],
|
||||||
thinking_id,
|
thinking_id,
|
||||||
plan_result):
|
plan_result,
|
||||||
|
) -> Tuple[Dict[str, Any], str, Dict[str, float]]:
|
||||||
with Timer("回复发送", cycle_timers):
|
with Timer("回复发送", cycle_timers):
|
||||||
reply_text = await self._send_response(response_set, reply_to_str, loop_start_time, action_message)
|
reply_text = await self._send_response(response_set, reply_to_str, loop_start_time, action_message)
|
||||||
|
|
||||||
@@ -286,9 +289,8 @@ class HeartFChatting:
|
|||||||
action_name="reply",
|
action_name="reply",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# 构建循环信息
|
# 构建循环信息
|
||||||
loop_info = {
|
loop_info: Dict[str, Any] = {
|
||||||
"loop_plan_info": {
|
"loop_plan_info": {
|
||||||
"action_result": plan_result.get("action_result", {}),
|
"action_result": plan_result.get("action_result", {}),
|
||||||
},
|
},
|
||||||
@@ -300,7 +302,7 @@ class HeartFChatting:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return loop_info, reply_text,cycle_timers
|
return loop_info, reply_text, cycle_timers
|
||||||
|
|
||||||
async def _observe(self, message_data: Optional[Dict[str, Any]] = None):
|
async def _observe(self, message_data: Optional[Dict[str, Any]] = None):
|
||||||
# sourcery skip: hoist-statement-from-if, merge-comparisons, reintroduce-else
|
# sourcery skip: hoist-statement-from-if, merge-comparisons, reintroduce-else
|
||||||
@@ -337,8 +339,9 @@ class HeartFChatting:
|
|||||||
skip_planner = False
|
skip_planner = False
|
||||||
if self.loop_mode == ChatMode.NORMAL:
|
if self.loop_mode == ChatMode.NORMAL:
|
||||||
# 过滤掉reply相关的动作,检查是否还有其他动作
|
# 过滤掉reply相关的动作,检查是否还有其他动作
|
||||||
non_reply_actions = {k: v for k, v in available_actions.items()
|
non_reply_actions = {
|
||||||
if k not in ['reply', 'no_reply', 'no_action']}
|
k: v for k, v in available_actions.items() if k not in ["reply", "no_reply", "no_action"]
|
||||||
|
}
|
||||||
|
|
||||||
if not non_reply_actions:
|
if not non_reply_actions:
|
||||||
skip_planner = True
|
skip_planner = True
|
||||||
@@ -366,17 +369,20 @@ class HeartFChatting:
|
|||||||
# 如果normal模式且不跳过规划器,开始一个回复生成进程,先准备好回复(其实是和planer同时进行的)
|
# 如果normal模式且不跳过规划器,开始一个回复生成进程,先准备好回复(其实是和planer同时进行的)
|
||||||
if not skip_planner:
|
if not skip_planner:
|
||||||
reply_to_str = await self.build_reply_to_str(message_data)
|
reply_to_str = await self.build_reply_to_str(message_data)
|
||||||
gen_task = asyncio.create_task(self._generate_response(
|
gen_task = asyncio.create_task(
|
||||||
|
self._generate_response(
|
||||||
message_data=message_data,
|
message_data=message_data,
|
||||||
available_actions=available_actions,
|
available_actions=available_actions,
|
||||||
reply_to=reply_to_str,
|
reply_to=reply_to_str,
|
||||||
request_type="chat.replyer.normal"))
|
request_type="chat.replyer.normal",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if not skip_planner:
|
if not skip_planner:
|
||||||
with Timer("规划器", cycle_timers):
|
with Timer("规划器", cycle_timers):
|
||||||
plan_result, target_message = await self.action_planner.plan(mode=self.loop_mode)
|
plan_result, target_message = await self.action_planner.plan(mode=self.loop_mode)
|
||||||
|
|
||||||
action_result: dict = plan_result.get("action_result", {}) # type: ignore
|
action_result: Dict[str, Any] = plan_result.get("action_result", {}) # type: ignore
|
||||||
action_type, action_data, reasoning, is_parallel = (
|
action_type, action_data, reasoning, is_parallel = (
|
||||||
action_result.get("action_type", "error"),
|
action_result.get("action_type", "error"),
|
||||||
action_result.get("action_data", {}),
|
action_result.get("action_data", {}),
|
||||||
@@ -386,29 +392,27 @@ class HeartFChatting:
|
|||||||
|
|
||||||
action_data["loop_start_time"] = loop_start_time
|
action_data["loop_start_time"] = loop_start_time
|
||||||
|
|
||||||
|
|
||||||
if action_type == "reply":
|
if action_type == "reply":
|
||||||
logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复")
|
logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复")
|
||||||
elif is_parallel:
|
elif is_parallel:
|
||||||
logger.info(
|
logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复, 同时执行{action_type}动作")
|
||||||
f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复, 同时执行{action_type}动作"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# 只有在gen_task存在时才进行相关操作
|
# 只有在gen_task存在时才进行相关操作
|
||||||
if gen_task is not None:
|
if gen_task:
|
||||||
if not gen_task.done():
|
if not gen_task.done():
|
||||||
gen_task.cancel()
|
gen_task.cancel()
|
||||||
logger.debug(f"{self.log_prefix} 已取消预生成的回复任务")
|
logger.debug(f"{self.log_prefix} 已取消预生成的回复任务")
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复,但选择执行{action_type},不发表回复"
|
f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复,但选择执行{action_type},不发表回复"
|
||||||
)
|
)
|
||||||
else:
|
elif generation_result := gen_task.result():
|
||||||
content = " ".join([item[1] for item in gen_task.result() if item[0] == "text"])
|
content = " ".join([item[1] for item in generation_result if item[0] == "text"])
|
||||||
logger.debug(f"{self.log_prefix} 预生成的回复任务已完成")
|
logger.debug(f"{self.log_prefix} 预生成的回复任务已完成")
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复"
|
f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复"
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
logger.warning(f"{self.log_prefix} 预生成的回复任务未生成有效内容")
|
||||||
|
|
||||||
action_message: Dict[str, Any] = message_data or target_message # type: ignore
|
action_message: Dict[str, Any] = message_data or target_message # type: ignore
|
||||||
if action_type == "reply":
|
if action_type == "reply":
|
||||||
@@ -417,11 +421,14 @@ class HeartFChatting:
|
|||||||
# 只有在gen_task存在时才等待
|
# 只有在gen_task存在时才等待
|
||||||
if not gen_task:
|
if not gen_task:
|
||||||
reply_to_str = await self.build_reply_to_str(message_data)
|
reply_to_str = await self.build_reply_to_str(message_data)
|
||||||
gen_task = asyncio.create_task(self._generate_response(
|
gen_task = asyncio.create_task(
|
||||||
|
self._generate_response(
|
||||||
message_data=message_data,
|
message_data=message_data,
|
||||||
available_actions=available_actions,
|
available_actions=available_actions,
|
||||||
reply_to=reply_to_str,
|
reply_to=reply_to_str,
|
||||||
request_type="chat.replyer.normal"))
|
request_type="chat.replyer.normal",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
gather_timeout = global_config.chat.thinking_timeout
|
gather_timeout = global_config.chat.thinking_timeout
|
||||||
try:
|
try:
|
||||||
@@ -446,30 +453,37 @@ class HeartFChatting:
|
|||||||
message_data=action_message,
|
message_data=action_message,
|
||||||
available_actions=available_actions,
|
available_actions=available_actions,
|
||||||
reply_to=reply_to_str,
|
reply_to=reply_to_str,
|
||||||
request_type="chat.replyer.focus")
|
request_type="chat.replyer.focus",
|
||||||
|
)
|
||||||
|
|
||||||
if not response_set:
|
if not response_set:
|
||||||
logger.warning(f"{self.log_prefix}模型未生成回复内容")
|
logger.warning(f"{self.log_prefix}模型未生成回复内容")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
loop_info, reply_text,cycle_timers = await self._send_and_store_reply(response_set, reply_to_str, loop_start_time, action_message, cycle_timers, thinking_id, plan_result)
|
loop_info, reply_text, cycle_timers = await self._send_and_store_reply(
|
||||||
|
response_set, reply_to_str, loop_start_time, action_message, cycle_timers, thinking_id, plan_result
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 并行执行:同时进行回复发送和动作执行
|
# 并行执行:同时进行回复发送和动作执行
|
||||||
tasks = []
|
# 先置空防止未定义错误
|
||||||
|
background_reply_task = None
|
||||||
|
background_action_task = None
|
||||||
# 如果是并行执行且在normal模式下,需要等待预生成的回复任务完成并发送回复
|
# 如果是并行执行且在normal模式下,需要等待预生成的回复任务完成并发送回复
|
||||||
if self.loop_mode == ChatMode.NORMAL and is_parallel and gen_task:
|
if self.loop_mode == ChatMode.NORMAL and is_parallel and gen_task:
|
||||||
async def handle_reply_task():
|
|
||||||
|
async def handle_reply_task() -> Tuple[Optional[Dict[str, Any]], str, Dict[str, float]]:
|
||||||
# 等待预生成的回复任务完成
|
# 等待预生成的回复任务完成
|
||||||
gather_timeout = global_config.chat.thinking_timeout
|
gather_timeout = global_config.chat.thinking_timeout
|
||||||
try:
|
try:
|
||||||
response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout)
|
response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout)
|
||||||
|
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
logger.warning(f"{self.log_prefix} 并行执行:回复生成超时>{global_config.chat.thinking_timeout}s,已跳过")
|
logger.warning(
|
||||||
|
f"{self.log_prefix} 并行执行:回复生成超时>{global_config.chat.thinking_timeout}s,已跳过"
|
||||||
|
)
|
||||||
return None, "", {}
|
return None, "", {}
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消")
|
logger.debug(f"{self.log_prefix} 并行执行:回复生成任务已被取消")
|
||||||
@@ -480,11 +494,19 @@ class HeartFChatting:
|
|||||||
return None, "", {}
|
return None, "", {}
|
||||||
|
|
||||||
reply_to_str = await self.build_reply_to_str(action_message)
|
reply_to_str = await self.build_reply_to_str(action_message)
|
||||||
loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply(response_set, reply_to_str, loop_start_time, action_message, cycle_timers, thinking_id, plan_result)
|
loop_info, reply_text, cycle_timers_reply = await self._send_and_store_reply(
|
||||||
|
response_set,
|
||||||
|
reply_to_str,
|
||||||
|
loop_start_time,
|
||||||
|
action_message,
|
||||||
|
cycle_timers,
|
||||||
|
thinking_id,
|
||||||
|
plan_result,
|
||||||
|
)
|
||||||
return loop_info, reply_text, cycle_timers_reply
|
return loop_info, reply_text, cycle_timers_reply
|
||||||
|
|
||||||
# 添加回复任务到并行任务列表
|
# 执行回复任务并赋值到变量
|
||||||
tasks.append(asyncio.create_task(handle_reply_task()))
|
background_reply_task = asyncio.create_task(handle_reply_task())
|
||||||
|
|
||||||
# 动作执行任务
|
# 动作执行任务
|
||||||
async def handle_action_task():
|
async def handle_action_task():
|
||||||
@@ -494,51 +516,54 @@ class HeartFChatting:
|
|||||||
)
|
)
|
||||||
return success, reply_text, command
|
return success, reply_text, command
|
||||||
|
|
||||||
# 添加动作执行任务到并行任务列表
|
# 执行动作任务并赋值到变量
|
||||||
tasks.append(asyncio.create_task(handle_action_task()))
|
background_action_task = asyncio.create_task(handle_action_task())
|
||||||
|
|
||||||
# 并行执行所有任务
|
|
||||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
||||||
|
|
||||||
# 处理结果
|
|
||||||
reply_loop_info = None
|
reply_loop_info = None
|
||||||
reply_text_from_reply = ""
|
reply_text_from_reply = ""
|
||||||
action_success = False
|
action_success = False
|
||||||
action_reply_text = ""
|
action_reply_text = ""
|
||||||
action_command = ""
|
action_command = ""
|
||||||
|
|
||||||
if len(tasks) == 2: # 有回复任务和动作任务
|
# 并行执行所有任务
|
||||||
|
if background_reply_task:
|
||||||
|
results = await asyncio.gather(
|
||||||
|
background_reply_task, background_action_task, return_exceptions=True
|
||||||
|
)
|
||||||
# 处理回复任务结果
|
# 处理回复任务结果
|
||||||
reply_result = results[0]
|
reply_result = results[0]
|
||||||
if isinstance(reply_result, Exception):
|
if isinstance(reply_result, BaseException):
|
||||||
logger.error(f"{self.log_prefix} 回复任务执行异常: {reply_result}")
|
logger.error(f"{self.log_prefix} 回复任务执行异常: {reply_result}")
|
||||||
elif reply_result and reply_result[0] is not None:
|
elif reply_result and reply_result[0] is not None:
|
||||||
reply_loop_info, reply_text_from_reply, _ = reply_result
|
reply_loop_info, reply_text_from_reply, _ = reply_result
|
||||||
|
|
||||||
# 处理动作任务结果
|
# 处理动作任务结果
|
||||||
action_result = results[1]
|
action_task_result = results[1]
|
||||||
if isinstance(action_result, Exception):
|
if isinstance(action_task_result, BaseException):
|
||||||
logger.error(f"{self.log_prefix} 动作任务执行异常: {action_result}")
|
logger.error(f"{self.log_prefix} 动作任务执行异常: {action_task_result}")
|
||||||
else:
|
else:
|
||||||
action_success, action_reply_text, action_command = action_result
|
action_success, action_reply_text, action_command = action_task_result
|
||||||
|
|
||||||
else: # 只有动作任务
|
|
||||||
action_result = results[0]
|
|
||||||
if isinstance(action_result, Exception):
|
|
||||||
logger.error(f"{self.log_prefix} 动作任务执行异常: {action_result}")
|
|
||||||
else:
|
else:
|
||||||
action_success, action_reply_text, action_command = action_result
|
results = await asyncio.gather(background_action_task, return_exceptions=True)
|
||||||
|
# 只有动作任务
|
||||||
|
action_task_result = results[0]
|
||||||
|
if isinstance(action_task_result, BaseException):
|
||||||
|
logger.error(f"{self.log_prefix} 动作任务执行异常: {action_task_result}")
|
||||||
|
else:
|
||||||
|
action_success, action_reply_text, action_command = action_task_result
|
||||||
|
|
||||||
# 构建最终的循环信息
|
# 构建最终的循环信息
|
||||||
if reply_loop_info:
|
if reply_loop_info:
|
||||||
# 如果有回复信息,使用回复的loop_info作为基础
|
# 如果有回复信息,使用回复的loop_info作为基础
|
||||||
loop_info = reply_loop_info
|
loop_info = reply_loop_info
|
||||||
# 更新动作执行信息
|
# 更新动作执行信息
|
||||||
loop_info["loop_action_info"].update({
|
loop_info["loop_action_info"].update(
|
||||||
|
{
|
||||||
"action_taken": action_success,
|
"action_taken": action_success,
|
||||||
"command": action_command,
|
"command": action_command,
|
||||||
"taken_time": time.time(),
|
"taken_time": time.time(),
|
||||||
})
|
}
|
||||||
|
)
|
||||||
reply_text = reply_text_from_reply
|
reply_text = reply_text_from_reply
|
||||||
else:
|
else:
|
||||||
# 没有回复信息,构建纯动作的loop_info
|
# 没有回复信息,构建纯动作的loop_info
|
||||||
@@ -555,7 +580,6 @@ class HeartFChatting:
|
|||||||
}
|
}
|
||||||
reply_text = action_reply_text
|
reply_text = action_reply_text
|
||||||
|
|
||||||
|
|
||||||
if ENABLE_S4U:
|
if ENABLE_S4U:
|
||||||
await stop_typing()
|
await stop_typing()
|
||||||
await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text)
|
await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text)
|
||||||
@@ -603,7 +627,7 @@ class HeartFChatting:
|
|||||||
action: str,
|
action: str,
|
||||||
reasoning: str,
|
reasoning: str,
|
||||||
action_data: dict,
|
action_data: dict,
|
||||||
cycle_timers: dict,
|
cycle_timers: Dict[str, float],
|
||||||
thinking_id: str,
|
thinking_id: str,
|
||||||
action_message: dict,
|
action_message: dict,
|
||||||
) -> tuple[bool, str, str]:
|
) -> tuple[bool, str, str]:
|
||||||
@@ -712,7 +736,11 @@ class HeartFChatting:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
async def _generate_response(
|
async def _generate_response(
|
||||||
self, message_data: dict, available_actions: Optional[Dict[str, ActionInfo]], reply_to: str, request_type: str = "chat.replyer.normal"
|
self,
|
||||||
|
message_data: dict,
|
||||||
|
available_actions: Optional[Dict[str, ActionInfo]],
|
||||||
|
reply_to: str,
|
||||||
|
request_type: str = "chat.replyer.normal",
|
||||||
) -> Optional[list]:
|
) -> Optional[list]:
|
||||||
"""生成普通回复"""
|
"""生成普通回复"""
|
||||||
try:
|
try:
|
||||||
@@ -734,7 +762,7 @@ class HeartFChatting:
|
|||||||
logger.error(f"{self.log_prefix}回复生成出现错误:{str(e)} {traceback.format_exc()}")
|
logger.error(f"{self.log_prefix}回复生成出现错误:{str(e)} {traceback.format_exc()}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _send_response(self, reply_set, reply_to, thinking_start_time, message_data):
|
async def _send_response(self, reply_set, reply_to, thinking_start_time, message_data) -> str:
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
new_message_count = message_api.count_new_messages(
|
new_message_count = message_api.count_new_messages(
|
||||||
chat_id=self.chat_stream.stream_id, start_time=thinking_start_time, end_time=current_time
|
chat_id=self.chat_stream.stream_id, start_time=thinking_start_time, end_time=current_time
|
||||||
@@ -746,13 +774,9 @@ class HeartFChatting:
|
|||||||
need_reply = new_message_count >= random.randint(2, 4)
|
need_reply = new_message_count >= random.randint(2, 4)
|
||||||
|
|
||||||
if need_reply:
|
if need_reply:
|
||||||
logger.info(
|
logger.info(f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,使用引用回复")
|
||||||
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,使用引用回复"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,不使用引用回复")
|
||||||
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,不使用引用回复"
|
|
||||||
)
|
|
||||||
|
|
||||||
reply_text = ""
|
reply_text = ""
|
||||||
first_replied = False
|
first_replied = False
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ class MessageSending(MessageProcessBase):
|
|||||||
is_emoji: bool = False,
|
is_emoji: bool = False,
|
||||||
thinking_start_time: float = 0,
|
thinking_start_time: float = 0,
|
||||||
apply_set_reply_logic: bool = False,
|
apply_set_reply_logic: bool = False,
|
||||||
reply_to: str = None, # type: ignore
|
reply_to: Optional[str] = None,
|
||||||
):
|
):
|
||||||
# 调用父类初始化
|
# 调用父类初始化
|
||||||
super().__init__(
|
super().__init__(
|
||||||
|
|||||||
@@ -211,8 +211,7 @@ class ActionPlanner:
|
|||||||
reasoning = f"Planner 内部处理错误: {outer_e}"
|
reasoning = f"Planner 内部处理错误: {outer_e}"
|
||||||
|
|
||||||
is_parallel = False
|
is_parallel = False
|
||||||
if mode == ChatMode.NORMAL:
|
if mode == ChatMode.NORMAL and action in current_available_actions:
|
||||||
if action in current_available_actions:
|
|
||||||
is_parallel = current_available_actions[action].parallel_action
|
is_parallel = current_available_actions[action].parallel_action
|
||||||
|
|
||||||
action_result = {
|
action_result = {
|
||||||
@@ -256,7 +255,7 @@ class ActionPlanner:
|
|||||||
|
|
||||||
actions_before_now = get_actions_by_timestamp_with_chat(
|
actions_before_now = get_actions_by_timestamp_with_chat(
|
||||||
chat_id=self.chat_id,
|
chat_id=self.chat_id,
|
||||||
timestamp_start=time.time()-3600,
|
timestamp_start=time.time() - 3600,
|
||||||
timestamp_end=time.time(),
|
timestamp_end=time.time(),
|
||||||
limit=5,
|
limit=5,
|
||||||
)
|
)
|
||||||
@@ -276,7 +275,6 @@ class ActionPlanner:
|
|||||||
if global_config.chat.at_bot_inevitable_reply:
|
if global_config.chat.at_bot_inevitable_reply:
|
||||||
mentioned_bonus = "\n- 有人提到你,或者at你"
|
mentioned_bonus = "\n- 有人提到你,或者at你"
|
||||||
|
|
||||||
|
|
||||||
by_what = "聊天内容"
|
by_what = "聊天内容"
|
||||||
target_prompt = '\n "target_message_id":"触发action的消息id"'
|
target_prompt = '\n "target_message_id":"触发action的消息id"'
|
||||||
no_action_block = f"""重要说明:
|
no_action_block = f"""重要说明:
|
||||||
|
|||||||
@@ -156,10 +156,18 @@ class DefaultReplyer:
|
|||||||
extra_info: str = "",
|
extra_info: str = "",
|
||||||
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
||||||
enable_tool: bool = True,
|
enable_tool: bool = True,
|
||||||
enable_timeout: bool = False,
|
|
||||||
) -> Tuple[bool, Optional[str], Optional[str]]:
|
) -> Tuple[bool, Optional[str], Optional[str]]:
|
||||||
"""
|
"""
|
||||||
回复器 (Replier): 核心逻辑,负责生成回复文本。
|
回复器 (Replier): 负责生成回复文本的核心逻辑。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
reply_to: 回复对象,格式为 "发送者:消息内容"
|
||||||
|
extra_info: 额外信息,用于补充上下文
|
||||||
|
available_actions: 可用的动作信息字典
|
||||||
|
enable_tool: 是否启用工具调用
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[bool, Optional[str], Optional[str]]: (是否成功, 生成的回复内容, 使用的prompt)
|
||||||
"""
|
"""
|
||||||
prompt = None
|
prompt = None
|
||||||
if available_actions is None:
|
if available_actions is None:
|
||||||
@@ -168,10 +176,9 @@ class DefaultReplyer:
|
|||||||
# 3. 构建 Prompt
|
# 3. 构建 Prompt
|
||||||
with Timer("构建Prompt", {}): # 内部计时器,可选保留
|
with Timer("构建Prompt", {}): # 内部计时器,可选保留
|
||||||
prompt = await self.build_prompt_reply_context(
|
prompt = await self.build_prompt_reply_context(
|
||||||
reply_to = reply_to,
|
reply_to=reply_to,
|
||||||
extra_info=extra_info,
|
extra_info=extra_info,
|
||||||
available_actions=available_actions,
|
available_actions=available_actions,
|
||||||
enable_timeout=enable_timeout,
|
|
||||||
enable_tool=enable_tool,
|
enable_tool=enable_tool,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -181,29 +188,12 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
# 4. 调用 LLM 生成回复
|
# 4. 调用 LLM 生成回复
|
||||||
content = None
|
content = None
|
||||||
reasoning_content = None
|
# TODO: 复活这里
|
||||||
model_name = "unknown_model"
|
# reasoning_content = None
|
||||||
|
# model_name = "unknown_model"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with Timer("LLM生成", {}): # 内部计时器,可选保留
|
content = await self.llm_generate_content(prompt)
|
||||||
# 加权随机选择一个模型配置
|
|
||||||
selected_model_config = self._select_weighted_model_config()
|
|
||||||
logger.info(
|
|
||||||
f"使用模型生成回复: {selected_model_config.get('name', 'N/A')} (选中概率: {selected_model_config.get('weight', 1.0)})"
|
|
||||||
)
|
|
||||||
|
|
||||||
express_model = LLMRequest(
|
|
||||||
model=selected_model_config,
|
|
||||||
request_type=self.request_type,
|
|
||||||
)
|
|
||||||
|
|
||||||
if global_config.debug.show_prompt:
|
|
||||||
logger.info(f"\n{prompt}\n")
|
|
||||||
else:
|
|
||||||
logger.debug(f"\n{prompt}\n")
|
|
||||||
|
|
||||||
content, (reasoning_content, model_name) = await express_model.generate_response_async(prompt)
|
|
||||||
|
|
||||||
logger.debug(f"replyer生成内容: {content}")
|
logger.debug(f"replyer生成内容: {content}")
|
||||||
|
|
||||||
except Exception as llm_e:
|
except Exception as llm_e:
|
||||||
@@ -220,62 +210,54 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
async def rewrite_reply_with_context(
|
async def rewrite_reply_with_context(
|
||||||
self,
|
self,
|
||||||
reply_data: Dict[str, Any],
|
|
||||||
raw_reply: str = "",
|
raw_reply: str = "",
|
||||||
reason: str = "",
|
reason: str = "",
|
||||||
reply_to: str = "",
|
reply_to: str = "",
|
||||||
relation_info: str = "",
|
return_prompt: bool = False,
|
||||||
) -> Tuple[bool, Optional[str]]:
|
) -> Tuple[bool, Optional[str], Optional[str]]:
|
||||||
"""
|
"""
|
||||||
表达器 (Expressor): 核心逻辑,负责生成回复文本。
|
表达器 (Expressor): 负责重写和优化回复文本。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
raw_reply: 原始回复内容
|
||||||
|
reason: 回复原因
|
||||||
|
reply_to: 回复对象,格式为 "发送者:消息内容"
|
||||||
|
relation_info: 关系信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[bool, Optional[str]]: (是否成功, 重写后的回复内容)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not reply_data:
|
|
||||||
reply_data = {
|
|
||||||
"reply_to": reply_to,
|
|
||||||
"relation_info": relation_info,
|
|
||||||
}
|
|
||||||
|
|
||||||
with Timer("构建Prompt", {}): # 内部计时器,可选保留
|
with Timer("构建Prompt", {}): # 内部计时器,可选保留
|
||||||
prompt = await self.build_prompt_rewrite_context(
|
prompt = await self.build_prompt_rewrite_context(
|
||||||
reply_data=reply_data,
|
raw_reply=raw_reply,
|
||||||
|
reason=reason,
|
||||||
|
reply_to=reply_to,
|
||||||
)
|
)
|
||||||
|
|
||||||
content = None
|
content = None
|
||||||
reasoning_content = None
|
# TODO: 复活这里
|
||||||
model_name = "unknown_model"
|
# reasoning_content = None
|
||||||
|
# model_name = "unknown_model"
|
||||||
if not prompt:
|
if not prompt:
|
||||||
logger.error("Prompt 构建失败,无法生成回复。")
|
logger.error("Prompt 构建失败,无法生成回复。")
|
||||||
return False, None
|
return False, None, None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with Timer("LLM生成", {}): # 内部计时器,可选保留
|
content = await self.llm_generate_content(prompt)
|
||||||
# 加权随机选择一个模型配置
|
|
||||||
selected_model_config = self._select_weighted_model_config()
|
|
||||||
logger.info(
|
|
||||||
f"使用模型重写回复: {selected_model_config.get('name', 'N/A')} (选中概率: {selected_model_config.get('weight', 1.0)})"
|
|
||||||
)
|
|
||||||
|
|
||||||
express_model = LLMRequest(
|
|
||||||
model=selected_model_config,
|
|
||||||
request_type=self.request_type,
|
|
||||||
)
|
|
||||||
|
|
||||||
content, (reasoning_content, model_name) = await express_model.generate_response_async(prompt)
|
|
||||||
|
|
||||||
logger.info(f"想要表达:{raw_reply}||理由:{reason}||生成回复: {content}\n")
|
logger.info(f"想要表达:{raw_reply}||理由:{reason}||生成回复: {content}\n")
|
||||||
|
|
||||||
except Exception as llm_e:
|
except Exception as llm_e:
|
||||||
# 精简报错信息
|
# 精简报错信息
|
||||||
logger.error(f"LLM 生成失败: {llm_e}")
|
logger.error(f"LLM 生成失败: {llm_e}")
|
||||||
return False, None # LLM 调用失败则无法生成回复
|
return False, None, prompt if return_prompt else None # LLM 调用失败则无法生成回复
|
||||||
|
|
||||||
return True, content
|
return True, content, prompt if return_prompt else None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"回复生成意外失败: {e}")
|
logger.error(f"回复生成意外失败: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False, None
|
return False, None, prompt if return_prompt else None
|
||||||
|
|
||||||
async def build_relation_info(self, reply_to: str = ""):
|
async def build_relation_info(self, reply_to: str = ""):
|
||||||
if not global_config.relationship.enable_relationship:
|
if not global_config.relationship.enable_relationship:
|
||||||
@@ -297,7 +279,16 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
return await relationship_fetcher.build_relation_info(person_id, points_num=5)
|
return await relationship_fetcher.build_relation_info(person_id, points_num=5)
|
||||||
|
|
||||||
async def build_expression_habits(self, chat_history, target):
|
async def build_expression_habits(self, chat_history: str, target: str) -> str:
|
||||||
|
"""构建表达习惯块
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chat_history: 聊天历史记录
|
||||||
|
target: 目标消息内容
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 表达习惯信息字符串
|
||||||
|
"""
|
||||||
if not global_config.expression.enable_expression:
|
if not global_config.expression.enable_expression:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@@ -343,11 +334,18 @@ class DefaultReplyer:
|
|||||||
if style_habits_str.strip() and grammar_habits_str.strip():
|
if style_habits_str.strip() and grammar_habits_str.strip():
|
||||||
expression_habits_title = "你可以参考以下的语言习惯和句法,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式结合到你的回复中:"
|
expression_habits_title = "你可以参考以下的语言习惯和句法,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式结合到你的回复中:"
|
||||||
|
|
||||||
expression_habits_block = f"{expression_habits_title}\n{expression_habits_block}"
|
return f"{expression_habits_title}\n{expression_habits_block}"
|
||||||
|
|
||||||
return expression_habits_block
|
async def build_memory_block(self, chat_history: str, target: str) -> str:
|
||||||
|
"""构建记忆块
|
||||||
|
|
||||||
async def build_memory_block(self, chat_history, target):
|
Args:
|
||||||
|
chat_history: 聊天历史记录
|
||||||
|
target: 目标消息内容
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 记忆信息字符串
|
||||||
|
"""
|
||||||
if not global_config.memory.enable_memory:
|
if not global_config.memory.enable_memory:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@@ -375,12 +373,13 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
return memory_str
|
return memory_str
|
||||||
|
|
||||||
async def build_tool_info(self, chat_history, reply_to: str = "", enable_tool: bool = True):
|
async def build_tool_info(self, chat_history: str, reply_to: str = "", enable_tool: bool = True) -> str:
|
||||||
"""构建工具信息块
|
"""构建工具信息块
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
reply_data: 回复数据,包含要回复的消息内容
|
chat_history: 聊天历史记录
|
||||||
chat_history: 聊天历史
|
reply_to: 回复对象,格式为 "发送者:消息内容"
|
||||||
|
enable_tool: 是否启用工具调用
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: 工具信息字符串
|
str: 工具信息字符串
|
||||||
@@ -424,7 +423,15 @@ class DefaultReplyer:
|
|||||||
logger.error(f"工具信息获取失败: {e}")
|
logger.error(f"工具信息获取失败: {e}")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _parse_reply_target(self, target_message: str) -> tuple:
|
def _parse_reply_target(self, target_message: str) -> Tuple[str, str]:
|
||||||
|
"""解析回复目标消息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
target_message: 目标消息,格式为 "发送者:消息内容" 或 "发送者:消息内容"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[str, str]: (发送者名称, 消息内容)
|
||||||
|
"""
|
||||||
sender = ""
|
sender = ""
|
||||||
target = ""
|
target = ""
|
||||||
# 添加None检查,防止NoneType错误
|
# 添加None检查,防止NoneType错误
|
||||||
@@ -438,7 +445,15 @@ class DefaultReplyer:
|
|||||||
target = parts[1].strip()
|
target = parts[1].strip()
|
||||||
return sender, target
|
return sender, target
|
||||||
|
|
||||||
async def build_keywords_reaction_prompt(self, target):
|
async def build_keywords_reaction_prompt(self, target: Optional[str]) -> str:
|
||||||
|
"""构建关键词反应提示
|
||||||
|
|
||||||
|
Args:
|
||||||
|
target: 目标消息内容
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 关键词反应提示字符串
|
||||||
|
"""
|
||||||
# 关键词检测与反应
|
# 关键词检测与反应
|
||||||
keywords_reaction_prompt = ""
|
keywords_reaction_prompt = ""
|
||||||
try:
|
try:
|
||||||
@@ -472,15 +487,25 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
return keywords_reaction_prompt
|
return keywords_reaction_prompt
|
||||||
|
|
||||||
async def _time_and_run_task(self, coroutine, name: str):
|
async def _time_and_run_task(self, coroutine, name: str) -> Tuple[str, Any, float]:
|
||||||
"""一个简单的帮助函数,用于计时和运行异步任务,返回任务名、结果和耗时"""
|
"""计时并运行异步任务的辅助函数
|
||||||
|
|
||||||
|
Args:
|
||||||
|
coroutine: 要执行的协程
|
||||||
|
name: 任务名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[str, Any, float]: (任务名称, 任务结果, 执行耗时)
|
||||||
|
"""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
result = await coroutine
|
result = await coroutine
|
||||||
end_time = time.time()
|
end_time = time.time()
|
||||||
duration = end_time - start_time
|
duration = end_time - start_time
|
||||||
return name, result, duration
|
return name, result, duration
|
||||||
|
|
||||||
def build_s4u_chat_history_prompts(self, message_list_before_now: list, target_user_id: str) -> tuple[str, str]:
|
def build_s4u_chat_history_prompts(
|
||||||
|
self, message_list_before_now: List[Dict[str, Any]], target_user_id: str
|
||||||
|
) -> Tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
构建 s4u 风格的分离对话 prompt
|
构建 s4u 风格的分离对话 prompt
|
||||||
|
|
||||||
@@ -489,7 +514,7 @@ class DefaultReplyer:
|
|||||||
target_user_id: 目标用户ID(当前对话对象)
|
target_user_id: 目标用户ID(当前对话对象)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple: (核心对话prompt, 背景对话prompt)
|
Tuple[str, str]: (核心对话prompt, 背景对话prompt)
|
||||||
"""
|
"""
|
||||||
core_dialogue_list = []
|
core_dialogue_list = []
|
||||||
background_dialogue_list = []
|
background_dialogue_list = []
|
||||||
@@ -508,7 +533,7 @@ class DefaultReplyer:
|
|||||||
# 其他用户的对话
|
# 其他用户的对话
|
||||||
background_dialogue_list.append(msg_dict)
|
background_dialogue_list.append(msg_dict)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"记录: {msg_dict}, 错误: {e}")
|
logger.error(f"处理消息记录时出错: {msg_dict}, 错误: {e}")
|
||||||
|
|
||||||
# 构建背景对话 prompt
|
# 构建背景对话 prompt
|
||||||
background_dialogue_prompt = ""
|
background_dialogue_prompt = ""
|
||||||
@@ -553,8 +578,25 @@ class DefaultReplyer:
|
|||||||
sender: str,
|
sender: str,
|
||||||
target: str,
|
target: str,
|
||||||
chat_info: str,
|
chat_info: str,
|
||||||
):
|
) -> Any:
|
||||||
"""构建 mai_think 上下文信息"""
|
"""构建 mai_think 上下文信息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chat_id: 聊天ID
|
||||||
|
memory_block: 记忆块内容
|
||||||
|
relation_info: 关系信息
|
||||||
|
time_block: 时间块内容
|
||||||
|
chat_target_1: 聊天目标1
|
||||||
|
chat_target_2: 聊天目标2
|
||||||
|
mood_prompt: 情绪提示
|
||||||
|
identity_block: 身份块内容
|
||||||
|
sender: 发送者名称
|
||||||
|
target: 目标消息内容
|
||||||
|
chat_info: 聊天信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Any: mai_think 实例
|
||||||
|
"""
|
||||||
mai_think = mai_thinking_manager.get_mai_think(chat_id)
|
mai_think = mai_thinking_manager.get_mai_think(chat_id)
|
||||||
mai_think.memory_block = memory_block
|
mai_think.memory_block = memory_block
|
||||||
mai_think.relation_info_block = relation_info
|
mai_think.relation_info_block = relation_info
|
||||||
@@ -573,19 +615,17 @@ class DefaultReplyer:
|
|||||||
reply_to: str,
|
reply_to: str,
|
||||||
extra_info: str = "",
|
extra_info: str = "",
|
||||||
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
||||||
enable_timeout: bool = False,
|
|
||||||
enable_tool: bool = True,
|
enable_tool: bool = True,
|
||||||
) -> str: # sourcery skip: merge-else-if-into-elif, remove-redundant-if
|
) -> str: # sourcery skip: merge-else-if-into-elif, remove-redundant-if
|
||||||
"""
|
"""
|
||||||
构建回复器上下文
|
构建回复器上下文
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
reply_data: 回复数据
|
reply_to: 回复对象,格式为 "发送者:消息内容"
|
||||||
replay_data 包含以下字段:
|
extra_info: 额外信息,用于补充上下文
|
||||||
structured_info: 结构化信息,一般是工具调用获得的信息
|
|
||||||
reply_to: 回复对象
|
|
||||||
extra_info/extra_info_block: 额外信息
|
|
||||||
available_actions: 可用动作
|
available_actions: 可用动作
|
||||||
|
enable_timeout: 是否启用超时处理
|
||||||
|
enable_tool: 是否启用工具调用
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: 构建好的上下文
|
str: 构建好的上下文
|
||||||
@@ -800,15 +840,14 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
async def build_prompt_rewrite_context(
|
async def build_prompt_rewrite_context(
|
||||||
self,
|
self,
|
||||||
reply_data: Dict[str, Any],
|
raw_reply: str,
|
||||||
|
reason: str,
|
||||||
|
reply_to: str,
|
||||||
) -> str:
|
) -> str:
|
||||||
chat_stream = self.chat_stream
|
chat_stream = self.chat_stream
|
||||||
chat_id = chat_stream.stream_id
|
chat_id = chat_stream.stream_id
|
||||||
is_group_chat = bool(chat_stream.group_info)
|
is_group_chat = bool(chat_stream.group_info)
|
||||||
|
|
||||||
reply_to = reply_data.get("reply_to", "none")
|
|
||||||
raw_reply = reply_data.get("raw_reply", "")
|
|
||||||
reason = reply_data.get("reason", "")
|
|
||||||
sender, target = self._parse_reply_target(reply_to)
|
sender, target = self._parse_reply_target(reply_to)
|
||||||
|
|
||||||
# 添加情绪状态获取
|
# 添加情绪状态获取
|
||||||
@@ -835,7 +874,7 @@ class DefaultReplyer:
|
|||||||
# 并行执行2个构建任务
|
# 并行执行2个构建任务
|
||||||
expression_habits_block, relation_info = await asyncio.gather(
|
expression_habits_block, relation_info = await asyncio.gather(
|
||||||
self.build_expression_habits(chat_talking_prompt_half, target),
|
self.build_expression_habits(chat_talking_prompt_half, target),
|
||||||
self.build_relation_info(reply_data),
|
self.build_relation_info(reply_to),
|
||||||
)
|
)
|
||||||
|
|
||||||
keywords_reaction_prompt = await self.build_keywords_reaction_prompt(target)
|
keywords_reaction_prompt = await self.build_keywords_reaction_prompt(target)
|
||||||
@@ -938,6 +977,30 @@ class DefaultReplyer:
|
|||||||
display_message=display_message,
|
display_message=display_message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def llm_generate_content(self, prompt: str) -> str:
|
||||||
|
with Timer("LLM生成", {}): # 内部计时器,可选保留
|
||||||
|
# 加权随机选择一个模型配置
|
||||||
|
selected_model_config = self._select_weighted_model_config()
|
||||||
|
logger.info(
|
||||||
|
f"使用模型生成回复: {selected_model_config.get('name', 'N/A')} (选中概率: {selected_model_config.get('weight', 1.0)})"
|
||||||
|
)
|
||||||
|
|
||||||
|
express_model = LLMRequest(
|
||||||
|
model=selected_model_config,
|
||||||
|
request_type=self.request_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
if global_config.debug.show_prompt:
|
||||||
|
logger.info(f"\n{prompt}\n")
|
||||||
|
else:
|
||||||
|
logger.debug(f"\n{prompt}\n")
|
||||||
|
|
||||||
|
# TODO: 这里的_应该做出替换
|
||||||
|
content, _ = await express_model.generate_response_async(prompt)
|
||||||
|
|
||||||
|
logger.debug(f"replyer生成内容: {content}")
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
def weighted_sample_no_replacement(items, weights, k) -> list:
|
def weighted_sample_no_replacement(items, weights, k) -> list:
|
||||||
"""
|
"""
|
||||||
@@ -996,9 +1059,7 @@ async def get_prompt_info(message: str, threshold: float):
|
|||||||
logger.debug(f"获取知识库内容耗时: {(end_time - start_time):.3f}秒")
|
logger.debug(f"获取知识库内容耗时: {(end_time - start_time):.3f}秒")
|
||||||
logger.debug(f"获取知识库内容,相关信息:{related_info[:100]}...,信息长度: {len(related_info)}")
|
logger.debug(f"获取知识库内容,相关信息:{related_info[:100]}...,信息长度: {len(related_info)}")
|
||||||
|
|
||||||
# 格式化知识信息
|
return f"你有以下这些**知识**:\n{related_info}\n请你**记住上面的知识**,之后可能会用到。\n"
|
||||||
formatted_prompt_info = f"你有以下这些**知识**:\n{related_info}\n请你**记住上面的知识**,之后可能会用到。\n"
|
|
||||||
return formatted_prompt_info
|
|
||||||
else:
|
else:
|
||||||
logger.debug("从LPMM知识库获取知识失败,可能是从未导入过知识,返回空知识...")
|
logger.debug("从LPMM知识库获取知识失败,可能是从未导入过知识,返回空知识...")
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -851,7 +851,7 @@ class LLMRequest:
|
|||||||
|
|
||||||
def _default_response_handler(
|
def _default_response_handler(
|
||||||
self, result: dict, user_id: str = "system", request_type: str = None, endpoint: str = "/chat/completions"
|
self, result: dict, user_id: str = "system", request_type: str = None, endpoint: str = "/chat/completions"
|
||||||
) -> Tuple:
|
):
|
||||||
"""默认响应解析"""
|
"""默认响应解析"""
|
||||||
if "choices" in result and result["choices"]:
|
if "choices" in result and result["choices"]:
|
||||||
message = result["choices"][0]["message"]
|
message = result["choices"][0]["message"]
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ from .apis import (
|
|||||||
person_api,
|
person_api,
|
||||||
plugin_manage_api,
|
plugin_manage_api,
|
||||||
send_api,
|
send_api,
|
||||||
utils_api,
|
|
||||||
register_plugin,
|
register_plugin,
|
||||||
get_logger,
|
get_logger,
|
||||||
)
|
)
|
||||||
@@ -68,7 +67,6 @@ __all__ = [
|
|||||||
"person_api",
|
"person_api",
|
||||||
"plugin_manage_api",
|
"plugin_manage_api",
|
||||||
"send_api",
|
"send_api",
|
||||||
"utils_api",
|
|
||||||
"register_plugin",
|
"register_plugin",
|
||||||
"get_logger",
|
"get_logger",
|
||||||
# 基础类
|
# 基础类
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ from src.plugin_system.apis import (
|
|||||||
person_api,
|
person_api,
|
||||||
plugin_manage_api,
|
plugin_manage_api,
|
||||||
send_api,
|
send_api,
|
||||||
utils_api,
|
|
||||||
)
|
)
|
||||||
from .logging_api import get_logger
|
from .logging_api import get_logger
|
||||||
from .plugin_register_api import register_plugin
|
from .plugin_register_api import register_plugin
|
||||||
@@ -35,7 +34,6 @@ __all__ = [
|
|||||||
"person_api",
|
"person_api",
|
||||||
"plugin_manage_api",
|
"plugin_manage_api",
|
||||||
"send_api",
|
"send_api",
|
||||||
"utils_api",
|
|
||||||
"get_logger",
|
"get_logger",
|
||||||
"register_plugin",
|
"register_plugin",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ class ChatManager:
|
|||||||
chat_stream: 聊天流对象
|
chat_stream: 聊天流对象
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, Any]: 聊天流信息字典
|
Dict ({str: Any}): 聊天流信息字典
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: 如果 chat_stream 不是 ChatStream 类型
|
TypeError: 如果 chat_stream 不是 ChatStream 类型
|
||||||
@@ -285,41 +285,41 @@ class ChatManager:
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
|
||||||
def get_all_streams(platform: Optional[str] | SpecialTypes = "qq"):
|
def get_all_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]:
|
||||||
"""获取所有聊天流的便捷函数"""
|
"""获取所有聊天流的便捷函数"""
|
||||||
return ChatManager.get_all_streams(platform)
|
return ChatManager.get_all_streams(platform)
|
||||||
|
|
||||||
|
|
||||||
def get_group_streams(platform: Optional[str] | SpecialTypes = "qq"):
|
def get_group_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]:
|
||||||
"""获取群聊聊天流的便捷函数"""
|
"""获取群聊聊天流的便捷函数"""
|
||||||
return ChatManager.get_group_streams(platform)
|
return ChatManager.get_group_streams(platform)
|
||||||
|
|
||||||
|
|
||||||
def get_private_streams(platform: Optional[str] | SpecialTypes = "qq"):
|
def get_private_streams(platform: Optional[str] | SpecialTypes = "qq") -> List[ChatStream]:
|
||||||
"""获取私聊聊天流的便捷函数"""
|
"""获取私聊聊天流的便捷函数"""
|
||||||
return ChatManager.get_private_streams(platform)
|
return ChatManager.get_private_streams(platform)
|
||||||
|
|
||||||
|
|
||||||
def get_stream_by_group_id(group_id: str, platform: Optional[str] | SpecialTypes = "qq"):
|
def get_stream_by_group_id(group_id: str, platform: Optional[str] | SpecialTypes = "qq") -> Optional[ChatStream]:
|
||||||
"""根据群ID获取聊天流的便捷函数"""
|
"""根据群ID获取聊天流的便捷函数"""
|
||||||
return ChatManager.get_group_stream_by_group_id(group_id, platform)
|
return ChatManager.get_group_stream_by_group_id(group_id, platform)
|
||||||
|
|
||||||
|
|
||||||
def get_stream_by_user_id(user_id: str, platform: Optional[str] | SpecialTypes = "qq"):
|
def get_stream_by_user_id(user_id: str, platform: Optional[str] | SpecialTypes = "qq") -> Optional[ChatStream]:
|
||||||
"""根据用户ID获取私聊流的便捷函数"""
|
"""根据用户ID获取私聊流的便捷函数"""
|
||||||
return ChatManager.get_private_stream_by_user_id(user_id, platform)
|
return ChatManager.get_private_stream_by_user_id(user_id, platform)
|
||||||
|
|
||||||
|
|
||||||
def get_stream_type(chat_stream: ChatStream):
|
def get_stream_type(chat_stream: ChatStream) -> str:
|
||||||
"""获取聊天流类型的便捷函数"""
|
"""获取聊天流类型的便捷函数"""
|
||||||
return ChatManager.get_stream_type(chat_stream)
|
return ChatManager.get_stream_type(chat_stream)
|
||||||
|
|
||||||
|
|
||||||
def get_stream_info(chat_stream: ChatStream):
|
def get_stream_info(chat_stream: ChatStream) -> Dict[str, Any]:
|
||||||
"""获取聊天流信息的便捷函数"""
|
"""获取聊天流信息的便捷函数"""
|
||||||
return ChatManager.get_stream_info(chat_stream)
|
return ChatManager.get_stream_info(chat_stream)
|
||||||
|
|
||||||
|
|
||||||
def get_streams_summary():
|
def get_streams_summary() -> Dict[str, int]:
|
||||||
"""获取聊天流统计摘要的便捷函数"""
|
"""获取聊天流统计摘要的便捷函数"""
|
||||||
return ChatManager.get_streams_summary()
|
return ChatManager.get_streams_summary()
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.person_info.person_info import get_person_info_manager
|
|
||||||
|
|
||||||
logger = get_logger("config_api")
|
logger = get_logger("config_api")
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ def get_global_config(key: str, default: Any = None) -> Any:
|
|||||||
插件应使用此方法读取全局配置,以保证只读和隔离性。
|
插件应使用此方法读取全局配置,以保证只读和隔离性。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key: 命名空间式配置键名,支持嵌套访问,如 "section.subsection.key",大小写敏感
|
key: 命名空间式配置键名,使用嵌套访问,如 "section.subsection.key",大小写敏感
|
||||||
default: 如果配置不存在时返回的默认值
|
default: 如果配置不存在时返回的默认值
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -76,50 +75,3 @@ def get_plugin_config(plugin_config: dict, key: str, default: Any = None) -> Any
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"[ConfigAPI] 获取插件配置 {key} 失败: {e}")
|
logger.warning(f"[ConfigAPI] 获取插件配置 {key} 失败: {e}")
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 用户信息API函数
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
async def get_user_id_by_person_name(person_name: str) -> tuple[str, str]:
|
|
||||||
"""根据内部用户名获取用户ID
|
|
||||||
|
|
||||||
Args:
|
|
||||||
person_name: 用户名
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
tuple[str, str]: (平台, 用户ID)
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
person_info_manager = get_person_info_manager()
|
|
||||||
person_id = person_info_manager.get_person_id_by_person_name(person_name)
|
|
||||||
user_id: str = await person_info_manager.get_value(person_id, "user_id") # type: ignore
|
|
||||||
platform: str = await person_info_manager.get_value(person_id, "platform") # type: ignore
|
|
||||||
return platform, user_id
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[ConfigAPI] 根据用户名获取用户ID失败: {e}")
|
|
||||||
return "", ""
|
|
||||||
|
|
||||||
|
|
||||||
async def get_person_info(person_id: str, key: str, default: Any = None) -> Any:
|
|
||||||
"""获取用户信息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
person_id: 用户ID
|
|
||||||
key: 信息键名
|
|
||||||
default: 默认值
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Any: 用户信息值或默认值
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
person_info_manager = get_person_info_manager()
|
|
||||||
response = await person_info_manager.get_value(person_id, key)
|
|
||||||
if not response:
|
|
||||||
raise ValueError(f"[ConfigAPI] 获取用户 {person_id} 的信息 '{key}' 失败,返回默认值")
|
|
||||||
return response
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[ConfigAPI] 获取用户信息失败: {e}")
|
|
||||||
return default
|
|
||||||
|
|||||||
@@ -152,10 +152,7 @@ async def db_query(
|
|||||||
|
|
||||||
except DoesNotExist:
|
except DoesNotExist:
|
||||||
# 记录不存在
|
# 记录不存在
|
||||||
if query_type == "get" and single_result:
|
return None if query_type == "get" and single_result else []
|
||||||
return None
|
|
||||||
return []
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[DatabaseAPI] 数据库操作出错: {e}")
|
logger.error(f"[DatabaseAPI] 数据库操作出错: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@@ -170,7 +167,8 @@ async def db_query(
|
|||||||
|
|
||||||
async def db_save(
|
async def db_save(
|
||||||
model_class: Type[Model], data: Dict[str, Any], key_field: Optional[str] = None, key_value: Optional[Any] = None
|
model_class: Type[Model], data: Dict[str, Any], key_field: Optional[str] = None, key_value: Optional[Any] = None
|
||||||
) -> Union[Dict[str, Any], None]:
|
) -> Optional[Dict[str, Any]]:
|
||||||
|
# sourcery skip: inline-immediately-returned-variable
|
||||||
"""保存数据到数据库(创建或更新)
|
"""保存数据到数据库(创建或更新)
|
||||||
|
|
||||||
如果提供了key_field和key_value,会先尝试查找匹配的记录进行更新;
|
如果提供了key_field和key_value,会先尝试查找匹配的记录进行更新;
|
||||||
@@ -203,10 +201,9 @@ async def db_save(
|
|||||||
try:
|
try:
|
||||||
# 如果提供了key_field和key_value,尝试更新现有记录
|
# 如果提供了key_field和key_value,尝试更新现有记录
|
||||||
if key_field and key_value is not None:
|
if key_field and key_value is not None:
|
||||||
# 查找现有记录
|
if existing_records := list(
|
||||||
existing_records = list(model_class.select().where(getattr(model_class, key_field) == key_value).limit(1))
|
model_class.select().where(getattr(model_class, key_field) == key_value).limit(1)
|
||||||
|
):
|
||||||
if existing_records:
|
|
||||||
# 更新现有记录
|
# 更新现有记录
|
||||||
existing_record = existing_records[0]
|
existing_record = existing_records[0]
|
||||||
for field, value in data.items():
|
for field, value in data.items():
|
||||||
@@ -244,8 +241,8 @@ async def db_get(
|
|||||||
Args:
|
Args:
|
||||||
model_class: Peewee模型类
|
model_class: Peewee模型类
|
||||||
filters: 过滤条件,字段名和值的字典
|
filters: 过滤条件,字段名和值的字典
|
||||||
order_by: 排序字段,前缀'-'表示降序,例如'-time'表示按时间字段(即time字段)降序
|
|
||||||
limit: 结果数量限制
|
limit: 结果数量限制
|
||||||
|
order_by: 排序字段,前缀'-'表示降序,例如'-time'表示按时间字段(即time字段)降序
|
||||||
single_result: 是否只返回单个结果,如果为True,则返回单个记录字典或None;否则返回记录字典列表或空列表
|
single_result: 是否只返回单个结果,如果为True,则返回单个记录字典或None;否则返回记录字典列表或空列表
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -310,7 +307,7 @@ async def store_action_info(
|
|||||||
thinking_id: str = "",
|
thinking_id: str = "",
|
||||||
action_data: Optional[dict] = None,
|
action_data: Optional[dict] = None,
|
||||||
action_name: str = "",
|
action_name: str = "",
|
||||||
) -> Union[Dict[str, Any], None]:
|
) -> Optional[Dict[str, Any]]:
|
||||||
"""存储动作信息到数据库
|
"""存储动作信息到数据库
|
||||||
|
|
||||||
将Action执行的相关信息保存到ActionRecords表中,用于后续的记忆和上下文构建。
|
将Action执行的相关信息保存到ActionRecords表中,用于后续的记忆和上下文构建。
|
||||||
|
|||||||
@@ -65,14 +65,14 @@ async def get_by_description(description: str) -> Optional[Tuple[str, str, str]]
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def get_random(count: Optional[int] = 1) -> Optional[List[Tuple[str, str, str]]]:
|
async def get_random(count: Optional[int] = 1) -> List[Tuple[str, str, str]]:
|
||||||
"""随机获取指定数量的表情包
|
"""随机获取指定数量的表情包
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
count: 要获取的表情包数量,默认为1
|
count: 要获取的表情包数量,默认为1
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Optional[List[Tuple[str, str, str]]]: 包含(base64编码, 表情包描述, 随机情感标签)的元组列表,如果失败则为None
|
List[Tuple[str, str, str]]: 包含(base64编码, 表情包描述, 随机情感标签)的元组列表,失败则返回空列表
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: 如果count不是整数类型
|
TypeError: 如果count不是整数类型
|
||||||
@@ -94,13 +94,13 @@ async def get_random(count: Optional[int] = 1) -> Optional[List[Tuple[str, str,
|
|||||||
|
|
||||||
if not all_emojis:
|
if not all_emojis:
|
||||||
logger.warning("[EmojiAPI] 没有可用的表情包")
|
logger.warning("[EmojiAPI] 没有可用的表情包")
|
||||||
return None
|
return []
|
||||||
|
|
||||||
# 过滤有效表情包
|
# 过滤有效表情包
|
||||||
valid_emojis = [emoji for emoji in all_emojis if not emoji.is_deleted]
|
valid_emojis = [emoji for emoji in all_emojis if not emoji.is_deleted]
|
||||||
if not valid_emojis:
|
if not valid_emojis:
|
||||||
logger.warning("[EmojiAPI] 没有有效的表情包")
|
logger.warning("[EmojiAPI] 没有有效的表情包")
|
||||||
return None
|
return []
|
||||||
|
|
||||||
if len(valid_emojis) < count:
|
if len(valid_emojis) < count:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
@@ -127,14 +127,14 @@ async def get_random(count: Optional[int] = 1) -> Optional[List[Tuple[str, str,
|
|||||||
|
|
||||||
if not results and count > 0:
|
if not results and count > 0:
|
||||||
logger.warning("[EmojiAPI] 随机获取表情包失败,没有一个可以成功处理")
|
logger.warning("[EmojiAPI] 随机获取表情包失败,没有一个可以成功处理")
|
||||||
return None
|
return []
|
||||||
|
|
||||||
logger.info(f"[EmojiAPI] 成功获取 {len(results)} 个随机表情包")
|
logger.info(f"[EmojiAPI] 成功获取 {len(results)} 个随机表情包")
|
||||||
return results
|
return results
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[EmojiAPI] 获取随机表情包失败: {e}")
|
logger.error(f"[EmojiAPI] 获取随机表情包失败: {e}")
|
||||||
return None
|
return []
|
||||||
|
|
||||||
|
|
||||||
async def get_by_emotion(emotion: str) -> Optional[Tuple[str, str, str]]:
|
async def get_by_emotion(emotion: str) -> Optional[Tuple[str, str, str]]:
|
||||||
@@ -162,10 +162,11 @@ async def get_by_emotion(emotion: str) -> Optional[Tuple[str, str, str]]:
|
|||||||
|
|
||||||
# 筛选匹配情感的表情包
|
# 筛选匹配情感的表情包
|
||||||
matching_emojis = []
|
matching_emojis = []
|
||||||
for emoji_obj in all_emojis:
|
matching_emojis.extend(
|
||||||
if not emoji_obj.is_deleted and emotion.lower() in [e.lower() for e in emoji_obj.emotion]:
|
emoji_obj
|
||||||
matching_emojis.append(emoji_obj)
|
for emoji_obj in all_emojis
|
||||||
|
if not emoji_obj.is_deleted and emotion.lower() in [e.lower() for e in emoji_obj.emotion]
|
||||||
|
)
|
||||||
if not matching_emojis:
|
if not matching_emojis:
|
||||||
logger.warning(f"[EmojiAPI] 未找到匹配情感 '{emotion}' 的表情包")
|
logger.warning(f"[EmojiAPI] 未找到匹配情感 '{emotion}' 的表情包")
|
||||||
return None
|
return None
|
||||||
@@ -256,10 +257,11 @@ def get_descriptions() -> List[str]:
|
|||||||
emoji_manager = get_emoji_manager()
|
emoji_manager = get_emoji_manager()
|
||||||
descriptions = []
|
descriptions = []
|
||||||
|
|
||||||
for emoji_obj in emoji_manager.emoji_objects:
|
descriptions.extend(
|
||||||
if not emoji_obj.is_deleted and emoji_obj.description:
|
emoji_obj.description
|
||||||
descriptions.append(emoji_obj.description)
|
for emoji_obj in emoji_manager.emoji_objects
|
||||||
|
if not emoji_obj.is_deleted and emoji_obj.description
|
||||||
|
)
|
||||||
return descriptions
|
return descriptions
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[EmojiAPI] 获取表情包描述失败: {e}")
|
logger.error(f"[EmojiAPI] 获取表情包描述失败: {e}")
|
||||||
|
|||||||
@@ -84,18 +84,23 @@ async def generate_reply(
|
|||||||
enable_chinese_typo: bool = True,
|
enable_chinese_typo: bool = True,
|
||||||
return_prompt: bool = False,
|
return_prompt: bool = False,
|
||||||
model_configs: Optional[List[Dict[str, Any]]] = None,
|
model_configs: Optional[List[Dict[str, Any]]] = None,
|
||||||
request_type: str = "",
|
request_type: str = "generator_api",
|
||||||
enable_timeout: bool = False,
|
|
||||||
) -> Tuple[bool, List[Tuple[str, Any]], Optional[str]]:
|
) -> Tuple[bool, List[Tuple[str, Any]], Optional[str]]:
|
||||||
"""生成回复
|
"""生成回复
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
chat_stream: 聊天流对象(优先)
|
chat_stream: 聊天流对象(优先)
|
||||||
chat_id: 聊天ID(备用)
|
chat_id: 聊天ID(备用)
|
||||||
action_data: 动作数据
|
action_data: 动作数据(向下兼容,包含reply_to和extra_info)
|
||||||
|
reply_to: 回复对象,格式为 "发送者:消息内容"
|
||||||
|
extra_info: 额外信息,用于补充上下文
|
||||||
|
available_actions: 可用动作
|
||||||
|
enable_tool: 是否启用工具调用
|
||||||
enable_splitter: 是否启用消息分割器
|
enable_splitter: 是否启用消息分割器
|
||||||
enable_chinese_typo: 是否启用错字生成器
|
enable_chinese_typo: 是否启用错字生成器
|
||||||
return_prompt: 是否返回提示词
|
return_prompt: 是否返回提示词
|
||||||
|
model_configs: 模型配置列表
|
||||||
|
request_type: 请求类型(可选,记录LLM使用)
|
||||||
Returns:
|
Returns:
|
||||||
Tuple[bool, List[Tuple[str, Any]], Optional[str]]: (是否成功, 回复集合, 提示词)
|
Tuple[bool, List[Tuple[str, Any]], Optional[str]]: (是否成功, 回复集合, 提示词)
|
||||||
"""
|
"""
|
||||||
@@ -118,7 +123,6 @@ async def generate_reply(
|
|||||||
reply_to=reply_to,
|
reply_to=reply_to,
|
||||||
extra_info=extra_info,
|
extra_info=extra_info,
|
||||||
available_actions=available_actions,
|
available_actions=available_actions,
|
||||||
enable_timeout=enable_timeout,
|
|
||||||
enable_tool=enable_tool,
|
enable_tool=enable_tool,
|
||||||
)
|
)
|
||||||
reply_set = []
|
reply_set = []
|
||||||
@@ -151,15 +155,24 @@ async def rewrite_reply(
|
|||||||
enable_splitter: bool = True,
|
enable_splitter: bool = True,
|
||||||
enable_chinese_typo: bool = True,
|
enable_chinese_typo: bool = True,
|
||||||
model_configs: Optional[List[Dict[str, Any]]] = None,
|
model_configs: Optional[List[Dict[str, Any]]] = None,
|
||||||
) -> Tuple[bool, List[Tuple[str, Any]]]:
|
raw_reply: str = "",
|
||||||
|
reason: str = "",
|
||||||
|
reply_to: str = "",
|
||||||
|
return_prompt: bool = False,
|
||||||
|
) -> Tuple[bool, List[Tuple[str, Any]], Optional[str]]:
|
||||||
"""重写回复
|
"""重写回复
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
chat_stream: 聊天流对象(优先)
|
chat_stream: 聊天流对象(优先)
|
||||||
reply_data: 回复数据
|
reply_data: 回复数据字典(向下兼容备用,当其他参数缺失时从此获取)
|
||||||
chat_id: 聊天ID(备用)
|
chat_id: 聊天ID(备用)
|
||||||
enable_splitter: 是否启用消息分割器
|
enable_splitter: 是否启用消息分割器
|
||||||
enable_chinese_typo: 是否启用错字生成器
|
enable_chinese_typo: 是否启用错字生成器
|
||||||
|
model_configs: 模型配置列表
|
||||||
|
raw_reply: 原始回复内容
|
||||||
|
reason: 回复原因
|
||||||
|
reply_to: 回复对象
|
||||||
|
return_prompt: 是否返回提示词
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple[bool, List[Tuple[str, Any]]]: (是否成功, 回复集合)
|
Tuple[bool, List[Tuple[str, Any]]]: (是否成功, 回复集合)
|
||||||
@@ -169,12 +182,23 @@ async def rewrite_reply(
|
|||||||
replyer = get_replyer(chat_stream, chat_id, model_configs=model_configs)
|
replyer = get_replyer(chat_stream, chat_id, model_configs=model_configs)
|
||||||
if not replyer:
|
if not replyer:
|
||||||
logger.error("[GeneratorAPI] 无法获取回复器")
|
logger.error("[GeneratorAPI] 无法获取回复器")
|
||||||
return False, []
|
return False, [], None
|
||||||
|
|
||||||
logger.info("[GeneratorAPI] 开始重写回复")
|
logger.info("[GeneratorAPI] 开始重写回复")
|
||||||
|
|
||||||
|
# 如果参数缺失,从reply_data中获取
|
||||||
|
if reply_data:
|
||||||
|
raw_reply = raw_reply or reply_data.get("raw_reply", "")
|
||||||
|
reason = reason or reply_data.get("reason", "")
|
||||||
|
reply_to = reply_to or reply_data.get("reply_to", "")
|
||||||
|
|
||||||
# 调用回复器重写回复
|
# 调用回复器重写回复
|
||||||
success, content = await replyer.rewrite_reply_with_context(reply_data=reply_data or {})
|
success, content, prompt = await replyer.rewrite_reply_with_context(
|
||||||
|
raw_reply=raw_reply,
|
||||||
|
reason=reason,
|
||||||
|
reply_to=reply_to,
|
||||||
|
return_prompt=return_prompt,
|
||||||
|
)
|
||||||
reply_set = []
|
reply_set = []
|
||||||
if content:
|
if content:
|
||||||
reply_set = await process_human_text(content, enable_splitter, enable_chinese_typo)
|
reply_set = await process_human_text(content, enable_splitter, enable_chinese_typo)
|
||||||
@@ -184,14 +208,14 @@ async def rewrite_reply(
|
|||||||
else:
|
else:
|
||||||
logger.warning("[GeneratorAPI] 重写回复失败")
|
logger.warning("[GeneratorAPI] 重写回复失败")
|
||||||
|
|
||||||
return success, reply_set
|
return success, reply_set, prompt if return_prompt else None
|
||||||
|
|
||||||
except ValueError as ve:
|
except ValueError as ve:
|
||||||
raise ve
|
raise ve
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[GeneratorAPI] 重写回复时出错: {e}")
|
logger.error(f"[GeneratorAPI] 重写回复时出错: {e}")
|
||||||
return False, []
|
return False, [], None
|
||||||
|
|
||||||
|
|
||||||
async def process_human_text(content: str, enable_splitter: bool, enable_chinese_typo: bool) -> List[Tuple[str, Any]]:
|
async def process_human_text(content: str, enable_splitter: bool, enable_chinese_typo: bool) -> List[Tuple[str, Any]]:
|
||||||
@@ -217,3 +241,27 @@ async def process_human_text(content: str, enable_splitter: bool, enable_chinese
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[GeneratorAPI] 处理人形文本时出错: {e}")
|
logger.error(f"[GeneratorAPI] 处理人形文本时出错: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
async def generate_response_custom(
|
||||||
|
chat_stream: Optional[ChatStream] = None,
|
||||||
|
chat_id: Optional[str] = None,
|
||||||
|
model_configs: Optional[List[Dict[str, Any]]] = None,
|
||||||
|
prompt: str = "",
|
||||||
|
) -> Optional[str]:
|
||||||
|
replyer = get_replyer(chat_stream, chat_id, model_configs=model_configs)
|
||||||
|
if not replyer:
|
||||||
|
logger.error("[GeneratorAPI] 无法获取回复器")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.debug("[GeneratorAPI] 开始生成自定义回复")
|
||||||
|
response = await replyer.llm_generate_content(prompt)
|
||||||
|
if response:
|
||||||
|
logger.debug("[GeneratorAPI] 自定义回复生成成功")
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
logger.warning("[GeneratorAPI] 自定义回复生成失败")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[GeneratorAPI] 生成自定义回复时出错: {e}")
|
||||||
|
return None
|
||||||
@@ -54,7 +54,7 @@ def get_available_models() -> Dict[str, Any]:
|
|||||||
|
|
||||||
async def generate_with_model(
|
async def generate_with_model(
|
||||||
prompt: str, model_config: Dict[str, Any], request_type: str = "plugin.generate", **kwargs
|
prompt: str, model_config: Dict[str, Any], request_type: str = "plugin.generate", **kwargs
|
||||||
) -> Tuple[bool, str, str, str]:
|
) -> Tuple[bool, str]:
|
||||||
"""使用指定模型生成内容
|
"""使用指定模型生成内容
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -73,10 +73,11 @@ async def generate_with_model(
|
|||||||
|
|
||||||
llm_request = LLMRequest(model=model_config, request_type=request_type, **kwargs)
|
llm_request = LLMRequest(model=model_config, request_type=request_type, **kwargs)
|
||||||
|
|
||||||
response, (reasoning, model_name) = await llm_request.generate_response_async(prompt)
|
# TODO: 复活这个_
|
||||||
return True, response, reasoning, model_name
|
response, _ = await llm_request.generate_response_async(prompt)
|
||||||
|
return True, response
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = f"生成内容时出错: {str(e)}"
|
error_msg = f"生成内容时出错: {str(e)}"
|
||||||
logger.error(f"[LLMAPI] {error_msg}")
|
logger.error(f"[LLMAPI] {error_msg}")
|
||||||
return False, error_msg, "", ""
|
return False, error_msg
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ def get_random_chat_messages(
|
|||||||
|
|
||||||
|
|
||||||
def get_messages_by_time_for_users(
|
def get_messages_by_time_for_users(
|
||||||
start_time: float, end_time: float, person_ids: list, limit: int = 0, limit_mode: str = "latest"
|
start_time: float, end_time: float, person_ids: List[str], limit: int = 0, limit_mode: str = "latest"
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
获取指定用户在所有聊天中指定时间范围内的消息
|
获取指定用户在所有聊天中指定时间范围内的消息
|
||||||
@@ -287,7 +287,7 @@ def get_messages_before_time_in_chat(
|
|||||||
return get_raw_msg_before_timestamp_with_chat(chat_id, timestamp, limit)
|
return get_raw_msg_before_timestamp_with_chat(chat_id, timestamp, limit)
|
||||||
|
|
||||||
|
|
||||||
def get_messages_before_time_for_users(timestamp: float, person_ids: list, limit: int = 0) -> List[Dict[str, Any]]:
|
def get_messages_before_time_for_users(timestamp: float, person_ids: List[str], limit: int = 0) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
获取指定用户在指定时间戳之前的消息
|
获取指定用户在指定时间戳之前的消息
|
||||||
|
|
||||||
@@ -372,7 +372,7 @@ def count_new_messages(chat_id: str, start_time: float = 0.0, end_time: Optional
|
|||||||
return num_new_messages_since(chat_id, start_time, end_time)
|
return num_new_messages_since(chat_id, start_time, end_time)
|
||||||
|
|
||||||
|
|
||||||
def count_new_messages_for_users(chat_id: str, start_time: float, end_time: float, person_ids: list) -> int:
|
def count_new_messages_for_users(chat_id: str, start_time: float, end_time: float, person_ids: List[str]) -> int:
|
||||||
"""
|
"""
|
||||||
计算指定聊天中指定用户从开始时间到结束时间的新消息数量
|
计算指定聊天中指定用户从开始时间到结束时间的新消息数量
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
from typing import Tuple, List
|
from typing import Tuple, List
|
||||||
|
|
||||||
|
|
||||||
def list_loaded_plugins() -> List[str]:
|
def list_loaded_plugins() -> List[str]:
|
||||||
"""
|
"""
|
||||||
列出所有当前加载的插件。
|
列出所有当前加载的插件。
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: 当前加载的插件名称列表。
|
List[str]: 当前加载的插件名称列表。
|
||||||
"""
|
"""
|
||||||
from src.plugin_system.core.plugin_manager import plugin_manager
|
from src.plugin_system.core.plugin_manager import plugin_manager
|
||||||
|
|
||||||
@@ -16,13 +18,34 @@ def list_registered_plugins() -> List[str]:
|
|||||||
列出所有已注册的插件。
|
列出所有已注册的插件。
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: 已注册的插件名称列表。
|
List[str]: 已注册的插件名称列表。
|
||||||
"""
|
"""
|
||||||
from src.plugin_system.core.plugin_manager import plugin_manager
|
from src.plugin_system.core.plugin_manager import plugin_manager
|
||||||
|
|
||||||
return plugin_manager.list_registered_plugins()
|
return plugin_manager.list_registered_plugins()
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin_path(plugin_name: str) -> str:
|
||||||
|
"""
|
||||||
|
获取指定插件的路径。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_name (str): 插件名称。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 插件目录的绝对路径。
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: 如果插件不存在。
|
||||||
|
"""
|
||||||
|
from src.plugin_system.core.plugin_manager import plugin_manager
|
||||||
|
|
||||||
|
if plugin_path := plugin_manager.get_plugin_path(plugin_name):
|
||||||
|
return plugin_path
|
||||||
|
else:
|
||||||
|
raise ValueError(f"插件 '{plugin_name}' 不存在。")
|
||||||
|
|
||||||
|
|
||||||
async def remove_plugin(plugin_name: str) -> bool:
|
async def remove_plugin(plugin_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
卸载指定的插件。
|
卸载指定的插件。
|
||||||
@@ -71,6 +94,7 @@ def load_plugin(plugin_name: str) -> Tuple[bool, int]:
|
|||||||
|
|
||||||
return plugin_manager.load_registered_plugin_classes(plugin_name)
|
return plugin_manager.load_registered_plugin_classes(plugin_name)
|
||||||
|
|
||||||
|
|
||||||
def add_plugin_directory(plugin_directory: str) -> bool:
|
def add_plugin_directory(plugin_directory: str) -> bool:
|
||||||
"""
|
"""
|
||||||
添加插件目录。
|
添加插件目录。
|
||||||
@@ -84,6 +108,7 @@ def add_plugin_directory(plugin_directory: str) -> bool:
|
|||||||
|
|
||||||
return plugin_manager.add_plugin_directory(plugin_directory)
|
return plugin_manager.add_plugin_directory(plugin_directory)
|
||||||
|
|
||||||
|
|
||||||
def rescan_plugin_directory() -> Tuple[int, int]:
|
def rescan_plugin_directory() -> Tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
重新扫描插件目录,加载新插件。
|
重新扫描插件目录,加载新插件。
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ async def _send_to_target(
|
|||||||
display_message: str = "",
|
display_message: str = "",
|
||||||
typing: bool = False,
|
typing: bool = False,
|
||||||
reply_to: str = "",
|
reply_to: str = "",
|
||||||
reply_to_platform_id: str = "",
|
reply_to_platform_id: Optional[str] = None,
|
||||||
storage_message: bool = True,
|
storage_message: bool = True,
|
||||||
show_log: bool = True,
|
show_log: bool = True,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
@@ -60,8 +60,11 @@ async def _send_to_target(
|
|||||||
content: 消息内容
|
content: 消息内容
|
||||||
stream_id: 目标流ID
|
stream_id: 目标流ID
|
||||||
display_message: 显示消息
|
display_message: 显示消息
|
||||||
typing: 是否显示正在输入
|
typing: 是否模拟打字等待。
|
||||||
reply_to: 回复消息的格式,如"发送者:消息内容"
|
reply_to: 回复消息,格式为"发送者:消息内容"
|
||||||
|
reply_to_platform_id: 回复消息,格式为"平台:用户ID",如果不提供则自动查找(插件开发者禁用!)
|
||||||
|
storage_message: 是否存储消息到数据库
|
||||||
|
show_log: 发送是否显示日志
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 是否发送成功
|
bool: 是否发送成功
|
||||||
@@ -97,6 +100,10 @@ async def _send_to_target(
|
|||||||
anchor_message = None
|
anchor_message = None
|
||||||
if reply_to:
|
if reply_to:
|
||||||
anchor_message = await _find_reply_message(target_stream, reply_to)
|
anchor_message = await _find_reply_message(target_stream, reply_to)
|
||||||
|
if anchor_message and anchor_message.message_info.user_info and not reply_to_platform_id:
|
||||||
|
reply_to_platform_id = (
|
||||||
|
f"{anchor_message.message_info.platform}:{anchor_message.message_info.user_info.user_id}"
|
||||||
|
)
|
||||||
|
|
||||||
# 构建发送消息对象
|
# 构建发送消息对象
|
||||||
bot_message = MessageSending(
|
bot_message = MessageSending(
|
||||||
@@ -262,12 +269,22 @@ async def text_to_stream(
|
|||||||
stream_id: 聊天流ID
|
stream_id: 聊天流ID
|
||||||
typing: 是否显示正在输入
|
typing: 是否显示正在输入
|
||||||
reply_to: 回复消息,格式为"发送者:消息内容"
|
reply_to: 回复消息,格式为"发送者:消息内容"
|
||||||
|
reply_to_platform_id: 回复消息,格式为"平台:用户ID",如果不提供则自动查找(插件开发者禁用!)
|
||||||
storage_message: 是否存储消息到数据库
|
storage_message: 是否存储消息到数据库
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 是否发送成功
|
bool: 是否发送成功
|
||||||
"""
|
"""
|
||||||
return await _send_to_target("text", text, stream_id, "", typing, reply_to, reply_to_platform_id, storage_message)
|
return await _send_to_target(
|
||||||
|
"text",
|
||||||
|
text,
|
||||||
|
stream_id,
|
||||||
|
"",
|
||||||
|
typing,
|
||||||
|
reply_to,
|
||||||
|
reply_to_platform_id=reply_to_platform_id,
|
||||||
|
storage_message=storage_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def emoji_to_stream(emoji_base64: str, stream_id: str, storage_message: bool = True) -> bool:
|
async def emoji_to_stream(emoji_base64: str, stream_id: str, storage_message: bool = True) -> bool:
|
||||||
@@ -350,249 +367,3 @@ async def custom_to_stream(
|
|||||||
storage_message=storage_message,
|
storage_message=storage_message,
|
||||||
show_log=show_log,
|
show_log=show_log,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def text_to_group(
|
|
||||||
text: str,
|
|
||||||
group_id: str,
|
|
||||||
platform: str = "qq",
|
|
||||||
typing: bool = False,
|
|
||||||
reply_to: str = "",
|
|
||||||
storage_message: bool = True,
|
|
||||||
) -> bool:
|
|
||||||
"""向群聊发送文本消息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: 要发送的文本内容
|
|
||||||
group_id: 群聊ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
typing: 是否显示正在输入
|
|
||||||
reply_to: 回复消息,格式为"发送者:消息内容"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, group_id, True)
|
|
||||||
|
|
||||||
return await _send_to_target("text", text, stream_id, "", typing, reply_to, storage_message=storage_message)
|
|
||||||
|
|
||||||
|
|
||||||
async def text_to_user(
|
|
||||||
text: str,
|
|
||||||
user_id: str,
|
|
||||||
platform: str = "qq",
|
|
||||||
typing: bool = False,
|
|
||||||
reply_to: str = "",
|
|
||||||
storage_message: bool = True,
|
|
||||||
) -> bool:
|
|
||||||
"""向用户发送私聊文本消息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: 要发送的文本内容
|
|
||||||
user_id: 用户ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
typing: 是否显示正在输入
|
|
||||||
reply_to: 回复消息,格式为"发送者:消息内容"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, user_id, False)
|
|
||||||
return await _send_to_target("text", text, stream_id, "", typing, reply_to, storage_message=storage_message)
|
|
||||||
|
|
||||||
|
|
||||||
async def emoji_to_group(emoji_base64: str, group_id: str, platform: str = "qq", storage_message: bool = True) -> bool:
|
|
||||||
"""向群聊发送表情包
|
|
||||||
|
|
||||||
Args:
|
|
||||||
emoji_base64: 表情包的base64编码
|
|
||||||
group_id: 群聊ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, group_id, True)
|
|
||||||
return await _send_to_target("emoji", emoji_base64, stream_id, "", typing=False, storage_message=storage_message)
|
|
||||||
|
|
||||||
|
|
||||||
async def emoji_to_user(emoji_base64: str, user_id: str, platform: str = "qq", storage_message: bool = True) -> bool:
|
|
||||||
"""向用户发送表情包
|
|
||||||
|
|
||||||
Args:
|
|
||||||
emoji_base64: 表情包的base64编码
|
|
||||||
user_id: 用户ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, user_id, False)
|
|
||||||
return await _send_to_target("emoji", emoji_base64, stream_id, "", typing=False, storage_message=storage_message)
|
|
||||||
|
|
||||||
|
|
||||||
async def image_to_group(image_base64: str, group_id: str, platform: str = "qq", storage_message: bool = True) -> bool:
|
|
||||||
"""向群聊发送图片
|
|
||||||
|
|
||||||
Args:
|
|
||||||
image_base64: 图片的base64编码
|
|
||||||
group_id: 群聊ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, group_id, True)
|
|
||||||
return await _send_to_target("image", image_base64, stream_id, "", typing=False, storage_message=storage_message)
|
|
||||||
|
|
||||||
|
|
||||||
async def image_to_user(image_base64: str, user_id: str, platform: str = "qq", storage_message: bool = True) -> bool:
|
|
||||||
"""向用户发送图片
|
|
||||||
|
|
||||||
Args:
|
|
||||||
image_base64: 图片的base64编码
|
|
||||||
user_id: 用户ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, user_id, False)
|
|
||||||
return await _send_to_target("image", image_base64, stream_id, "", typing=False)
|
|
||||||
|
|
||||||
|
|
||||||
async def command_to_group(command: str, group_id: str, platform: str = "qq", storage_message: bool = True) -> bool:
|
|
||||||
"""向群聊发送命令
|
|
||||||
|
|
||||||
Args:
|
|
||||||
command: 命令
|
|
||||||
group_id: 群聊ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, group_id, True)
|
|
||||||
return await _send_to_target("command", command, stream_id, "", typing=False, storage_message=storage_message)
|
|
||||||
|
|
||||||
|
|
||||||
async def command_to_user(command: str, user_id: str, platform: str = "qq", storage_message: bool = True) -> bool:
|
|
||||||
"""向用户发送命令
|
|
||||||
|
|
||||||
Args:
|
|
||||||
command: 命令
|
|
||||||
user_id: 用户ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, user_id, False)
|
|
||||||
return await _send_to_target("command", command, stream_id, "", typing=False, storage_message=storage_message)
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 通用发送函数 - 支持任意消息类型
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
async def custom_to_group(
|
|
||||||
message_type: str,
|
|
||||||
content: str,
|
|
||||||
group_id: str,
|
|
||||||
platform: str = "qq",
|
|
||||||
display_message: str = "",
|
|
||||||
typing: bool = False,
|
|
||||||
reply_to: str = "",
|
|
||||||
storage_message: bool = True,
|
|
||||||
) -> bool:
|
|
||||||
"""向群聊发送自定义类型消息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
message_type: 消息类型,如"text"、"image"、"emoji"、"video"、"file"等
|
|
||||||
content: 消息内容(通常是base64编码或文本)
|
|
||||||
group_id: 群聊ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
display_message: 显示消息
|
|
||||||
typing: 是否显示正在输入
|
|
||||||
reply_to: 回复消息,格式为"发送者:消息内容"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, group_id, True)
|
|
||||||
return await _send_to_target(
|
|
||||||
message_type, content, stream_id, display_message, typing, reply_to, storage_message=storage_message
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def custom_to_user(
|
|
||||||
message_type: str,
|
|
||||||
content: str,
|
|
||||||
user_id: str,
|
|
||||||
platform: str = "qq",
|
|
||||||
display_message: str = "",
|
|
||||||
typing: bool = False,
|
|
||||||
reply_to: str = "",
|
|
||||||
storage_message: bool = True,
|
|
||||||
) -> bool:
|
|
||||||
"""向用户发送自定义类型消息
|
|
||||||
|
|
||||||
Args:
|
|
||||||
message_type: 消息类型,如"text"、"image"、"emoji"、"video"、"file"等
|
|
||||||
content: 消息内容(通常是base64编码或文本)
|
|
||||||
user_id: 用户ID
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
display_message: 显示消息
|
|
||||||
typing: 是否显示正在输入
|
|
||||||
reply_to: 回复消息,格式为"发送者:消息内容"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, user_id, False)
|
|
||||||
return await _send_to_target(
|
|
||||||
message_type, content, stream_id, display_message, typing, reply_to, storage_message=storage_message
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def custom_message(
|
|
||||||
message_type: str,
|
|
||||||
content: str,
|
|
||||||
target_id: str,
|
|
||||||
is_group: bool = True,
|
|
||||||
platform: str = "qq",
|
|
||||||
display_message: str = "",
|
|
||||||
typing: bool = False,
|
|
||||||
reply_to: str = "",
|
|
||||||
storage_message: bool = True,
|
|
||||||
) -> bool:
|
|
||||||
"""发送自定义消息的通用接口
|
|
||||||
|
|
||||||
Args:
|
|
||||||
message_type: 消息类型,如"text"、"image"、"emoji"、"video"、"file"、"audio"等
|
|
||||||
content: 消息内容
|
|
||||||
target_id: 目标ID(群ID或用户ID)
|
|
||||||
is_group: 是否为群聊,True为群聊,False为私聊
|
|
||||||
platform: 平台,默认为"qq"
|
|
||||||
display_message: 显示消息
|
|
||||||
typing: 是否显示正在输入
|
|
||||||
reply_to: 回复消息,格式为"发送者:消息内容"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否发送成功
|
|
||||||
|
|
||||||
示例:
|
|
||||||
# 发送视频到群聊
|
|
||||||
await send_api.custom_message("video", video_base64, "123456", True)
|
|
||||||
|
|
||||||
# 发送文件到用户
|
|
||||||
await send_api.custom_message("file", file_base64, "987654", False)
|
|
||||||
|
|
||||||
# 发送音频到群聊并回复特定消息
|
|
||||||
await send_api.custom_message("audio", audio_base64, "123456", True, reply_to="张三:你好")
|
|
||||||
"""
|
|
||||||
stream_id = get_chat_manager().get_stream_id(platform, target_id, is_group)
|
|
||||||
return await _send_to_target(
|
|
||||||
message_type, content, stream_id, display_message, typing, reply_to, storage_message=storage_message
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
"""工具类API模块
|
|
||||||
|
|
||||||
提供了各种辅助功能
|
|
||||||
使用方式:
|
|
||||||
from src.plugin_system.apis import utils_api
|
|
||||||
plugin_path = utils_api.get_plugin_path()
|
|
||||||
data = utils_api.read_json_file("data.json")
|
|
||||||
timestamp = utils_api.get_timestamp()
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import inspect
|
|
||||||
import datetime
|
|
||||||
import uuid
|
|
||||||
from typing import Any, Optional
|
|
||||||
from src.common.logger import get_logger
|
|
||||||
|
|
||||||
logger = get_logger("utils_api")
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 文件操作API函数
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_path(caller_frame=None) -> str:
|
|
||||||
"""获取调用者插件的路径
|
|
||||||
|
|
||||||
Args:
|
|
||||||
caller_frame: 调用者的栈帧,默认为None(自动获取)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 插件目录的绝对路径
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if caller_frame is None:
|
|
||||||
caller_frame = inspect.currentframe().f_back # type: ignore
|
|
||||||
|
|
||||||
plugin_module_path = inspect.getfile(caller_frame) # type: ignore
|
|
||||||
plugin_dir = os.path.dirname(plugin_module_path)
|
|
||||||
return plugin_dir
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[UtilsAPI] 获取插件路径失败: {e}")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def read_json_file(file_path: str, default: Any = None) -> Any:
|
|
||||||
"""读取JSON文件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path: 文件路径,可以是相对于插件目录的路径
|
|
||||||
default: 如果文件不存在或读取失败时返回的默认值
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Any: JSON数据或默认值
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# 如果是相对路径,则相对于调用者的插件目录
|
|
||||||
if not os.path.isabs(file_path):
|
|
||||||
caller_frame = inspect.currentframe().f_back # type: ignore
|
|
||||||
plugin_dir = get_plugin_path(caller_frame)
|
|
||||||
file_path = os.path.join(plugin_dir, file_path)
|
|
||||||
|
|
||||||
if not os.path.exists(file_path):
|
|
||||||
logger.warning(f"[UtilsAPI] 文件不存在: {file_path}")
|
|
||||||
return default
|
|
||||||
|
|
||||||
with open(file_path, "r", encoding="utf-8") as f:
|
|
||||||
return json.load(f)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[UtilsAPI] 读取JSON文件出错: {e}")
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def write_json_file(file_path: str, data: Any, indent: int = 2) -> bool:
|
|
||||||
"""写入JSON文件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path: 文件路径,可以是相对于插件目录的路径
|
|
||||||
data: 要写入的数据
|
|
||||||
indent: JSON缩进
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否写入成功
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# 如果是相对路径,则相对于调用者的插件目录
|
|
||||||
if not os.path.isabs(file_path):
|
|
||||||
caller_frame = inspect.currentframe().f_back # type: ignore
|
|
||||||
plugin_dir = get_plugin_path(caller_frame)
|
|
||||||
file_path = os.path.join(plugin_dir, file_path)
|
|
||||||
|
|
||||||
# 确保目录存在
|
|
||||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
||||||
|
|
||||||
with open(file_path, "w", encoding="utf-8") as f:
|
|
||||||
json.dump(data, f, ensure_ascii=False, indent=indent)
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[UtilsAPI] 写入JSON文件出错: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 时间相关API函数
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def get_timestamp() -> int:
|
|
||||||
"""获取当前时间戳
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 当前时间戳(秒)
|
|
||||||
"""
|
|
||||||
return int(time.time())
|
|
||||||
|
|
||||||
|
|
||||||
def format_time(timestamp: Optional[int | float] = None, format_str: str = "%Y-%m-%d %H:%M:%S") -> str:
|
|
||||||
"""格式化时间
|
|
||||||
|
|
||||||
Args:
|
|
||||||
timestamp: 时间戳,如果为None则使用当前时间
|
|
||||||
format_str: 时间格式字符串
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 格式化后的时间字符串
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if timestamp is None:
|
|
||||||
timestamp = time.time()
|
|
||||||
return datetime.datetime.fromtimestamp(timestamp).strftime(format_str)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[UtilsAPI] 格式化时间失败: {e}")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def parse_time(time_str: str, format_str: str = "%Y-%m-%d %H:%M:%S") -> int:
|
|
||||||
"""解析时间字符串为时间戳
|
|
||||||
|
|
||||||
Args:
|
|
||||||
time_str: 时间字符串
|
|
||||||
format_str: 时间格式字符串
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: 时间戳(秒)
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
dt = datetime.datetime.strptime(time_str, format_str)
|
|
||||||
return int(dt.timestamp())
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"[UtilsAPI] 解析时间失败: {e}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 其他工具函数
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def generate_unique_id() -> str:
|
|
||||||
"""生成唯一ID
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 唯一ID
|
|
||||||
"""
|
|
||||||
return str(uuid.uuid4())
|
|
||||||
@@ -208,7 +208,7 @@ class BaseAction(ABC):
|
|||||||
return False, f"等待新消息失败: {str(e)}"
|
return False, f"等待新消息失败: {str(e)}"
|
||||||
|
|
||||||
async def send_text(
|
async def send_text(
|
||||||
self, content: str, reply_to: str = "", reply_to_platform_id: str = "", typing: bool = False
|
self, content: str, reply_to: str = "", typing: bool = False
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""发送文本消息
|
"""发送文本消息
|
||||||
|
|
||||||
@@ -227,7 +227,6 @@ class BaseAction(ABC):
|
|||||||
text=content,
|
text=content,
|
||||||
stream_id=self.chat_id,
|
stream_id=self.chat_id,
|
||||||
reply_to=reply_to,
|
reply_to=reply_to,
|
||||||
reply_to_platform_id=reply_to_platform_id,
|
|
||||||
typing=typing,
|
typing=typing,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -225,6 +225,18 @@ class PluginManager:
|
|||||||
"""
|
"""
|
||||||
return list(self.plugin_classes.keys())
|
return list(self.plugin_classes.keys())
|
||||||
|
|
||||||
|
def get_plugin_path(self, plugin_name: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
获取指定插件的路径。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plugin_name: 插件名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[str]: 插件目录的绝对路径,如果插件不存在则返回None。
|
||||||
|
"""
|
||||||
|
return self.plugin_paths.get(plugin_name)
|
||||||
|
|
||||||
# === 私有方法 ===
|
# === 私有方法 ===
|
||||||
# == 目录管理 ==
|
# == 目录管理 ==
|
||||||
def _ensure_plugin_directories(self) -> None:
|
def _ensure_plugin_directories(self) -> None:
|
||||||
@@ -289,6 +301,7 @@ class PluginManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
module = module_from_spec(spec)
|
module = module_from_spec(spec)
|
||||||
|
module.__package__ = module_name # 设置模块包名
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
|
|
||||||
logger.debug(f"插件模块加载成功: {plugin_file}")
|
logger.debug(f"插件模块加载成功: {plugin_file}")
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ class EmojiAction(BaseAction):
|
|||||||
logger.error(f"{self.log_prefix} 未找到'utils_small'模型配置,无法调用LLM")
|
logger.error(f"{self.log_prefix} 未找到'utils_small'模型配置,无法调用LLM")
|
||||||
return False, "未找到'utils_small'模型配置"
|
return False, "未找到'utils_small'模型配置"
|
||||||
|
|
||||||
success, chosen_emotion, _, _ = await llm_api.generate_with_model(
|
success, chosen_emotion = await llm_api.generate_with_model(
|
||||||
prompt, model_config=chat_model_config, request_type="emoji"
|
prompt, model_config=chat_model_config, request_type="emoji"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from src.plugin_system import (
|
|||||||
component_manage_api,
|
component_manage_api,
|
||||||
ComponentInfo,
|
ComponentInfo,
|
||||||
ComponentType,
|
ComponentType,
|
||||||
|
send_api,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -27,8 +28,15 @@ class ManagementCommand(BaseCommand):
|
|||||||
or not self.message.message_info.user_info
|
or not self.message.message_info.user_info
|
||||||
or str(self.message.message_info.user_info.user_id) not in self.get_config("plugin.permission", []) # type: ignore
|
or str(self.message.message_info.user_info.user_id) not in self.get_config("plugin.permission", []) # type: ignore
|
||||||
):
|
):
|
||||||
await self.send_text("你没有权限使用插件管理命令")
|
await self._send_message("你没有权限使用插件管理命令")
|
||||||
return False, "没有权限", True
|
return False, "没有权限", True
|
||||||
|
if not self.message.chat_stream:
|
||||||
|
await self._send_message("无法获取聊天流信息")
|
||||||
|
return False, "无法获取聊天流信息", True
|
||||||
|
self.stream_id = self.message.chat_stream.stream_id
|
||||||
|
if not self.stream_id:
|
||||||
|
await self._send_message("无法获取聊天流信息")
|
||||||
|
return False, "无法获取聊天流信息", True
|
||||||
command_list = self.matched_groups["manage_command"].strip().split(" ")
|
command_list = self.matched_groups["manage_command"].strip().split(" ")
|
||||||
if len(command_list) == 1:
|
if len(command_list) == 1:
|
||||||
await self.show_help("all")
|
await self.show_help("all")
|
||||||
@@ -42,7 +50,7 @@ class ManagementCommand(BaseCommand):
|
|||||||
case "help":
|
case "help":
|
||||||
await self.show_help("all")
|
await self.show_help("all")
|
||||||
case _:
|
case _:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
if len(command_list) == 3:
|
if len(command_list) == 3:
|
||||||
if command_list[1] == "plugin":
|
if command_list[1] == "plugin":
|
||||||
@@ -56,7 +64,7 @@ class ManagementCommand(BaseCommand):
|
|||||||
case "rescan":
|
case "rescan":
|
||||||
await self._rescan_plugin_dirs()
|
await self._rescan_plugin_dirs()
|
||||||
case _:
|
case _:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
elif command_list[1] == "component":
|
elif command_list[1] == "component":
|
||||||
if command_list[2] == "list":
|
if command_list[2] == "list":
|
||||||
@@ -64,10 +72,10 @@ class ManagementCommand(BaseCommand):
|
|||||||
elif command_list[2] == "help":
|
elif command_list[2] == "help":
|
||||||
await self.show_help("component")
|
await self.show_help("component")
|
||||||
else:
|
else:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
else:
|
else:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
if len(command_list) == 4:
|
if len(command_list) == 4:
|
||||||
if command_list[1] == "plugin":
|
if command_list[1] == "plugin":
|
||||||
@@ -81,28 +89,28 @@ class ManagementCommand(BaseCommand):
|
|||||||
case "add_dir":
|
case "add_dir":
|
||||||
await self._add_dir(command_list[3])
|
await self._add_dir(command_list[3])
|
||||||
case _:
|
case _:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
elif command_list[1] == "component":
|
elif command_list[1] == "component":
|
||||||
if command_list[2] != "list":
|
if command_list[2] != "list":
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
if command_list[3] == "enabled":
|
if command_list[3] == "enabled":
|
||||||
await self._list_enabled_components()
|
await self._list_enabled_components()
|
||||||
elif command_list[3] == "disabled":
|
elif command_list[3] == "disabled":
|
||||||
await self._list_disabled_components()
|
await self._list_disabled_components()
|
||||||
else:
|
else:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
else:
|
else:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
if len(command_list) == 5:
|
if len(command_list) == 5:
|
||||||
if command_list[1] != "component":
|
if command_list[1] != "component":
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
if command_list[2] != "list":
|
if command_list[2] != "list":
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
if command_list[3] == "enabled":
|
if command_list[3] == "enabled":
|
||||||
await self._list_enabled_components(target_type=command_list[4])
|
await self._list_enabled_components(target_type=command_list[4])
|
||||||
@@ -111,11 +119,11 @@ class ManagementCommand(BaseCommand):
|
|||||||
elif command_list[3] == "type":
|
elif command_list[3] == "type":
|
||||||
await self._list_registered_components_by_type(command_list[4])
|
await self._list_registered_components_by_type(command_list[4])
|
||||||
else:
|
else:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
if len(command_list) == 6:
|
if len(command_list) == 6:
|
||||||
if command_list[1] != "component":
|
if command_list[1] != "component":
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
if command_list[2] == "enable":
|
if command_list[2] == "enable":
|
||||||
if command_list[3] == "global":
|
if command_list[3] == "global":
|
||||||
@@ -123,7 +131,7 @@ class ManagementCommand(BaseCommand):
|
|||||||
elif command_list[3] == "local":
|
elif command_list[3] == "local":
|
||||||
await self._locally_enable_component(command_list[4], command_list[5])
|
await self._locally_enable_component(command_list[4], command_list[5])
|
||||||
else:
|
else:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
elif command_list[2] == "disable":
|
elif command_list[2] == "disable":
|
||||||
if command_list[3] == "global":
|
if command_list[3] == "global":
|
||||||
@@ -131,10 +139,10 @@ class ManagementCommand(BaseCommand):
|
|||||||
elif command_list[3] == "local":
|
elif command_list[3] == "local":
|
||||||
await self._locally_disable_component(command_list[4], command_list[5])
|
await self._locally_disable_component(command_list[4], command_list[5])
|
||||||
else:
|
else:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
else:
|
else:
|
||||||
await self.send_text("插件管理命令不合法")
|
await self._send_message("插件管理命令不合法")
|
||||||
return False, "命令不合法", True
|
return False, "命令不合法", True
|
||||||
|
|
||||||
return True, "命令执行完成", True
|
return True, "命令执行完成", True
|
||||||
@@ -180,51 +188,51 @@ class ManagementCommand(BaseCommand):
|
|||||||
)
|
)
|
||||||
case _:
|
case _:
|
||||||
return
|
return
|
||||||
await self.send_text(help_msg)
|
await self._send_message(help_msg)
|
||||||
|
|
||||||
async def _list_loaded_plugins(self):
|
async def _list_loaded_plugins(self):
|
||||||
plugins = plugin_manage_api.list_loaded_plugins()
|
plugins = plugin_manage_api.list_loaded_plugins()
|
||||||
await self.send_text(f"已加载的插件: {', '.join(plugins)}")
|
await self._send_message(f"已加载的插件: {', '.join(plugins)}")
|
||||||
|
|
||||||
async def _list_registered_plugins(self):
|
async def _list_registered_plugins(self):
|
||||||
plugins = plugin_manage_api.list_registered_plugins()
|
plugins = plugin_manage_api.list_registered_plugins()
|
||||||
await self.send_text(f"已注册的插件: {', '.join(plugins)}")
|
await self._send_message(f"已注册的插件: {', '.join(plugins)}")
|
||||||
|
|
||||||
async def _rescan_plugin_dirs(self):
|
async def _rescan_plugin_dirs(self):
|
||||||
plugin_manage_api.rescan_plugin_directory()
|
plugin_manage_api.rescan_plugin_directory()
|
||||||
await self.send_text("插件目录重新扫描执行中")
|
await self._send_message("插件目录重新扫描执行中")
|
||||||
|
|
||||||
async def _load_plugin(self, plugin_name: str):
|
async def _load_plugin(self, plugin_name: str):
|
||||||
success, count = plugin_manage_api.load_plugin(plugin_name)
|
success, count = plugin_manage_api.load_plugin(plugin_name)
|
||||||
if success:
|
if success:
|
||||||
await self.send_text(f"插件加载成功: {plugin_name}")
|
await self._send_message(f"插件加载成功: {plugin_name}")
|
||||||
else:
|
else:
|
||||||
if count == 0:
|
if count == 0:
|
||||||
await self.send_text(f"插件{plugin_name}为禁用状态")
|
await self._send_message(f"插件{plugin_name}为禁用状态")
|
||||||
await self.send_text(f"插件加载失败: {plugin_name}")
|
await self._send_message(f"插件加载失败: {plugin_name}")
|
||||||
|
|
||||||
async def _unload_plugin(self, plugin_name: str):
|
async def _unload_plugin(self, plugin_name: str):
|
||||||
success = await plugin_manage_api.remove_plugin(plugin_name)
|
success = await plugin_manage_api.remove_plugin(plugin_name)
|
||||||
if success:
|
if success:
|
||||||
await self.send_text(f"插件卸载成功: {plugin_name}")
|
await self._send_message(f"插件卸载成功: {plugin_name}")
|
||||||
else:
|
else:
|
||||||
await self.send_text(f"插件卸载失败: {plugin_name}")
|
await self._send_message(f"插件卸载失败: {plugin_name}")
|
||||||
|
|
||||||
async def _reload_plugin(self, plugin_name: str):
|
async def _reload_plugin(self, plugin_name: str):
|
||||||
success = await plugin_manage_api.reload_plugin(plugin_name)
|
success = await plugin_manage_api.reload_plugin(plugin_name)
|
||||||
if success:
|
if success:
|
||||||
await self.send_text(f"插件重新加载成功: {plugin_name}")
|
await self._send_message(f"插件重新加载成功: {plugin_name}")
|
||||||
else:
|
else:
|
||||||
await self.send_text(f"插件重新加载失败: {plugin_name}")
|
await self._send_message(f"插件重新加载失败: {plugin_name}")
|
||||||
|
|
||||||
async def _add_dir(self, dir_path: str):
|
async def _add_dir(self, dir_path: str):
|
||||||
await self.send_text(f"正在添加插件目录: {dir_path}")
|
await self._send_message(f"正在添加插件目录: {dir_path}")
|
||||||
success = plugin_manage_api.add_plugin_directory(dir_path)
|
success = plugin_manage_api.add_plugin_directory(dir_path)
|
||||||
await asyncio.sleep(0.5) # 防止乱序发送
|
await asyncio.sleep(0.5) # 防止乱序发送
|
||||||
if success:
|
if success:
|
||||||
await self.send_text(f"插件目录添加成功: {dir_path}")
|
await self._send_message(f"插件目录添加成功: {dir_path}")
|
||||||
else:
|
else:
|
||||||
await self.send_text(f"插件目录添加失败: {dir_path}")
|
await self._send_message(f"插件目录添加失败: {dir_path}")
|
||||||
|
|
||||||
def _fetch_all_registered_components(self) -> List[ComponentInfo]:
|
def _fetch_all_registered_components(self) -> List[ComponentInfo]:
|
||||||
all_plugin_info = component_manage_api.get_all_plugin_info()
|
all_plugin_info = component_manage_api.get_all_plugin_info()
|
||||||
@@ -255,29 +263,29 @@ class ManagementCommand(BaseCommand):
|
|||||||
async def _list_all_registered_components(self):
|
async def _list_all_registered_components(self):
|
||||||
components_info = self._fetch_all_registered_components()
|
components_info = self._fetch_all_registered_components()
|
||||||
if not components_info:
|
if not components_info:
|
||||||
await self.send_text("没有注册的组件")
|
await self._send_message("没有注册的组件")
|
||||||
return
|
return
|
||||||
|
|
||||||
all_components_str = ", ".join(
|
all_components_str = ", ".join(
|
||||||
f"{component.name} ({component.component_type})" for component in components_info
|
f"{component.name} ({component.component_type})" for component in components_info
|
||||||
)
|
)
|
||||||
await self.send_text(f"已注册的组件: {all_components_str}")
|
await self._send_message(f"已注册的组件: {all_components_str}")
|
||||||
|
|
||||||
async def _list_enabled_components(self, target_type: str = "global"):
|
async def _list_enabled_components(self, target_type: str = "global"):
|
||||||
components_info = self._fetch_all_registered_components()
|
components_info = self._fetch_all_registered_components()
|
||||||
if not components_info:
|
if not components_info:
|
||||||
await self.send_text("没有注册的组件")
|
await self._send_message("没有注册的组件")
|
||||||
return
|
return
|
||||||
|
|
||||||
if target_type == "global":
|
if target_type == "global":
|
||||||
enabled_components = [component for component in components_info if component.enabled]
|
enabled_components = [component for component in components_info if component.enabled]
|
||||||
if not enabled_components:
|
if not enabled_components:
|
||||||
await self.send_text("没有满足条件的已启用全局组件")
|
await self._send_message("没有满足条件的已启用全局组件")
|
||||||
return
|
return
|
||||||
enabled_components_str = ", ".join(
|
enabled_components_str = ", ".join(
|
||||||
f"{component.name} ({component.component_type})" for component in enabled_components
|
f"{component.name} ({component.component_type})" for component in enabled_components
|
||||||
)
|
)
|
||||||
await self.send_text(f"满足条件的已启用全局组件: {enabled_components_str}")
|
await self._send_message(f"满足条件的已启用全局组件: {enabled_components_str}")
|
||||||
elif target_type == "local":
|
elif target_type == "local":
|
||||||
locally_disabled_components = self._fetch_locally_disabled_components()
|
locally_disabled_components = self._fetch_locally_disabled_components()
|
||||||
enabled_components = [
|
enabled_components = [
|
||||||
@@ -286,28 +294,28 @@ class ManagementCommand(BaseCommand):
|
|||||||
if (component.name not in locally_disabled_components and component.enabled)
|
if (component.name not in locally_disabled_components and component.enabled)
|
||||||
]
|
]
|
||||||
if not enabled_components:
|
if not enabled_components:
|
||||||
await self.send_text("本聊天没有满足条件的已启用组件")
|
await self._send_message("本聊天没有满足条件的已启用组件")
|
||||||
return
|
return
|
||||||
enabled_components_str = ", ".join(
|
enabled_components_str = ", ".join(
|
||||||
f"{component.name} ({component.component_type})" for component in enabled_components
|
f"{component.name} ({component.component_type})" for component in enabled_components
|
||||||
)
|
)
|
||||||
await self.send_text(f"本聊天满足条件的已启用组件: {enabled_components_str}")
|
await self._send_message(f"本聊天满足条件的已启用组件: {enabled_components_str}")
|
||||||
|
|
||||||
async def _list_disabled_components(self, target_type: str = "global"):
|
async def _list_disabled_components(self, target_type: str = "global"):
|
||||||
components_info = self._fetch_all_registered_components()
|
components_info = self._fetch_all_registered_components()
|
||||||
if not components_info:
|
if not components_info:
|
||||||
await self.send_text("没有注册的组件")
|
await self._send_message("没有注册的组件")
|
||||||
return
|
return
|
||||||
|
|
||||||
if target_type == "global":
|
if target_type == "global":
|
||||||
disabled_components = [component for component in components_info if not component.enabled]
|
disabled_components = [component for component in components_info if not component.enabled]
|
||||||
if not disabled_components:
|
if not disabled_components:
|
||||||
await self.send_text("没有满足条件的已禁用全局组件")
|
await self._send_message("没有满足条件的已禁用全局组件")
|
||||||
return
|
return
|
||||||
disabled_components_str = ", ".join(
|
disabled_components_str = ", ".join(
|
||||||
f"{component.name} ({component.component_type})" for component in disabled_components
|
f"{component.name} ({component.component_type})" for component in disabled_components
|
||||||
)
|
)
|
||||||
await self.send_text(f"满足条件的已禁用全局组件: {disabled_components_str}")
|
await self._send_message(f"满足条件的已禁用全局组件: {disabled_components_str}")
|
||||||
elif target_type == "local":
|
elif target_type == "local":
|
||||||
locally_disabled_components = self._fetch_locally_disabled_components()
|
locally_disabled_components = self._fetch_locally_disabled_components()
|
||||||
disabled_components = [
|
disabled_components = [
|
||||||
@@ -316,12 +324,12 @@ class ManagementCommand(BaseCommand):
|
|||||||
if (component.name in locally_disabled_components or not component.enabled)
|
if (component.name in locally_disabled_components or not component.enabled)
|
||||||
]
|
]
|
||||||
if not disabled_components:
|
if not disabled_components:
|
||||||
await self.send_text("本聊天没有满足条件的已禁用组件")
|
await self._send_message("本聊天没有满足条件的已禁用组件")
|
||||||
return
|
return
|
||||||
disabled_components_str = ", ".join(
|
disabled_components_str = ", ".join(
|
||||||
f"{component.name} ({component.component_type})" for component in disabled_components
|
f"{component.name} ({component.component_type})" for component in disabled_components
|
||||||
)
|
)
|
||||||
await self.send_text(f"本聊天满足条件的已禁用组件: {disabled_components_str}")
|
await self._send_message(f"本聊天满足条件的已禁用组件: {disabled_components_str}")
|
||||||
|
|
||||||
async def _list_registered_components_by_type(self, target_type: str):
|
async def _list_registered_components_by_type(self, target_type: str):
|
||||||
match target_type:
|
match target_type:
|
||||||
@@ -332,18 +340,18 @@ class ManagementCommand(BaseCommand):
|
|||||||
case "event_handler":
|
case "event_handler":
|
||||||
component_type = ComponentType.EVENT_HANDLER
|
component_type = ComponentType.EVENT_HANDLER
|
||||||
case _:
|
case _:
|
||||||
await self.send_text(f"未知组件类型: {target_type}")
|
await self._send_message(f"未知组件类型: {target_type}")
|
||||||
return
|
return
|
||||||
|
|
||||||
components_info = component_manage_api.get_components_info_by_type(component_type)
|
components_info = component_manage_api.get_components_info_by_type(component_type)
|
||||||
if not components_info:
|
if not components_info:
|
||||||
await self.send_text(f"没有注册的 {target_type} 组件")
|
await self._send_message(f"没有注册的 {target_type} 组件")
|
||||||
return
|
return
|
||||||
|
|
||||||
components_str = ", ".join(
|
components_str = ", ".join(
|
||||||
f"{name} ({component.component_type})" for name, component in components_info.items()
|
f"{name} ({component.component_type})" for name, component in components_info.items()
|
||||||
)
|
)
|
||||||
await self.send_text(f"注册的 {target_type} 组件: {components_str}")
|
await self._send_message(f"注册的 {target_type} 组件: {components_str}")
|
||||||
|
|
||||||
async def _globally_enable_component(self, component_name: str, component_type: str):
|
async def _globally_enable_component(self, component_name: str, component_type: str):
|
||||||
match component_type:
|
match component_type:
|
||||||
@@ -354,12 +362,12 @@ class ManagementCommand(BaseCommand):
|
|||||||
case "event_handler":
|
case "event_handler":
|
||||||
target_component_type = ComponentType.EVENT_HANDLER
|
target_component_type = ComponentType.EVENT_HANDLER
|
||||||
case _:
|
case _:
|
||||||
await self.send_text(f"未知组件类型: {component_type}")
|
await self._send_message(f"未知组件类型: {component_type}")
|
||||||
return
|
return
|
||||||
if component_manage_api.globally_enable_component(component_name, target_component_type):
|
if component_manage_api.globally_enable_component(component_name, target_component_type):
|
||||||
await self.send_text(f"全局启用组件成功: {component_name}")
|
await self._send_message(f"全局启用组件成功: {component_name}")
|
||||||
else:
|
else:
|
||||||
await self.send_text(f"全局启用组件失败: {component_name}")
|
await self._send_message(f"全局启用组件失败: {component_name}")
|
||||||
|
|
||||||
async def _globally_disable_component(self, component_name: str, component_type: str):
|
async def _globally_disable_component(self, component_name: str, component_type: str):
|
||||||
match component_type:
|
match component_type:
|
||||||
@@ -370,13 +378,13 @@ class ManagementCommand(BaseCommand):
|
|||||||
case "event_handler":
|
case "event_handler":
|
||||||
target_component_type = ComponentType.EVENT_HANDLER
|
target_component_type = ComponentType.EVENT_HANDLER
|
||||||
case _:
|
case _:
|
||||||
await self.send_text(f"未知组件类型: {component_type}")
|
await self._send_message(f"未知组件类型: {component_type}")
|
||||||
return
|
return
|
||||||
success = await component_manage_api.globally_disable_component(component_name, target_component_type)
|
success = await component_manage_api.globally_disable_component(component_name, target_component_type)
|
||||||
if success:
|
if success:
|
||||||
await self.send_text(f"全局禁用组件成功: {component_name}")
|
await self._send_message(f"全局禁用组件成功: {component_name}")
|
||||||
else:
|
else:
|
||||||
await self.send_text(f"全局禁用组件失败: {component_name}")
|
await self._send_message(f"全局禁用组件失败: {component_name}")
|
||||||
|
|
||||||
async def _locally_enable_component(self, component_name: str, component_type: str):
|
async def _locally_enable_component(self, component_name: str, component_type: str):
|
||||||
match component_type:
|
match component_type:
|
||||||
@@ -387,16 +395,16 @@ class ManagementCommand(BaseCommand):
|
|||||||
case "event_handler":
|
case "event_handler":
|
||||||
target_component_type = ComponentType.EVENT_HANDLER
|
target_component_type = ComponentType.EVENT_HANDLER
|
||||||
case _:
|
case _:
|
||||||
await self.send_text(f"未知组件类型: {component_type}")
|
await self._send_message(f"未知组件类型: {component_type}")
|
||||||
return
|
return
|
||||||
if component_manage_api.locally_enable_component(
|
if component_manage_api.locally_enable_component(
|
||||||
component_name,
|
component_name,
|
||||||
target_component_type,
|
target_component_type,
|
||||||
self.message.chat_stream.stream_id,
|
self.message.chat_stream.stream_id,
|
||||||
):
|
):
|
||||||
await self.send_text(f"本地启用组件成功: {component_name}")
|
await self._send_message(f"本地启用组件成功: {component_name}")
|
||||||
else:
|
else:
|
||||||
await self.send_text(f"本地启用组件失败: {component_name}")
|
await self._send_message(f"本地启用组件失败: {component_name}")
|
||||||
|
|
||||||
async def _locally_disable_component(self, component_name: str, component_type: str):
|
async def _locally_disable_component(self, component_name: str, component_type: str):
|
||||||
match component_type:
|
match component_type:
|
||||||
@@ -407,16 +415,19 @@ class ManagementCommand(BaseCommand):
|
|||||||
case "event_handler":
|
case "event_handler":
|
||||||
target_component_type = ComponentType.EVENT_HANDLER
|
target_component_type = ComponentType.EVENT_HANDLER
|
||||||
case _:
|
case _:
|
||||||
await self.send_text(f"未知组件类型: {component_type}")
|
await self._send_message(f"未知组件类型: {component_type}")
|
||||||
return
|
return
|
||||||
if component_manage_api.locally_disable_component(
|
if component_manage_api.locally_disable_component(
|
||||||
component_name,
|
component_name,
|
||||||
target_component_type,
|
target_component_type,
|
||||||
self.message.chat_stream.stream_id,
|
self.message.chat_stream.stream_id,
|
||||||
):
|
):
|
||||||
await self.send_text(f"本地禁用组件成功: {component_name}")
|
await self._send_message(f"本地禁用组件成功: {component_name}")
|
||||||
else:
|
else:
|
||||||
await self.send_text(f"本地禁用组件失败: {component_name}")
|
await self._send_message(f"本地禁用组件失败: {component_name}")
|
||||||
|
|
||||||
|
async def _send_message(self, message: str):
|
||||||
|
await send_api.text_to_stream(message, self.stream_id, typing=False, storage_message=False)
|
||||||
|
|
||||||
|
|
||||||
@register_plugin
|
@register_plugin
|
||||||
@@ -430,7 +441,9 @@ class PluginManagementPlugin(BasePlugin):
|
|||||||
"plugin": {
|
"plugin": {
|
||||||
"enabled": ConfigField(bool, default=False, description="是否启用插件"),
|
"enabled": ConfigField(bool, default=False, description="是否启用插件"),
|
||||||
"config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"),
|
"config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"),
|
||||||
"permission": ConfigField(list, default=[], description="有权限使用插件管理命令的用户列表,请填写字符串形式的用户ID"),
|
"permission": ConfigField(
|
||||||
|
list, default=[], description="有权限使用插件管理命令的用户列表,请填写字符串形式的用户ID"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user