Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions astrbot/core/platform/sources/misskey/misskey_adapter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import asyncio
import random
import json
from typing import Dict, Any, Optional, Awaitable, List

from astrbot.api import logger
Expand Down Expand Up @@ -270,10 +269,10 @@ async def _start_websocket_connection(self):

async def _handle_notification(self, data: Dict[str, Any]):
try:
notification_type = data.get("type")
logger.debug(
f"[Misskey] 收到通知事件:\n{json.dumps(data, indent=2, ensure_ascii=False)}"
f"[Misskey] 收到通知事件: type={notification_type}, user_id={data.get('userId', 'unknown')}"
)
notification_type = data.get("type")
if notification_type in ["mention", "reply", "quote"]:
note = data.get("note")
if note and self._is_bot_mentioned(note):
Expand All @@ -294,17 +293,16 @@ async def _handle_notification(self, data: Dict[str, Any]):

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(
Expand All @@ -329,8 +327,9 @@ async def _handle_chat_message(self, data: Dict[str, Any]):
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:
Expand Down Expand Up @@ -365,7 +364,18 @@ async def send_by_session(
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)

# 检查是否有文件组件
Expand Down Expand Up @@ -560,6 +570,10 @@ async def _upload_comp(comp) -> Optional[object]:
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,
Expand All @@ -575,12 +589,16 @@ async def _upload_comp(comp) -> Optional[object]:
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"],
Expand Down
4 changes: 0 additions & 4 deletions astrbot/core/platform/sources/misskey/misskey_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,6 @@ def _build_channel_summary(message_type: Optional[str], body: Any) -> str:
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")
Expand Down
30 changes: 19 additions & 11 deletions astrbot/core/platform/sources/misskey/misskey_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ def process_component(component):
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:
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down