mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-05-13 07:26:45 +00:00
fix(feishu): handle more IM websocket events
This commit is contained in:
@@ -318,5 +318,5 @@ class FeishuModule(_ModuleBase, _MessageBase[Feishu]):
|
||||
client = self.get_instance(client_config.name)
|
||||
if not client:
|
||||
return False
|
||||
sequence = int(stream_meta.get("sequence") or 1) + 1
|
||||
sequence = int(stream_meta.get("sequence") or 0) + 1
|
||||
return client.close_streaming_card(card_id=card_id, sequence=sequence)
|
||||
|
||||
@@ -32,7 +32,11 @@ from lark_oapi.api.im.v1 import (
|
||||
GetMessageResourceRequest,
|
||||
PatchMessageRequest,
|
||||
PatchMessageRequestBody,
|
||||
P2ImChatAccessEventBotP2pChatEnteredV1,
|
||||
P2ImMessageMessageReadV1,
|
||||
P2ImMessageReactionCreatedV1,
|
||||
P2ImMessageReactionDeletedV1,
|
||||
P2ImMessageRecalledV1,
|
||||
P2ImMessageReceiveV1,
|
||||
ReplyMessageRequest,
|
||||
ReplyMessageRequestBody,
|
||||
@@ -119,6 +123,10 @@ class Feishu:
|
||||
)
|
||||
builder.register_p2_im_message_receive_v1(self._on_message)
|
||||
builder.register_p2_im_message_message_read_v1(self._on_message_read)
|
||||
builder.register_p2_im_message_reaction_created_v1(self._on_message_reaction_created)
|
||||
builder.register_p2_im_message_reaction_deleted_v1(self._on_message_reaction_deleted)
|
||||
builder.register_p2_im_message_recalled_v1(self._on_message_recalled)
|
||||
builder.register_p2_im_chat_access_event_bot_p2p_chat_entered_v1(self._on_bot_p2p_chat_entered)
|
||||
builder.register_p2_card_action_trigger(self._on_card_action)
|
||||
return builder.build()
|
||||
|
||||
@@ -359,6 +367,54 @@ class Feishu:
|
||||
len(getattr(event, "message_id_list", None) or []),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _on_message_reaction_created(data: P2ImMessageReactionCreatedV1) -> None:
|
||||
"""忽略消息表情新增事件,避免长连接打印未注册处理器错误。"""
|
||||
event = getattr(data, "event", None)
|
||||
operator = getattr(event, "operator", None)
|
||||
reaction = getattr(event, "reaction", None)
|
||||
logger.debug(
|
||||
"收到飞书消息表情新增事件:message_id=%s, user=%s, emoji=%s",
|
||||
getattr(event, "message_id", None),
|
||||
getattr(operator, "open_id", None) or getattr(operator, "user_id", None),
|
||||
getattr(reaction, "emoji_type", None),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _on_message_reaction_deleted(data: P2ImMessageReactionDeletedV1) -> None:
|
||||
"""忽略消息表情删除事件,避免长连接打印未注册处理器错误。"""
|
||||
event = getattr(data, "event", None)
|
||||
operator = getattr(event, "operator", None)
|
||||
reaction = getattr(event, "reaction", None)
|
||||
logger.debug(
|
||||
"收到飞书消息表情删除事件:message_id=%s, user=%s, emoji=%s",
|
||||
getattr(event, "message_id", None),
|
||||
getattr(operator, "open_id", None) or getattr(operator, "user_id", None),
|
||||
getattr(reaction, "emoji_type", None),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _on_message_recalled(data: P2ImMessageRecalledV1) -> None:
|
||||
"""忽略消息撤回事件,避免长连接打印未注册处理器错误。"""
|
||||
event = getattr(data, "event", None)
|
||||
operator = getattr(event, "operator", None)
|
||||
logger.debug(
|
||||
"收到飞书消息撤回事件:message_id=%s, user=%s",
|
||||
getattr(event, "message_id", None),
|
||||
getattr(operator, "open_id", None) or getattr(operator, "user_id", None),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _on_bot_p2p_chat_entered(data: P2ImChatAccessEventBotP2pChatEnteredV1) -> None:
|
||||
"""忽略机器人进入单聊事件,避免长连接打印未注册处理器错误。"""
|
||||
event = getattr(data, "event", None)
|
||||
operator = getattr(event, "operator_id", None)
|
||||
logger.debug(
|
||||
"收到飞书机器人进入单聊事件:chat_id=%s, user=%s",
|
||||
getattr(event, "chat_id", None),
|
||||
getattr(operator, "open_id", None) or getattr(operator, "user_id", None),
|
||||
)
|
||||
|
||||
def get_state(self) -> bool:
|
||||
"""返回飞书客户端是否已就绪。"""
|
||||
return self._ready.is_set() and self._api_client is not None
|
||||
@@ -681,7 +737,9 @@ class Feishu:
|
||||
"feishu_streaming": {
|
||||
"card_id": card_id,
|
||||
"element_id": self.STREAM_CARD_BODY_ELEMENT_ID,
|
||||
"sequence": 1,
|
||||
# CardKit 的后续 PATCH/设置调用都依赖单调递增 sequence,
|
||||
# 首次建卡后尚未发生内容更新,因此从 0 开始记录。
|
||||
"sequence": 0,
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1181,7 +1239,7 @@ class Feishu:
|
||||
if isinstance(stream_meta, dict) and not buttons:
|
||||
card_id = str(stream_meta.get("card_id") or "").strip()
|
||||
element_id = str(stream_meta.get("element_id") or self.STREAM_CARD_BODY_ELEMENT_ID).strip()
|
||||
sequence = int(stream_meta.get("sequence") or 1) + 1
|
||||
sequence = int(stream_meta.get("sequence") or 0) + 1
|
||||
if card_id and element_id and self._update_streaming_card_content(
|
||||
card_id=card_id,
|
||||
element_id=element_id,
|
||||
|
||||
@@ -144,6 +144,36 @@ class TestFeishu(unittest.TestCase):
|
||||
self.assertTrue(result.is_callback)
|
||||
self.assertEqual(result.chat_id, "oc_123")
|
||||
|
||||
def test_build_event_handler_registers_common_im_events(self):
|
||||
registered = []
|
||||
|
||||
class _Builder:
|
||||
def __getattr__(self, name):
|
||||
if name.startswith("register_"):
|
||||
def _register(handler):
|
||||
registered.append(name)
|
||||
return self
|
||||
return _register
|
||||
raise AttributeError(name)
|
||||
|
||||
def build(self):
|
||||
return "handler"
|
||||
|
||||
client = self._build_client()
|
||||
fake_builder = _Builder()
|
||||
|
||||
with patch("app.modules.feishu.feishu.lark.EventDispatcherHandler.builder", return_value=fake_builder):
|
||||
handler = client._build_event_handler()
|
||||
|
||||
self.assertEqual(handler, "handler")
|
||||
self.assertIn("register_p2_im_message_receive_v1", registered)
|
||||
self.assertIn("register_p2_im_message_message_read_v1", registered)
|
||||
self.assertIn("register_p2_im_message_reaction_created_v1", registered)
|
||||
self.assertIn("register_p2_im_message_reaction_deleted_v1", registered)
|
||||
self.assertIn("register_p2_im_message_recalled_v1", registered)
|
||||
self.assertIn("register_p2_im_chat_access_event_bot_p2p_chat_entered_v1", registered)
|
||||
self.assertIn("register_p2_card_action_trigger", registered)
|
||||
|
||||
def test_parse_message_blocks_non_admin_command(self):
|
||||
client = self._build_client(FEISHU_ADMINS="ou_admin")
|
||||
|
||||
@@ -292,6 +322,7 @@ class TestFeishu(unittest.TestCase):
|
||||
|
||||
self.assertTrue(result["success"])
|
||||
self.assertEqual(result["metadata"]["feishu_streaming"]["card_id"], "card_stream")
|
||||
self.assertEqual(result["metadata"]["feishu_streaming"]["sequence"], 0)
|
||||
card_request = client._api_client.cardkit.v1.card.create.call_args.args[0]
|
||||
self.assertEqual(card_request.request_body.type, "card_json")
|
||||
card_payload = json.loads(card_request.request_body.data)
|
||||
@@ -324,6 +355,31 @@ class TestFeishu(unittest.TestCase):
|
||||
self.assertEqual(reply_request.message_id, "om_origin")
|
||||
self.assertEqual(reply_request.request_body.msg_type, "interactive")
|
||||
self.assertEqual(json.loads(reply_request.request_body.content)["data"]["card_id"], "card_stream")
|
||||
self.assertEqual(result["metadata"]["feishu_streaming"]["sequence"], 0)
|
||||
|
||||
def test_edit_replied_streaming_card_uses_first_increment_sequence(self):
|
||||
client = self._build_client()
|
||||
client._api_client, message_api = self._build_message_api(
|
||||
patch_response=self._success_response(),
|
||||
card_content_response=self._success_response(),
|
||||
)
|
||||
|
||||
success = client.edit_message(
|
||||
message_id="om_reply",
|
||||
text="补充内容",
|
||||
metadata={
|
||||
"feishu_streaming": {
|
||||
"card_id": "card_stream",
|
||||
"element_id": Feishu.STREAM_CARD_BODY_ELEMENT_ID,
|
||||
"sequence": 0,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
self.assertTrue(success)
|
||||
message_api.patch.assert_not_called()
|
||||
content_request = client._api_client.cardkit.v1.card_element.content.call_args.args[0]
|
||||
self.assertEqual(content_request.request_body.sequence, 1)
|
||||
|
||||
def test_edit_message_uses_cardkit_content_for_streaming_card(self):
|
||||
client = self._build_client()
|
||||
@@ -339,7 +395,7 @@ class TestFeishu(unittest.TestCase):
|
||||
"feishu_streaming": {
|
||||
"card_id": "card_stream",
|
||||
"element_id": Feishu.STREAM_CARD_BODY_ELEMENT_ID,
|
||||
"sequence": 1,
|
||||
"sequence": 0,
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -350,7 +406,7 @@ class TestFeishu(unittest.TestCase):
|
||||
content_request = client._api_client.cardkit.v1.card_element.content.call_args.args[0]
|
||||
self.assertEqual(content_request.card_id, "card_stream")
|
||||
self.assertEqual(content_request.element_id, Feishu.STREAM_CARD_BODY_ELEMENT_ID)
|
||||
self.assertEqual(content_request.request_body.sequence, 2)
|
||||
self.assertEqual(content_request.request_body.sequence, 1)
|
||||
|
||||
def test_close_streaming_card_updates_card_settings(self):
|
||||
client = self._build_client()
|
||||
@@ -628,7 +684,7 @@ class TestFeishu(unittest.TestCase):
|
||||
metadata={
|
||||
"feishu_streaming": {
|
||||
"card_id": "card_stream",
|
||||
"sequence": 3,
|
||||
"sequence": 2,
|
||||
}
|
||||
},
|
||||
success=True,
|
||||
@@ -636,7 +692,7 @@ class TestFeishu(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assertTrue(success)
|
||||
client.close_streaming_card.assert_called_once_with(card_id="card_stream", sequence=4)
|
||||
client.close_streaming_card.assert_called_once_with(card_id="card_stream", sequence=3)
|
||||
|
||||
def test_module_post_message_prefers_file_and_voice_paths(self):
|
||||
module = FeishuModule()
|
||||
|
||||
Reference in New Issue
Block a user