feat: 优化 Misskey 适配器的通知和聊天消息处理,改进 @用户提及逻辑 (#3075)

This commit is contained in:
PaloMiku
2025-10-19 20:05:55 +08:00
committed by GitHub
parent dc71c04b67
commit 8b0d4d4de4
3 changed files with 48 additions and 26 deletions
@@ -1,6 +1,5 @@
import asyncio
import random
import json
from typing import Dict, Any, Optional, Awaitable, List
from astrbot.api import logger
@@ -270,10 +269,10 @@ class MisskeyPlatformAdapter(Platform):
async def _handle_notification(self, data: Dict[str, Any]):
try:
logger.debug(
f"[Misskey] 收到通知事件:\n{json.dumps(data, indent=2, ensure_ascii=False)}"
)
notification_type = data.get("type")
logger.debug(
f"[Misskey] 收到通知事件: type={notification_type}, user_id={data.get('userId', 'unknown')}"
)
if notification_type in ["mention", "reply", "quote"]:
note = data.get("note")
if note and self._is_bot_mentioned(note):
@@ -294,17 +293,16 @@ class MisskeyPlatformAdapter(Platform):
async def _handle_chat_message(self, data: Dict[str, Any]):
try:
logger.debug(
f"[Misskey] 收到聊天事件数据:\n{json.dumps(data, indent=2, ensure_ascii=False)}"
)
sender_id = str(
data.get("fromUserId", "") or data.get("fromUser", {}).get("id", "")
)
room_id = data.get("toRoomId")
logger.debug(
f"[Misskey] 收到聊天事件: sender_id={sender_id}, room_id={room_id}, is_self={sender_id == self.client_self_id}"
)
if sender_id == self.client_self_id:
return
room_id = data.get("toRoomId")
if room_id:
raw_text = data.get("text", "")
logger.debug(
@@ -329,8 +327,9 @@ class MisskeyPlatformAdapter(Platform):
logger.error(f"[Misskey] 处理聊天消息失败: {e}")
async def _debug_handler(self, data: Dict[str, Any]):
event_type = data.get("type", "unknown")
logger.debug(
f"[Misskey] 收到未处理事件:\n{json.dumps(data, indent=2, ensure_ascii=False)}"
f"[Misskey] 收到未处理事件: type={event_type}, channel={data.get('channel', 'unknown')}"
)
def _is_bot_mentioned(self, note: Dict[str, Any]) -> bool:
@@ -365,7 +364,18 @@ class MisskeyPlatformAdapter(Platform):
text, has_at_user = serialize_message_chain(message_chain.chain)
if not has_at_user and session_id:
user_info = self._user_cache.get(session_id)
# 从session_id中提取用户ID用于缓存查询
# session_id格式为: "chat%<user_id>" 或 "room%<room_id>" 或 "note%<user_id>"
user_id_for_cache = None
if "%" in session_id:
parts = session_id.split("%")
if len(parts) >= 2:
user_id_for_cache = parts[1]
user_info = None
if user_id_for_cache:
user_info = self._user_cache.get(user_id_for_cache)
text = add_at_mention_if_needed(text, user_info, has_at_user)
# 检查是否有文件组件
@@ -560,6 +570,10 @@ class MisskeyPlatformAdapter(Platform):
user_id_for_cache = (
session_id.split("%")[1] if "%" in session_id else session_id
)
# 获取用户缓存信息(包含reply_to_note_id
user_info_for_reply = self._user_cache.get(user_id_for_cache, {})
visibility, visible_user_ids = resolve_message_visibility(
user_id=user_id_for_cache,
user_cache=self._user_cache,
@@ -575,12 +589,16 @@ class MisskeyPlatformAdapter(Platform):
appended = "\n" + "\n".join(fallback_urls)
text = (text or "") + appended
# 从缓存中获取原消息ID作为reply_id
reply_id = user_info_for_reply.get("reply_to_note_id")
await self.api.create_note(
text=text,
visibility=visibility,
visible_user_ids=visible_user_ids,
file_ids=file_ids or None,
local_only=self.local_only,
reply_id=reply_id, # 添加reply_id参数
cw=fields["cw"],
poll=fields["poll"],
renote_id=fields["renote_id"],
@@ -222,10 +222,6 @@ class StreamingClient:
channel_summary = _build_channel_summary(message_type, body)
logger.info(channel_summary)
logger.debug(
f"[Misskey WebSocket] 收到完整消息: {json.dumps(data, indent=2, ensure_ascii=False)}"
)
if message_type == "channel":
channel_id = body.get("id")
event_type = body.get("type")
@@ -84,7 +84,12 @@ def serialize_message_chain(chain: List[Any]) -> Tuple[str, bool]:
return "[图片]"
elif isinstance(component, Comp.At):
has_at = True
return f"@{component.qq}"
# 优先使用name字段(用户名),如果没有则使用qq字段
# 这样可以避免在Misskey中生成 @<user_id> 这样的无效提及
if hasattr(component, "name") and component.name:
return f"@{component.name}"
else:
return f"@{component.qq}"
elif hasattr(component, "text"):
text = getattr(component, "text", "")
if "@" in text:
@@ -233,21 +238,22 @@ def extract_room_id_from_session_id(session_id: str) -> str:
def add_at_mention_if_needed(
text: str, user_info: Optional[Dict[str, Any]], has_at: bool = False
) -> str:
"""如果需要且没有@用户,则添加@用户"""
"""如果需要且没有@用户,则添加@用户
注意:仅在有有效的username时才添加@提及,避免使用用户ID
"""
if has_at or not user_info:
return text
username = user_info.get("username")
nickname = user_info.get("nickname")
# 如果没有username,则不添加@提及,返回原文本
# 这样可以避免生成 @<user_id> 这样的无效提及
if not username:
return text
if username:
mention = f"@{username}"
if not text.startswith(mention):
text = f"{mention}\n{text}".strip()
elif nickname:
mention = f"@{nickname}"
if not text.startswith(mention):
text = f"{mention}\n{text}".strip()
mention = f"@{username}"
if not text.startswith(mention):
text = f"{mention}\n{text}".strip()
return text
@@ -403,6 +409,8 @@ def cache_user_info(
"nickname": sender_info["nickname"],
"visibility": raw_data.get("visibility", "public"),
"visible_user_ids": raw_data.get("visibleUserIds", []),
# 保存原消息ID,用于回复时作为reply_id
"reply_to_note_id": raw_data.get("id"),
}
user_cache[sender_info["sender_id"]] = user_cache_data