feat(ai_auto_response): add admin-controlled persona switching
This commit is contained in:
@@ -131,6 +131,10 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
self.cooldown = CooldownManager(self.cooldown_config)
|
||||
self.image_config = self._config.get("image", {}) or {}
|
||||
self.spam_config = self._config.get("spam_guard", {}) or {}
|
||||
try:
|
||||
self.redis_client = self.db_manager.get_redis_connection() if self.db_manager else None
|
||||
except Exception:
|
||||
self.redis_client = None
|
||||
self._synced_member_context_versions: Dict[str, str] = {}
|
||||
self.log_debug = bool((self._config.get("logging", {}) or {}).get("debug", True))
|
||||
self.LOG.debug(f"[{self.name}] 初始化完成")
|
||||
@@ -164,6 +168,8 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
content = self._normalize_content(message)
|
||||
if not content:
|
||||
return False
|
||||
if self._parse_persona_command(content):
|
||||
return True
|
||||
if should_ignore(content, self.filters):
|
||||
return False
|
||||
if is_targeting_other_user(message):
|
||||
@@ -188,6 +194,10 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
)
|
||||
return False, "duplicate_message"
|
||||
try:
|
||||
command = self._parse_persona_command(content)
|
||||
if command:
|
||||
handled = await self._handle_persona_command(message, command)
|
||||
return False, handled
|
||||
if is_prompt_attack(content):
|
||||
self._log_event(
|
||||
"skip",
|
||||
@@ -241,6 +251,7 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
name_map=group_name_map,
|
||||
)
|
||||
group_profile = group_memory_bundle.get("group_profile", {}) or {}
|
||||
group_profile = self._apply_persona_override(room_id, group_profile)
|
||||
social_context = group_memory_bundle.get("social_context", {}) or {"items": [], "prompt": ""}
|
||||
group_facts = group_memory_bundle.get("group_facts", {}) or {"items": [], "prompt": ""}
|
||||
self._log_event(
|
||||
@@ -539,6 +550,104 @@ class AIAutoResponsePlugin(MessagePluginInterface):
|
||||
if len(items) > size:
|
||||
self.group_messages[room_id] = items[-size:]
|
||||
|
||||
@staticmethod
|
||||
def _parse_persona_command(content: str) -> Dict[str, str] | None:
|
||||
text = str(content or "").strip()
|
||||
if not text.startswith("#"):
|
||||
return None
|
||||
if text in {"#人格列表", "#人格", "#personas"}:
|
||||
return {"type": "list"}
|
||||
if text in {"#当前人格", "#人格状态", "#persona"}:
|
||||
return {"type": "current"}
|
||||
if text.startswith("#切换人格"):
|
||||
target = text[len("#切换人格"):].strip()
|
||||
if target:
|
||||
return {"type": "switch", "target": target}
|
||||
return {"type": "switch", "target": ""}
|
||||
return None
|
||||
|
||||
async def _handle_persona_command(self, message: Dict[str, Any], command: Dict[str, str]) -> str:
|
||||
room_id = str(message.get("roomid", "") or "")
|
||||
sender = str(message.get("sender", "") or "")
|
||||
bot: WechatAPIClient = message.get("bot")
|
||||
command_type = str(command.get("type", "") or "")
|
||||
if command_type == "list":
|
||||
items = []
|
||||
for preset in self.persona_engine.list_personas():
|
||||
aliases = " / ".join((preset.get("aliases", []) or [])[:3])
|
||||
line = f"{preset.get('name')}({preset.get('id')})"
|
||||
if aliases:
|
||||
line += f" - {aliases}"
|
||||
items.append(line)
|
||||
text = "可用人格:\n" + "\n".join(f"- {item}" for item in items)
|
||||
await bot.send_text_message(room_id, text, sender)
|
||||
return "persona_list"
|
||||
current_id = self._get_room_persona_id(room_id) or self.persona_engine.default_persona_id
|
||||
current_preset = self.persona_engine.presets.get(current_id, {})
|
||||
if command_type == "current":
|
||||
await bot.send_text_message(
|
||||
room_id,
|
||||
f"当前人格:{current_preset.get('name', current_id)}({current_id})",
|
||||
sender,
|
||||
)
|
||||
return "persona_current"
|
||||
if command_type == "switch":
|
||||
if not GroupBotManager.is_admin(sender):
|
||||
await bot.send_text_message(room_id, "只有管理员才能切换人格。", sender)
|
||||
self._log_event(
|
||||
"skip",
|
||||
room_id=room_id,
|
||||
sender=sender,
|
||||
reason="persona_switch_no_permission",
|
||||
trigger_type="persona_command",
|
||||
reply_mode="admin_guard",
|
||||
)
|
||||
return "persona_switch_no_permission"
|
||||
target = str(command.get("target", "") or "").strip()
|
||||
if not target:
|
||||
await bot.send_text_message(room_id, "写法:#切换人格 于谦", sender)
|
||||
return "persona_switch_missing"
|
||||
target_id = self.persona_engine.resolve_persona_id(target)
|
||||
if not target_id:
|
||||
await bot.send_text_message(room_id, f"没找到这个人格:{target},先发 #人格列表 看看。", sender)
|
||||
return "persona_switch_invalid"
|
||||
self._set_room_persona_id(room_id, target_id)
|
||||
target_preset = self.persona_engine.presets.get(target_id, {})
|
||||
await bot.send_text_message(
|
||||
room_id,
|
||||
f"已切到 {target_preset.get('name', target_id)}({target_id})",
|
||||
sender,
|
||||
)
|
||||
return "persona_switch"
|
||||
return "persona_unknown"
|
||||
|
||||
def _persona_redis_key(self, room_id: str) -> str:
|
||||
return f"ai_auto_response:persona:{room_id}"
|
||||
|
||||
def _get_room_persona_id(self, room_id: str) -> str:
|
||||
if not room_id or not self.redis_client:
|
||||
return ""
|
||||
try:
|
||||
value = self.redis_client.get(self._persona_redis_key(room_id))
|
||||
return str(value or "").strip()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _set_room_persona_id(self, room_id: str, persona_id: str) -> bool:
|
||||
if not room_id or not persona_id or not self.redis_client:
|
||||
return False
|
||||
try:
|
||||
return bool(self.redis_client.set(self._persona_redis_key(room_id), persona_id))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _apply_persona_override(self, room_id: str, group_profile: Dict) -> Dict:
|
||||
profile = dict(group_profile or {})
|
||||
persona_id = self._get_room_persona_id(room_id)
|
||||
if persona_id and persona_id in self.persona_engine.presets:
|
||||
profile["persona_id"] = persona_id
|
||||
return profile
|
||||
|
||||
def _build_message_key(self, message: Dict[str, Any], content: str) -> str:
|
||||
full_msg = message.get("full_wx_msg")
|
||||
if full_msg is not None:
|
||||
|
||||
Reference in New Issue
Block a user