feat:给动作添加了选择器,并添加了新api
This commit is contained in:
@@ -3,3 +3,30 @@
|
||||
"""
|
||||
这是一个测试插件,用于测试图片发送功能
|
||||
"""
|
||||
|
||||
"""豆包图片生成插件
|
||||
|
||||
这是一个基于火山引擎豆包模型的AI图片生成插件。
|
||||
|
||||
功能特性:
|
||||
- 智能LLM判定:根据聊天内容智能判断是否需要生成图片
|
||||
- 高质量图片生成:使用豆包Seed Dream模型生成图片
|
||||
- 结果缓存:避免重复生成相同内容的图片
|
||||
- 配置验证:自动验证和修复配置文件
|
||||
- 参数验证:完整的输入参数验证和错误处理
|
||||
- 多尺寸支持:支持多种图片尺寸生成
|
||||
|
||||
使用场景:
|
||||
- 用户要求画图或生成图片时自动触发
|
||||
- 将文字描述转换为视觉图像
|
||||
- 创意图片和艺术作品生成
|
||||
|
||||
配置文件:src/plugins/doubao_pic/actions/pic_action_config.toml
|
||||
|
||||
配置要求:
|
||||
1. 设置火山引擎API密钥 (volcano_generate_api_key)
|
||||
2. 配置API基础URL (base_url)
|
||||
3. 选择合适的生成模型和参数
|
||||
|
||||
注意:需要有效的火山引擎API访问权限才能正常使用。
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import os
|
||||
import toml
|
||||
from src.common.logger_manager import get_logger
|
||||
|
||||
logger = get_logger("pic_config")
|
||||
|
||||
CONFIG_CONTENT = """\
|
||||
# 火山方舟 API 的基础 URL
|
||||
@@ -18,10 +22,83 @@ default_guidance_scale = 2.5
|
||||
# 默认随机种子
|
||||
default_seed = 42
|
||||
|
||||
# 缓存设置
|
||||
cache_enabled = true
|
||||
cache_max_size = 10
|
||||
|
||||
# 更多插件特定配置可以在此添加...
|
||||
# custom_parameter = "some_value"
|
||||
"""
|
||||
|
||||
# 默认配置字典,用于验证和修复
|
||||
DEFAULT_CONFIG = {
|
||||
"base_url": "https://ark.cn-beijing.volces.com/api/v3",
|
||||
"volcano_generate_api_key": "YOUR_VOLCANO_GENERATE_API_KEY_HERE",
|
||||
"default_model": "doubao-seedream-3-0-t2i-250415",
|
||||
"default_size": "1024x1024",
|
||||
"default_watermark": True,
|
||||
"default_guidance_scale": 2.5,
|
||||
"default_seed": 42,
|
||||
"cache_enabled": True,
|
||||
"cache_max_size": 10
|
||||
}
|
||||
|
||||
|
||||
def validate_and_fix_config(config_path: str) -> bool:
|
||||
"""验证并修复配置文件"""
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
config = toml.load(f)
|
||||
|
||||
# 检查缺失的配置项
|
||||
missing_keys = []
|
||||
fixed = False
|
||||
|
||||
for key, default_value in DEFAULT_CONFIG.items():
|
||||
if key not in config:
|
||||
missing_keys.append(key)
|
||||
config[key] = default_value
|
||||
fixed = True
|
||||
logger.info(f"添加缺失的配置项: {key} = {default_value}")
|
||||
|
||||
# 验证配置值的类型和范围
|
||||
if isinstance(config.get("default_guidance_scale"), (int, float)):
|
||||
if not 0.1 <= config["default_guidance_scale"] <= 20.0:
|
||||
config["default_guidance_scale"] = 2.5
|
||||
fixed = True
|
||||
logger.info("修复无效的 default_guidance_scale 值")
|
||||
|
||||
if isinstance(config.get("default_seed"), (int, float)):
|
||||
config["default_seed"] = int(config["default_seed"])
|
||||
else:
|
||||
config["default_seed"] = 42
|
||||
fixed = True
|
||||
logger.info("修复无效的 default_seed 值")
|
||||
|
||||
if config.get("cache_max_size") and not isinstance(config["cache_max_size"], int):
|
||||
config["cache_max_size"] = 10
|
||||
fixed = True
|
||||
logger.info("修复无效的 cache_max_size 值")
|
||||
|
||||
# 如果有修复,写回文件
|
||||
if fixed:
|
||||
# 创建备份
|
||||
backup_path = config_path + ".backup"
|
||||
if os.path.exists(config_path):
|
||||
os.rename(config_path, backup_path)
|
||||
logger.info(f"已创建配置备份: {backup_path}")
|
||||
|
||||
# 写入修复后的配置
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
toml.dump(config, f)
|
||||
logger.info(f"配置文件已修复: {config_path}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"验证配置文件时出错: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def generate_config():
|
||||
# 获取当前脚本所在的目录
|
||||
@@ -32,13 +109,13 @@ def generate_config():
|
||||
try:
|
||||
with open(config_file_path, "w", encoding="utf-8") as f:
|
||||
f.write(CONFIG_CONTENT)
|
||||
print(f"配置文件已生成: {config_file_path}")
|
||||
print("请记得编辑该文件,填入您的火山引擎API 密钥。")
|
||||
logger.info(f"配置文件已生成: {config_file_path}")
|
||||
logger.info("请记得编辑该文件,填入您的火山引擎API 密钥。")
|
||||
except IOError as e:
|
||||
print(f"错误:无法写入配置文件 {config_file_path}。原因: {e}")
|
||||
# else:
|
||||
# print(f"配置文件已存在: {config_file_path}")
|
||||
# print("未进行任何更改。如果您想重新生成,请先删除或重命名现有文件。")
|
||||
logger.error(f"错误:无法写入配置文件 {config_file_path}。原因: {e}")
|
||||
else:
|
||||
# 验证并修复现有配置
|
||||
validate_and_fix_config(config_file_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -6,6 +6,7 @@ import base64 # 新增:用于Base64编码
|
||||
import traceback # 新增:用于打印堆栈跟踪
|
||||
from typing import Tuple
|
||||
from src.chat.focus_chat.planners.actions.plugin_action import PluginAction, register_action
|
||||
from src.chat.focus_chat.planners.actions.base_action import ActionActivationType
|
||||
from src.common.logger_manager import get_logger
|
||||
from .generate_pic_config import generate_config
|
||||
|
||||
@@ -34,8 +35,49 @@ class PicAction(PluginAction):
|
||||
"当有人要求你生成并发送一张图片时使用",
|
||||
"当有人让你画一张图时使用",
|
||||
]
|
||||
default = False
|
||||
default = True
|
||||
action_config_file_name = "pic_action_config.toml"
|
||||
|
||||
# 激活类型设置 - 使用LLM判定,能更好理解用户意图
|
||||
action_activation_type = ActionActivationType.LLM_JUDGE
|
||||
llm_judge_prompt = """
|
||||
判定是否需要使用图片生成动作的条件:
|
||||
1. 用户明确要求画图、生成图片或创作图像
|
||||
2. 用户描述了想要看到的画面或场景
|
||||
3. 对话中提到需要视觉化展示某些概念
|
||||
4. 用户想要创意图片或艺术作品
|
||||
|
||||
适合使用的情况:
|
||||
- "画一张..."、"画个..."、"生成图片"
|
||||
- "我想看看...的样子"
|
||||
- "能画出...吗"
|
||||
- "创作一幅..."
|
||||
|
||||
绝对不要使用的情况:
|
||||
1. 纯文字聊天和问答
|
||||
2. 只是提到"图片"、"画"等词但不是要求生成
|
||||
3. 谈论已存在的图片或照片
|
||||
4. 技术讨论中提到绘图概念但无生成需求
|
||||
5. 用户明确表示不需要图片时
|
||||
"""
|
||||
|
||||
# 简单的请求缓存,避免短时间内重复请求
|
||||
_request_cache = {}
|
||||
_cache_max_size = 10
|
||||
|
||||
@classmethod
|
||||
def _get_cache_key(cls, description: str, model: str, size: str) -> str:
|
||||
"""生成缓存键"""
|
||||
return f"{description[:100]}|{model}|{size}" # 限制描述长度避免键过长
|
||||
|
||||
@classmethod
|
||||
def _cleanup_cache(cls):
|
||||
"""清理缓存,保持大小在限制内"""
|
||||
if len(cls._request_cache) > cls._cache_max_size:
|
||||
# 简单的FIFO策略,移除最旧的条目
|
||||
keys_to_remove = list(cls._request_cache.keys())[:-cls._cache_max_size//2]
|
||||
for key in keys_to_remove:
|
||||
del cls._request_cache[key]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -66,6 +108,7 @@ class PicAction(PluginAction):
|
||||
"""处理图片生成动作(通过HTTP API)"""
|
||||
logger.info(f"{self.log_prefix} 执行 pic_action (HTTP): {self.reasoning}")
|
||||
|
||||
# 配置验证
|
||||
http_base_url = self.config.get("base_url")
|
||||
http_api_key = self.config.get("volcano_generate_api_key")
|
||||
|
||||
@@ -75,15 +118,51 @@ class PicAction(PluginAction):
|
||||
logger.error(f"{self.log_prefix} HTTP调用配置缺失: base_url 或 volcano_generate_api_key.")
|
||||
return False, "HTTP配置不完整"
|
||||
|
||||
# API密钥验证
|
||||
if http_api_key == "YOUR_VOLCANO_GENERATE_API_KEY_HERE":
|
||||
error_msg = "图片生成功能尚未配置,请设置正确的API密钥。"
|
||||
await self.send_message_by_expressor(error_msg)
|
||||
logger.error(f"{self.log_prefix} API密钥未配置")
|
||||
return False, "API密钥未配置"
|
||||
|
||||
# 参数验证
|
||||
description = self.action_data.get("description")
|
||||
if not description:
|
||||
if not description or not description.strip():
|
||||
logger.warning(f"{self.log_prefix} 图片描述为空,无法生成图片。")
|
||||
await self.send_message_by_expressor("你需要告诉我想要画什么样的图片哦~")
|
||||
await self.send_message_by_expressor("你需要告诉我想要画什么样的图片哦~ 比如说'画一只可爱的小猫'")
|
||||
return False, "图片描述为空"
|
||||
|
||||
# 清理和验证描述
|
||||
description = description.strip()
|
||||
if len(description) > 1000: # 限制描述长度
|
||||
description = description[:1000]
|
||||
logger.info(f"{self.log_prefix} 图片描述过长,已截断")
|
||||
|
||||
# 获取配置
|
||||
default_model = self.config.get("default_model", "doubao-seedream-3-0-t2i-250415")
|
||||
image_size = self.action_data.get("size", self.config.get("default_size", "1024x1024"))
|
||||
|
||||
# 验证图片尺寸格式
|
||||
if not self._validate_image_size(image_size):
|
||||
logger.warning(f"{self.log_prefix} 无效的图片尺寸: {image_size},使用默认值")
|
||||
image_size = "1024x1024"
|
||||
|
||||
# 检查缓存
|
||||
cache_key = self._get_cache_key(description, default_model, image_size)
|
||||
if cache_key in self._request_cache:
|
||||
cached_result = self._request_cache[cache_key]
|
||||
logger.info(f"{self.log_prefix} 使用缓存的图片结果")
|
||||
await self.send_message_by_expressor("我之前画过类似的图片,用之前的结果~")
|
||||
|
||||
# 直接发送缓存的结果
|
||||
send_success = await self.send_message(type="image", data=cached_result)
|
||||
if send_success:
|
||||
await self.send_message_by_expressor("图片表情已发送!")
|
||||
return True, "图片表情已发送(缓存)"
|
||||
else:
|
||||
# 缓存失败,清除这个缓存项并继续正常流程
|
||||
del self._request_cache[cache_key]
|
||||
|
||||
# guidance_scale 现在完全由配置文件控制
|
||||
guidance_scale_input = self.config.get("default_guidance_scale", 2.5) # 默认2.5
|
||||
guidance_scale_val = 2.5 # Fallback default
|
||||
@@ -160,6 +239,10 @@ class PicAction(PluginAction):
|
||||
base64_image_string = encode_result
|
||||
send_success = await self.send_message(type="image", data=base64_image_string)
|
||||
if send_success:
|
||||
# 缓存成功的结果
|
||||
self._request_cache[cache_key] = base64_image_string
|
||||
self._cleanup_cache()
|
||||
|
||||
await self.send_message_by_expressor("图片表情已发送!")
|
||||
return True, "图片表情已发送"
|
||||
else:
|
||||
@@ -267,3 +350,11 @@ class PicAction(PluginAction):
|
||||
logger.error(f"{self.log_prefix} (HTTP) 图片生成时意外错误: {e!r}", exc_info=True)
|
||||
traceback.print_exc()
|
||||
return False, f"图片生成HTTP请求时发生意外错误: {str(e)[:100]}"
|
||||
|
||||
def _validate_image_size(self, image_size: str) -> bool:
|
||||
"""验证图片尺寸格式"""
|
||||
try:
|
||||
width, height = map(int, image_size.split('x'))
|
||||
return 100 <= width <= 10000 and 100 <= height <= 10000
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
# 火山方舟 API 的基础 URL
|
||||
base_url = "https://ark.cn-beijing.volces.com/api/v3"
|
||||
# 用于图片生成的API密钥
|
||||
volcano_generate_api_key = "YOUR_VOLCANO_GENERATE_API_KEY_HERE"
|
||||
# 默认图片生成模型
|
||||
default_model = "doubao-seedream-3-0-t2i-250415"
|
||||
# 默认图片尺寸
|
||||
default_size = "1024x1024"
|
||||
|
||||
|
||||
# 是否默认开启水印
|
||||
default_watermark = true
|
||||
# 默认引导强度
|
||||
default_guidance_scale = 2.5
|
||||
# 默认随机种子
|
||||
default_seed = 42
|
||||
|
||||
# 更多插件特定配置可以在此添加...
|
||||
# custom_parameter = "some_value"
|
||||
cache_enabled = true
|
||||
cache_max_size = 10
|
||||
|
||||
19
src/plugins/doubao_pic/actions/pic_action_config.toml.backup
Normal file
19
src/plugins/doubao_pic/actions/pic_action_config.toml.backup
Normal file
@@ -0,0 +1,19 @@
|
||||
# 火山方舟 API 的基础 URL
|
||||
base_url = "https://ark.cn-beijing.volces.com/api/v3"
|
||||
# 用于图片生成的API密钥
|
||||
volcano_generate_api_key = "YOUR_VOLCANO_GENERATE_API_KEY_HERE"
|
||||
# 默认图片生成模型
|
||||
default_model = "doubao-seedream-3-0-t2i-250415"
|
||||
# 默认图片尺寸
|
||||
default_size = "1024x1024"
|
||||
|
||||
|
||||
# 是否默认开启水印
|
||||
default_watermark = true
|
||||
# 默认引导强度
|
||||
default_guidance_scale = 2.5
|
||||
# 默认随机种子
|
||||
default_seed = 42
|
||||
|
||||
# 更多插件特定配置可以在此添加...
|
||||
# custom_parameter = "some_value"
|
||||
@@ -1,4 +1,21 @@
|
||||
"""测试插件包"""
|
||||
"""禁言插件包
|
||||
|
||||
这是一个群聊管理插件,提供智能禁言功能。
|
||||
|
||||
功能特性:
|
||||
- 智能LLM判定:根据聊天内容智能判断是否需要禁言
|
||||
- 灵活的时长管理:支持自定义禁言时长限制
|
||||
- 模板化消息:支持自定义禁言提示消息
|
||||
- 参数验证:完整的输入参数验证和错误处理
|
||||
- 配置文件支持:所有设置可通过配置文件调整
|
||||
|
||||
使用场景:
|
||||
- 用户发送违规内容时自动判定禁言
|
||||
- 用户主动要求被禁言时执行操作
|
||||
- 管理员通过聊天指令触发禁言动作
|
||||
|
||||
配置文件:src/plugins/mute_plugin/actions/mute_action_config.toml
|
||||
"""
|
||||
|
||||
"""
|
||||
这是一个测试插件
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from src.common.logger_manager import get_logger
|
||||
from src.chat.focus_chat.planners.actions.plugin_action import PluginAction, register_action
|
||||
from src.chat.focus_chat.planners.actions.plugin_action import PluginAction, register_action, ActionActivationType
|
||||
from typing import Tuple
|
||||
|
||||
logger = get_logger("mute_action")
|
||||
@@ -22,9 +22,102 @@ class MuteAction(PluginAction):
|
||||
"当有人发了擦边,或者色情内容时使用",
|
||||
"当有人要求禁言自己时使用",
|
||||
]
|
||||
default = False # 默认动作,是否手动添加到使用集
|
||||
default = True # 默认动作,是否手动添加到使用集
|
||||
associated_types = ["command", "text"]
|
||||
# associated_types = ["text"]
|
||||
action_config_file_name = "mute_action_config.toml"
|
||||
|
||||
# 激活类型设置 - 使用LLM判定,因为禁言是严肃的管理动作,需要谨慎判断
|
||||
action_activation_type = ActionActivationType.LLM_JUDGE
|
||||
llm_judge_prompt = """
|
||||
判定是否需要使用禁言动作的严格条件:
|
||||
|
||||
必须使用禁言的情况:
|
||||
1. 用户发送明显违规内容(色情、暴力、政治敏感等)
|
||||
2. 恶意刷屏或垃圾信息轰炸
|
||||
3. 用户主动明确要求被禁言("禁言我"等)
|
||||
4. 严重违反群规的行为
|
||||
5. 恶意攻击他人或群组管理
|
||||
|
||||
绝对不要使用的情况:
|
||||
1. 正常聊天和讨论,即使话题敏感
|
||||
2. 情绪化表达但无恶意
|
||||
3. 开玩笑或调侃,除非过分
|
||||
4. 单纯的意见分歧或争论
|
||||
5. 轻微的不当言论(应优先提醒)
|
||||
6. 用户只是提到"禁言"词汇但非要求
|
||||
|
||||
注意:禁言是严厉措施,只在明确违规或用户主动要求时使用。
|
||||
宁可保守也不要误判,保护用户的发言权利。
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# 生成配置文件(如果不存在)
|
||||
self._generate_config_if_needed()
|
||||
|
||||
def _generate_config_if_needed(self):
|
||||
"""生成配置文件(如果不存在)"""
|
||||
import os
|
||||
|
||||
# 获取动作文件所在目录
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
config_path = os.path.join(current_dir, "mute_action_config.toml")
|
||||
|
||||
if not os.path.exists(config_path):
|
||||
config_content = """\
|
||||
# 禁言动作配置文件
|
||||
|
||||
# 默认禁言时长限制(秒)
|
||||
min_duration = 60 # 最短禁言时长
|
||||
max_duration = 2592000 # 最长禁言时长(30天)
|
||||
default_duration = 300 # 默认禁言时长(5分钟)
|
||||
|
||||
# 禁言消息模板
|
||||
templates = [
|
||||
"好的,禁言 {target} {duration},理由:{reason}",
|
||||
"收到,对 {target} 执行禁言 {duration},因为{reason}",
|
||||
"明白了,禁言 {target} {duration},原因是{reason}"
|
||||
]
|
||||
|
||||
# 错误消息模板
|
||||
error_messages = [
|
||||
"没有指定禁言对象呢~",
|
||||
"没有指定禁言时长呢~",
|
||||
"禁言时长必须是正数哦~",
|
||||
"禁言时长必须是数字哦~",
|
||||
"找不到 {target} 这个人呢~",
|
||||
"查找用户信息时出现问题~"
|
||||
]
|
||||
|
||||
# 是否启用时长美化显示
|
||||
enable_duration_formatting = true
|
||||
|
||||
# 是否记录禁言历史
|
||||
log_mute_history = true
|
||||
"""
|
||||
try:
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
f.write(config_content)
|
||||
logger.info(f"已生成禁言动作配置文件: {config_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"生成配置文件失败: {e}")
|
||||
|
||||
def _get_duration_limits(self) -> tuple[int, int, int]:
|
||||
"""获取时长限制配置"""
|
||||
min_dur = self.config.get("min_duration", 60)
|
||||
max_dur = self.config.get("max_duration", 2592000)
|
||||
default_dur = self.config.get("default_duration", 300)
|
||||
return min_dur, max_dur, default_dur
|
||||
|
||||
def _get_template_message(self, target: str, duration_str: str, reason: str) -> str:
|
||||
"""获取模板化的禁言消息"""
|
||||
templates = self.config.get("templates", [
|
||||
"好的,禁言 {target} {duration},理由:{reason}"
|
||||
])
|
||||
|
||||
import random
|
||||
template = random.choice(templates)
|
||||
return template.format(target=target, duration=duration_str, reason=reason)
|
||||
|
||||
async def process(self) -> Tuple[bool, str]:
|
||||
"""处理群聊禁言动作"""
|
||||
@@ -35,47 +128,115 @@ class MuteAction(PluginAction):
|
||||
duration = self.action_data.get("duration")
|
||||
reason = self.action_data.get("reason", "违反群规")
|
||||
|
||||
if not target or not duration:
|
||||
error_msg = "禁言参数不完整,需要target和duration"
|
||||
# 参数验证
|
||||
if not target:
|
||||
error_msg = "禁言目标不能为空"
|
||||
logger.error(f"{self.log_prefix} {error_msg}")
|
||||
await self.send_message_by_expressor("没有指定禁言对象呢~")
|
||||
return False, error_msg
|
||||
|
||||
if not duration:
|
||||
error_msg = "禁言时长不能为空"
|
||||
logger.error(f"{self.log_prefix} {error_msg}")
|
||||
await self.send_message_by_expressor("没有指定禁言时长呢~")
|
||||
return False, error_msg
|
||||
|
||||
# 获取时长限制配置
|
||||
min_duration, max_duration, default_duration = self._get_duration_limits()
|
||||
|
||||
# 验证时长格式并转换
|
||||
try:
|
||||
duration_int = int(duration)
|
||||
if duration_int <= 0:
|
||||
error_msg = "禁言时长必须大于0"
|
||||
logger.error(f"{self.log_prefix} {error_msg}")
|
||||
error_templates = self.config.get("error_messages", ["禁言时长必须是正数哦~"])
|
||||
await self.send_message_by_expressor(error_templates[2] if len(error_templates) > 2 else "禁言时长必须是正数哦~")
|
||||
return False, error_msg
|
||||
|
||||
# 限制禁言时长范围
|
||||
if duration_int < min_duration:
|
||||
duration_int = min_duration
|
||||
logger.info(f"{self.log_prefix} 禁言时长过短,调整为{min_duration}秒")
|
||||
elif duration_int > max_duration:
|
||||
duration_int = max_duration
|
||||
logger.info(f"{self.log_prefix} 禁言时长过长,调整为{max_duration}秒")
|
||||
|
||||
except (ValueError, TypeError) as e:
|
||||
error_msg = f"禁言时长格式无效: {duration}"
|
||||
logger.error(f"{self.log_prefix} {error_msg}")
|
||||
error_templates = self.config.get("error_messages", ["禁言时长必须是数字哦~"])
|
||||
await self.send_message_by_expressor(error_templates[3] if len(error_templates) > 3 else "禁言时长必须是数字哦~")
|
||||
return False, error_msg
|
||||
|
||||
# 获取用户ID
|
||||
platform, user_id = await self.get_user_id_by_person_name(target)
|
||||
try:
|
||||
platform, user_id = await self.get_user_id_by_person_name(target)
|
||||
except Exception as e:
|
||||
error_msg = f"查找用户ID时出错: {e}"
|
||||
logger.error(f"{self.log_prefix} {error_msg}")
|
||||
await self.send_message_by_expressor("查找用户信息时出现问题~")
|
||||
return False, error_msg
|
||||
|
||||
if not user_id:
|
||||
error_msg = f"未找到用户 {target} 的ID"
|
||||
await self.send_message_by_expressor(f"压根没 {target} 这个人")
|
||||
await self.send_message_by_expressor(f"找不到 {target} 这个人呢~")
|
||||
logger.error(f"{self.log_prefix} {error_msg}")
|
||||
return False, error_msg
|
||||
|
||||
# 发送表达情绪的消息
|
||||
await self.send_message_by_expressor(f"禁言{target} {duration}秒,因为{reason}")
|
||||
enable_formatting = self.config.get("enable_duration_formatting", True)
|
||||
time_str = self._format_duration(duration_int) if enable_formatting else f"{duration_int}秒"
|
||||
|
||||
# 使用模板化消息
|
||||
message = self._get_template_message(target, time_str, reason)
|
||||
await self.send_message_by_expressor(message)
|
||||
|
||||
try:
|
||||
# 确保duration是字符串类型
|
||||
if int(duration) < 60:
|
||||
duration = 60
|
||||
if int(duration) > 3600 * 24 * 30:
|
||||
duration = 3600 * 24 * 30
|
||||
duration_str = str(int(duration))
|
||||
duration_str = str(duration_int)
|
||||
|
||||
# 发送群聊禁言命令,按照新格式
|
||||
await self.send_message(
|
||||
type="command",
|
||||
data={"name": "GROUP_BAN", "args": {"qq_id": str(user_id), "duration": duration_str}},
|
||||
display_message=f"尝试禁言了 {target} {duration_str}秒",
|
||||
display_message=f"尝试禁言了 {target} {time_str}",
|
||||
)
|
||||
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=f"你尝试禁言了 {target} {duration_str}秒",
|
||||
action_prompt_display=f"你尝试禁言了 {target} {time_str},理由:{reason}",
|
||||
)
|
||||
|
||||
logger.info(f"{self.log_prefix} 成功发送禁言命令,用户 {target}({user_id}),时长 {duration} 秒")
|
||||
return True, f"成功禁言 {target},时长 {duration} 秒"
|
||||
logger.info(f"{self.log_prefix} 成功发送禁言命令,用户 {target}({user_id}),时长 {duration_int} 秒")
|
||||
return True, f"成功禁言 {target},时长 {time_str}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 执行禁言动作时出错: {e}")
|
||||
await self.send_message_by_expressor(f"执行禁言动作时出错: {e}")
|
||||
return False, f"执行禁言动作时出错: {e}"
|
||||
|
||||
def _format_duration(self, seconds: int) -> str:
|
||||
"""将秒数格式化为可读的时间字符串"""
|
||||
if seconds < 60:
|
||||
return f"{seconds}秒"
|
||||
elif seconds < 3600:
|
||||
minutes = seconds // 60
|
||||
remaining_seconds = seconds % 60
|
||||
if remaining_seconds > 0:
|
||||
return f"{minutes}分{remaining_seconds}秒"
|
||||
else:
|
||||
return f"{minutes}分钟"
|
||||
elif seconds < 86400:
|
||||
hours = seconds // 3600
|
||||
remaining_minutes = (seconds % 3600) // 60
|
||||
if remaining_minutes > 0:
|
||||
return f"{hours}小时{remaining_minutes}分钟"
|
||||
else:
|
||||
return f"{hours}小时"
|
||||
else:
|
||||
days = seconds // 86400
|
||||
remaining_hours = (seconds % 86400) // 3600
|
||||
if remaining_hours > 0:
|
||||
return f"{days}天{remaining_hours}小时"
|
||||
else:
|
||||
return f"{days}天"
|
||||
|
||||
29
src/plugins/mute_plugin/actions/mute_action_config.toml
Normal file
29
src/plugins/mute_plugin/actions/mute_action_config.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
# 禁言动作配置文件
|
||||
|
||||
# 默认禁言时长限制(秒)
|
||||
min_duration = 60 # 最短禁言时长
|
||||
max_duration = 2592000 # 最长禁言时长(30天)
|
||||
default_duration = 300 # 默认禁言时长(5分钟)
|
||||
|
||||
# 禁言消息模板
|
||||
templates = [
|
||||
"好的,禁言 {target} {duration},理由:{reason}",
|
||||
"收到,对 {target} 执行禁言 {duration},因为{reason}",
|
||||
"明白了,禁言 {target} {duration},原因是{reason}"
|
||||
]
|
||||
|
||||
# 错误消息模板
|
||||
error_messages = [
|
||||
"没有指定禁言对象呢~",
|
||||
"没有指定禁言时长呢~",
|
||||
"禁言时长必须是正数哦~",
|
||||
"禁言时长必须是数字哦~",
|
||||
"找不到 {target} 这个人呢~",
|
||||
"查找用户信息时出现问题~"
|
||||
]
|
||||
|
||||
# 是否启用时长美化显示
|
||||
enable_duration_formatting = true
|
||||
|
||||
# 是否记录禁言历史
|
||||
log_mute_history = true
|
||||
@@ -1,5 +1,5 @@
|
||||
from src.common.logger_manager import get_logger
|
||||
from src.chat.focus_chat.planners.actions.plugin_action import PluginAction, register_action
|
||||
from src.chat.focus_chat.planners.actions.plugin_action import PluginAction, register_action, ActionActivationType
|
||||
from typing import Tuple
|
||||
|
||||
logger = get_logger("vtb_action")
|
||||
@@ -22,6 +22,22 @@ class VTBAction(PluginAction):
|
||||
]
|
||||
default = True # 设为默认动作
|
||||
associated_types = ["vtb_text"]
|
||||
|
||||
# 激活类型设置 - 使用LLM判定,因为需要根据情感表达需求判断
|
||||
action_activation_type = ActionActivationType.LLM_JUDGE
|
||||
llm_judge_prompt = """
|
||||
判定是否需要使用VTB虚拟主播动作的条件:
|
||||
1. 当前聊天内容涉及明显的情感表达需求
|
||||
2. 用户询问或讨论情感相关话题
|
||||
3. 场景需要生动的情感回应
|
||||
4. 当前回复内容可以通过VTB动作增强表达效果
|
||||
|
||||
不需要使用的情况:
|
||||
1. 纯粹的信息查询
|
||||
2. 技术性问题讨论
|
||||
3. 不涉及情感的日常对话
|
||||
4. 已经有足够的情感表达
|
||||
"""
|
||||
|
||||
async def process(self) -> Tuple[bool, str]:
|
||||
"""处理VTB虚拟主播动作"""
|
||||
|
||||
Reference in New Issue
Block a user