Skip to content
Open
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
43 changes: 37 additions & 6 deletions astrbot/core/platform/sources/lark/lark_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,19 @@ async def send_by_session(
if session.message_type == MessageType.GROUP_MESSAGE:
id_type = "chat_id"
receive_id = session.session_id
if "%" in receive_id:
receive_id = receive_id.split("%")[1]
# 从 session_id 中提取真实的 chat_id,仅去除已知后缀(%thread% / %root%)
for _suffix in ("%thread%", "%root%"):
if _suffix in receive_id:
receive_id = receive_id.split(_suffix)[0]
break
else:
id_type = "open_id"
receive_id = session.session_id
# 单聊也需要去除已知后缀(%thread% / %root%)
for _suffix in ("%thread%", "%root%"):
if _suffix in receive_id:
receive_id = receive_id.split(_suffix)[0]
break

# 复用 LarkMessageEvent 中的通用发送逻辑
await LarkMessageEvent.send_message_chain(
Expand Down Expand Up @@ -580,20 +588,43 @@ async def convert_msg(self, event: lark.im.v1.P2ImMessageReceiveV1) -> None:
user_id=event.event.sender.sender_id.open_id,
nickname=event.event.sender.sender_id.open_id[:8],
)
# 构建 session_id:按话题/回复链隔离上下文
if abm.type == MessageType.GROUP_MESSAGE:
abm.session_id = abm.group_id
base_id = abm.group_id or ""
if message.thread_id:
# 话题群中的消息,按 thread_id 隔离
abm.session_id = f"{base_id}%thread%{message.thread_id}"
elif message.root_id:
# 群聊中的回复链,按 root_id 隔离
abm.session_id = f"{base_id}%root%{message.root_id}"
else:
abm.session_id = base_id
else:
abm.session_id = abm.sender.user_id
base_id = abm.sender.user_id
if message.thread_id:
abm.session_id = f"{base_id}%thread%{message.thread_id}"
elif message.root_id:
# 单聊中的回复链,按 root_id 隔离
abm.session_id = f"{base_id}%root%{message.root_id}"
else:
abm.session_id = base_id

await self.handle_msg(abm)
# 判断是否需要通过 reply_in_thread 创建新话题
# 没有已存在的 thread_id 时,需要 reply_in_thread=True 创建话题
# 已在话题中的消息回复自然在话题内,无需 reply_in_thread
_should_reply_in_thread = not bool(message.thread_id)
await self.handle_msg(abm, should_reply_in_thread=_should_reply_in_thread)

async def handle_msg(self, abm: AstrBotMessage) -> None:
async def handle_msg(
self, abm: AstrBotMessage, should_reply_in_thread: bool = False
) -> None:
event = LarkMessageEvent(
message_str=abm.message_str,
message_obj=abm,
platform_meta=self.meta(),
session_id=abm.session_id,
bot=self.lark_api,
should_reply_in_thread=should_reply_in_thread,
)

self._event_queue.put_nowait(event)
Expand Down
44 changes: 40 additions & 4 deletions astrbot/core/platform/sources/lark/lark_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ def __init__(
platform_meta,
session_id,
bot: lark.Client,
should_reply_in_thread: bool = False,
) -> None:
super().__init__(message_str, message_obj, platform_meta, session_id)
self.bot = bot
self.should_reply_in_thread = should_reply_in_thread

@staticmethod
async def _send_im_message(
Expand All @@ -61,6 +63,7 @@ async def _send_im_message(
reply_message_id: str | None = None,
receive_id: str | None = None,
receive_id_type: str | None = None,
reply_in_thread: bool = False,
) -> bool:
"""发送飞书 IM 消息的通用辅助函数

Expand All @@ -71,6 +74,7 @@ async def _send_im_message(
reply_message_id: 回复的消息ID(用于回复消息)
receive_id: 接收者ID(用于主动发送)
receive_id_type: 接收者ID类型(用于主动发送)
reply_in_thread: 是否在话题中回复

Returns:
是否发送成功
Expand All @@ -88,7 +92,7 @@ async def _send_im_message(
.content(content)
.msg_type(msg_type)
.uuid(str(uuid.uuid4()))
.reply_in_thread(False)
.reply_in_thread(reply_in_thread)
.build()
)
.build()
Expand Down Expand Up @@ -287,6 +291,7 @@ async def send_message_chain(
reply_message_id: str | None = None,
receive_id: str | None = None,
receive_id_type: str | None = None,
reply_in_thread: bool = False,
) -> None:
"""通用的消息链发送方法

Expand All @@ -296,6 +301,7 @@ async def send_message_chain(
reply_message_id: 回复的消息ID(用于回复消息)
receive_id: 接收者ID(用于主动发送)
receive_id_type: 接收者ID类型,如 'open_id', 'chat_id'(用于主动发送)
reply_in_thread: 是否在话题中回复
"""
if lark_client.im is None:
logger.error("[Lark] API Client im 模块未初始化")
Expand Down Expand Up @@ -337,22 +343,38 @@ async def send_message_chain(
reply_message_id=reply_message_id,
receive_id=receive_id,
receive_id_type=receive_id_type,
reply_in_thread=reply_in_thread,
)

# 发送附件
for file_comp in file_components:
await LarkMessageEvent._send_file_message(
file_comp, lark_client, reply_message_id, receive_id, receive_id_type
file_comp,
lark_client,
reply_message_id,
receive_id,
receive_id_type,
reply_in_thread=reply_in_thread,
)

for audio_comp in audio_components:
await LarkMessageEvent._send_audio_message(
audio_comp, lark_client, reply_message_id, receive_id, receive_id_type
audio_comp,
lark_client,
reply_message_id,
receive_id,
receive_id_type,
reply_in_thread=reply_in_thread,
)

for media_comp in media_components:
await LarkMessageEvent._send_media_message(
media_comp, lark_client, reply_message_id, receive_id, receive_id_type
media_comp,
lark_client,
reply_message_id,
receive_id,
receive_id_type,
reply_in_thread=reply_in_thread,
)

async def send(self, message: MessageChain) -> None:
Expand All @@ -361,6 +383,7 @@ async def send(self, message: MessageChain) -> None:
message,
self.bot,
reply_message_id=self.message_obj.message_id,
reply_in_thread=self.should_reply_in_thread,
)
await super().send(message)

Expand All @@ -371,6 +394,7 @@ async def _send_file_message(
reply_message_id: str | None = None,
receive_id: str | None = None,
receive_id_type: str | None = None,
reply_in_thread: bool = False,
) -> None:
"""发送文件消息

Expand All @@ -396,6 +420,7 @@ async def _send_file_message(
reply_message_id=reply_message_id,
receive_id=receive_id,
receive_id_type=receive_id_type,
reply_in_thread=reply_in_thread,
)

@staticmethod
Expand All @@ -405,6 +430,7 @@ async def _send_audio_message(
reply_message_id: str | None = None,
receive_id: str | None = None,
receive_id_type: str | None = None,
reply_in_thread: bool = False,
) -> None:
"""发送音频消息

Expand Down Expand Up @@ -469,6 +495,7 @@ async def _send_audio_message(
reply_message_id=reply_message_id,
receive_id=receive_id,
receive_id_type=receive_id_type,
reply_in_thread=reply_in_thread,
)

@staticmethod
Expand All @@ -478,6 +505,7 @@ async def _send_media_message(
reply_message_id: str | None = None,
receive_id: str | None = None,
receive_id_type: str | None = None,
reply_in_thread: bool = False,
) -> None:
"""发送视频消息

Expand Down Expand Up @@ -542,6 +570,7 @@ async def _send_media_message(
reply_message_id=reply_message_id,
receive_id=receive_id,
receive_id_type=receive_id_type,
reply_in_thread=reply_in_thread,
)

async def react(self, emoji: str) -> None:
Expand Down Expand Up @@ -633,6 +662,7 @@ async def _send_card_message(
reply_message_id: str | None = None,
receive_id: str | None = None,
receive_id_type: str | None = None,
reply_in_thread: bool = False,
) -> bool:
"""将卡片实体作为 interactive 消息发送。"""
content = json.dumps(
Expand All @@ -646,6 +676,7 @@ async def _send_card_message(
reply_message_id=reply_message_id,
receive_id=receive_id,
receive_id_type=receive_id_type,
reply_in_thread=reply_in_thread,
)

async def _update_streaming_text(
Expand Down Expand Up @@ -747,6 +778,10 @@ async def send_streaming(self, generator, use_fallback: bool = False):
使用解耦发送循环,LLM token 到达时只更新 buffer 并唤醒发送协程,
发送频率由网络 RTT 自然限流。
"""
# 非话题消息:通过 reply_in_thread=True 创建新话题,同时使用流式卡片
if self.should_reply_in_thread:
logger.info("[Lark] 非话题消息,将通过 reply_in_thread=True 创建新话题")

# Step 1: 创建流式卡片实体
card_id = await self._create_streaming_card()
if not card_id:
Expand All @@ -758,6 +793,7 @@ async def send_streaming(self, generator, use_fallback: bool = False):
sent = await self._send_card_message(
card_id,
reply_message_id=self.message_obj.message_id,
reply_in_thread=self.should_reply_in_thread,
)
if not sent:
logger.error("[Lark] 发送流式卡片消息失败,回退到非流式发送")
Expand Down