mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-12 16:02:35 +08:00
fix: map feishu user identity for agent subscribe
This commit is contained in:
@@ -51,6 +51,7 @@ from lark_oapi.event.callback.model.p2_card_action_trigger import (
|
|||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.context import Context, MediaInfo
|
from app.core.context import Context, MediaInfo
|
||||||
|
from app.db.user_oper import UserOper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import CommingMessage, Notification
|
from app.schemas import CommingMessage, Notification
|
||||||
from app.schemas.types import MessageChannel, NotificationType
|
from app.schemas.types import MessageChannel, NotificationType
|
||||||
@@ -269,6 +270,27 @@ class Feishu:
|
|||||||
if normalized_user_id:
|
if normalized_user_id:
|
||||||
self._user_receive_id_type_mapping[normalized_user_id] = "user_id"
|
self._user_receive_id_type_mapping[normalized_user_id] = "user_id"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _resolve_username(
|
||||||
|
open_id: Optional[str],
|
||||||
|
user_id: Optional[str],
|
||||||
|
fallback: Optional[str],
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""根据飞书绑定 ID 映射 MoviePilot 用户名,未绑定时保留渠道名称。"""
|
||||||
|
binding_ids = {}
|
||||||
|
if open_id:
|
||||||
|
binding_ids["feishu_openid"] = open_id
|
||||||
|
if user_id:
|
||||||
|
binding_ids["feishu_userid"] = user_id
|
||||||
|
if binding_ids:
|
||||||
|
try:
|
||||||
|
mapped_username = UserOper().get_name(**binding_ids)
|
||||||
|
if mapped_username:
|
||||||
|
return mapped_username
|
||||||
|
except Exception as err:
|
||||||
|
logger.debug(f"解析飞书用户绑定失败:{err}")
|
||||||
|
return fallback
|
||||||
|
|
||||||
def _on_message(self, data: P2ImMessageReceiveV1) -> None:
|
def _on_message(self, data: P2ImMessageReceiveV1) -> None:
|
||||||
"""处理飞书长连接收到的普通消息事件。"""
|
"""处理飞书长连接收到的普通消息事件。"""
|
||||||
event = getattr(data, "event", None)
|
event = getattr(data, "event", None)
|
||||||
@@ -454,7 +476,11 @@ class Feishu:
|
|||||||
sender = message.get("sender") or {}
|
sender = message.get("sender") or {}
|
||||||
open_id = sender.get("open_id")
|
open_id = sender.get("open_id")
|
||||||
user_id = sender.get("user_id")
|
user_id = sender.get("user_id")
|
||||||
username = sender.get("name") or open_id or user_id
|
username = self._resolve_username(
|
||||||
|
open_id=open_id,
|
||||||
|
user_id=user_id,
|
||||||
|
fallback=sender.get("name") or open_id or user_id,
|
||||||
|
)
|
||||||
userid = open_id or user_id
|
userid = open_id or user_id
|
||||||
if not userid:
|
if not userid:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -60,6 +60,32 @@ class TestAgentAddSubscribeTool(unittest.TestCase):
|
|||||||
self.assertEqual(async_add.await_args.kwargs["username"], "tg_display_name")
|
self.assertEqual(async_add.await_args.kwargs["username"], "tg_display_name")
|
||||||
self.assertIn("成功添加订阅:The Matrix (1999)", result)
|
self.assertIn("成功添加订阅:The Matrix (1999)", result)
|
||||||
|
|
||||||
|
def test_feishu_subscription_uses_pre_resolved_username_when_openid_lookup_misses(self):
|
||||||
|
tool = AddSubscribeTool(session_id="session-1", user_id="ou_feishu_user")
|
||||||
|
tool.set_message_attr(
|
||||||
|
channel=MessageChannel.Feishu.value,
|
||||||
|
source="feishu-main",
|
||||||
|
username="moviepilot-user",
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"app.agent.tools.impl.add_subscribe.SubscribeChain.async_add",
|
||||||
|
new=AsyncMock(return_value=(1, "")),
|
||||||
|
) as async_add, patch(
|
||||||
|
"app.agent.tools.impl.add_subscribe.UserOper.get_name",
|
||||||
|
return_value=None,
|
||||||
|
):
|
||||||
|
result = asyncio.run(
|
||||||
|
tool.run(
|
||||||
|
title="The Matrix",
|
||||||
|
year="1999",
|
||||||
|
media_type="movie",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(async_add.await_args.kwargs["username"], "moviepilot-user")
|
||||||
|
self.assertIn("成功添加订阅:The Matrix (1999)", result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -123,19 +123,20 @@ class TestFeishu(unittest.TestCase):
|
|||||||
def test_parse_message_returns_callback_message(self):
|
def test_parse_message_returns_callback_message(self):
|
||||||
client = self._build_client()
|
client = self._build_client()
|
||||||
|
|
||||||
result = client.parse_message(
|
with patch("app.modules.feishu.feishu.UserOper.get_name", return_value=None):
|
||||||
{
|
result = client.parse_message(
|
||||||
"type": "cardAction",
|
{
|
||||||
"callback_data": "approve",
|
"type": "cardAction",
|
||||||
"message_id": "om_123",
|
"callback_data": "approve",
|
||||||
"chat_id": "oc_123",
|
"message_id": "om_123",
|
||||||
"sender": {
|
"chat_id": "oc_123",
|
||||||
"open_id": "ou_user_1",
|
"sender": {
|
||||||
"user_id": "u_user_1",
|
"open_id": "ou_user_1",
|
||||||
"name": "tester",
|
"user_id": "u_user_1",
|
||||||
},
|
"name": "tester",
|
||||||
}
|
},
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
|
||||||
self.assertIsNotNone(result)
|
self.assertIsNotNone(result)
|
||||||
self.assertEqual(result.channel, MessageChannel.Feishu)
|
self.assertEqual(result.channel, MessageChannel.Feishu)
|
||||||
@@ -195,7 +196,9 @@ class TestFeishu(unittest.TestCase):
|
|||||||
def test_parse_message_blocks_non_admin_command(self):
|
def test_parse_message_blocks_non_admin_command(self):
|
||||||
client = self._build_client(FEISHU_ADMINS="ou_admin")
|
client = self._build_client(FEISHU_ADMINS="ou_admin")
|
||||||
|
|
||||||
with patch.object(client, "send_text", return_value={"success": True}) as send_text:
|
with patch("app.modules.feishu.feishu.UserOper.get_name", return_value=None), patch.object(
|
||||||
|
client, "send_text", return_value={"success": True}
|
||||||
|
) as send_text:
|
||||||
result = client.parse_message(
|
result = client.parse_message(
|
||||||
{
|
{
|
||||||
"type": "message",
|
"type": "message",
|
||||||
@@ -217,6 +220,30 @@ class TestFeishu(unittest.TestCase):
|
|||||||
receive_id_type="open_id",
|
receive_id_type="open_id",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_parse_message_maps_feishu_ids_to_moviepilot_username(self):
|
||||||
|
client = self._build_client()
|
||||||
|
|
||||||
|
with patch("app.modules.feishu.feishu.UserOper.get_name", return_value="moviepilot-user") as get_name:
|
||||||
|
result = client.parse_message(
|
||||||
|
{
|
||||||
|
"type": "message",
|
||||||
|
"text": "/ai 添加黑客帝国订阅",
|
||||||
|
"sender": {
|
||||||
|
"open_id": "ou_bound_user",
|
||||||
|
"user_id": "u_bound_user",
|
||||||
|
"name": "ou_bound_user",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
self.assertEqual(result.userid, "ou_bound_user")
|
||||||
|
self.assertEqual(result.username, "moviepilot-user")
|
||||||
|
get_name.assert_called_once_with(
|
||||||
|
feishu_openid="ou_bound_user",
|
||||||
|
feishu_userid="u_bound_user",
|
||||||
|
)
|
||||||
|
|
||||||
def test_send_notification_uses_direct_card_content(self):
|
def test_send_notification_uses_direct_card_content(self):
|
||||||
client = self._build_client()
|
client = self._build_client()
|
||||||
client._api_client, message_api = self._build_message_api(
|
client._api_client, message_api = self._build_message_api(
|
||||||
@@ -499,33 +526,34 @@ class TestFeishu(unittest.TestCase):
|
|||||||
def test_parse_message_supports_image_and_file_payloads(self):
|
def test_parse_message_supports_image_and_file_payloads(self):
|
||||||
client = self._build_client()
|
client = self._build_client()
|
||||||
|
|
||||||
image_message = client.parse_message(
|
with patch("app.modules.feishu.feishu.UserOper.get_name", return_value=None):
|
||||||
{
|
image_message = client.parse_message(
|
||||||
"type": "message",
|
{
|
||||||
"text": "",
|
"type": "message",
|
||||||
"images": [{"ref": "feishu://image/img_v2_test"}],
|
"text": "",
|
||||||
"message_id": "om_img",
|
"images": [{"ref": "feishu://image/img_v2_test"}],
|
||||||
"chat_id": "oc_chat",
|
"message_id": "om_img",
|
||||||
"sender": {
|
"chat_id": "oc_chat",
|
||||||
"open_id": "ou_user_5",
|
"sender": {
|
||||||
"name": "tester",
|
"open_id": "ou_user_5",
|
||||||
},
|
"name": "tester",
|
||||||
}
|
},
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
|
||||||
file_message = client.parse_message(
|
file_message = client.parse_message(
|
||||||
{
|
{
|
||||||
"type": "message",
|
"type": "message",
|
||||||
"text": "",
|
"text": "",
|
||||||
"files": [{"ref": "feishu://file/file_key/report.pdf", "name": "report.pdf"}],
|
"files": [{"ref": "feishu://file/file_key/report.pdf", "name": "report.pdf"}],
|
||||||
"message_id": "om_file",
|
"message_id": "om_file",
|
||||||
"chat_id": "oc_chat",
|
"chat_id": "oc_chat",
|
||||||
"sender": {
|
"sender": {
|
||||||
"open_id": "ou_user_6",
|
"open_id": "ou_user_6",
|
||||||
"name": "tester",
|
"name": "tester",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(image_message.images[0].ref, "feishu://image/img_v2_test")
|
self.assertEqual(image_message.images[0].ref, "feishu://image/img_v2_test")
|
||||||
self.assertEqual(file_message.files[0].ref, "feishu://file/file_key/report.pdf")
|
self.assertEqual(file_message.files[0].ref, "feishu://file/file_key/report.pdf")
|
||||||
|
|||||||
Reference in New Issue
Block a user