mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
refactor(feishu): promote download helper methods to public, update call sites and tests
This commit is contained in:
@@ -663,7 +663,7 @@ class MoviePilotAgent:
|
|||||||
and self.should_dispatch_reply
|
and self.should_dispatch_reply
|
||||||
and not self._tool_context.get("user_reply_sent")
|
and not self._tool_context.get("user_reply_sent")
|
||||||
):
|
):
|
||||||
await self.send_agent_message(remaining_text, is_streaming_fallback=True)
|
await self.send_agent_message(remaining_text)
|
||||||
elif (
|
elif (
|
||||||
remaining_text
|
remaining_text
|
||||||
and self.persist_output_message
|
and self.persist_output_message
|
||||||
@@ -743,16 +743,15 @@ class MoviePilotAgent:
|
|||||||
# 确保停止流式输出
|
# 确保停止流式输出
|
||||||
await self.stream_handler.stop_streaming()
|
await self.stream_handler.stop_streaming()
|
||||||
|
|
||||||
async def send_agent_message(self, message: str, title: str = "", is_streaming_fallback: bool = False):
|
async def send_agent_message(self, message: str, title: str = ""):
|
||||||
"""
|
"""
|
||||||
通过原渠道发送消息给用户
|
通过原渠道发送消息给用户
|
||||||
"""
|
"""
|
||||||
mtype = NotificationType.System if is_streaming_fallback else NotificationType.Agent
|
|
||||||
await AgentChain().async_post_message(
|
await AgentChain().async_post_message(
|
||||||
Notification(
|
Notification(
|
||||||
channel=self.channel,
|
channel=self.channel,
|
||||||
source=self.source,
|
source=self.source,
|
||||||
mtype=mtype,
|
mtype=NotificationType.Agent,
|
||||||
userid=self.user_id,
|
userid=self.user_id,
|
||||||
username=self.username,
|
username=self.username,
|
||||||
original_message_id=self.original_message_id,
|
original_message_id=self.original_message_id,
|
||||||
|
|||||||
@@ -243,13 +243,13 @@ class FeishuModule(_ModuleBase, _MessageBase[Feishu]):
|
|||||||
image_key = image_key.strip()
|
image_key = image_key.strip()
|
||||||
downloaded = None
|
downloaded = None
|
||||||
if message_id:
|
if message_id:
|
||||||
downloaded = client._download_message_resource_bytes(
|
downloaded = client.download_message_resource_bytes(
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
file_key=image_key,
|
file_key=image_key,
|
||||||
resource_type="image",
|
resource_type="image",
|
||||||
)
|
)
|
||||||
if not downloaded:
|
if not downloaded:
|
||||||
downloaded = client._download_image_bytes(image_key)
|
downloaded = client.download_image_bytes(image_key)
|
||||||
if not downloaded:
|
if not downloaded:
|
||||||
return None
|
return None
|
||||||
content, _, content_type = downloaded
|
content, _, content_type = downloaded
|
||||||
@@ -271,7 +271,7 @@ class FeishuModule(_ModuleBase, _MessageBase[Feishu]):
|
|||||||
file_key = parts[0].strip() if parts else ""
|
file_key = parts[0].strip() if parts else ""
|
||||||
if not file_key:
|
if not file_key:
|
||||||
return None
|
return None
|
||||||
downloaded = client._download_file_bytes(file_key)
|
downloaded = client.download_file_bytes(file_key)
|
||||||
if not downloaded:
|
if not downloaded:
|
||||||
return None
|
return None
|
||||||
content, _, _ = downloaded
|
content, _, _ = downloaded
|
||||||
|
|||||||
+110
-101
@@ -1,7 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
|
||||||
import json
|
import json
|
||||||
import mimetypes
|
|
||||||
import threading
|
import threading
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -65,16 +63,16 @@ class Feishu:
|
|||||||
STREAM_CARD_BODY_ELEMENT_ID = "mp_stream_body"
|
STREAM_CARD_BODY_ELEMENT_ID = "mp_stream_body"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
FEISHU_APP_ID: Optional[str] = None,
|
FEISHU_APP_ID: Optional[str] = None,
|
||||||
FEISHU_APP_SECRET: Optional[str] = None,
|
FEISHU_APP_SECRET: Optional[str] = None,
|
||||||
FEISHU_OPEN_ID: Optional[str] = None,
|
FEISHU_OPEN_ID: Optional[str] = None,
|
||||||
FEISHU_CHAT_ID: Optional[str] = None,
|
FEISHU_CHAT_ID: Optional[str] = None,
|
||||||
FEISHU_ADMINS: Optional[str] = None,
|
FEISHU_ADMINS: Optional[str] = None,
|
||||||
FEISHU_VERIFICATION_TOKEN: Optional[str] = None,
|
FEISHU_VERIFICATION_TOKEN: Optional[str] = None,
|
||||||
FEISHU_ENCRYPT_KEY: Optional[str] = None,
|
FEISHU_ENCRYPT_KEY: Optional[str] = None,
|
||||||
name: Optional[str] = None,
|
name: Optional[str] = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""初始化飞书客户端与长连接所需配置。"""
|
"""初始化飞书客户端与长连接所需配置。"""
|
||||||
self._name = name or "feishu"
|
self._name = name or "feishu"
|
||||||
@@ -202,7 +200,9 @@ class Feishu:
|
|||||||
threading.Thread(target=_run, daemon=True).start()
|
threading.Thread(target=_run, daemon=True).start()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_message_content(message) -> Tuple[str, Optional[List[CommingMessage.MessageImage]], Optional[List[str]], Optional[List[CommingMessage.MessageAttachment]]]:
|
def _parse_message_content(message) -> Tuple[
|
||||||
|
str, Optional[List[CommingMessage.MessageImage]], Optional[List[str]], Optional[
|
||||||
|
List[CommingMessage.MessageAttachment]]]:
|
||||||
"""从飞书事件消息体中提取文本、图片、音频和文件引用。"""
|
"""从飞书事件消息体中提取文本、图片、音频和文件引用。"""
|
||||||
raw_content = getattr(message, "content", None)
|
raw_content = getattr(message, "content", None)
|
||||||
if not raw_content:
|
if not raw_content:
|
||||||
@@ -254,9 +254,9 @@ class Feishu:
|
|||||||
self._chat_open_mapping[normalized_chat_id] = normalized_userid
|
self._chat_open_mapping[normalized_chat_id] = normalized_userid
|
||||||
|
|
||||||
def _remember_user_id_type(
|
def _remember_user_id_type(
|
||||||
self,
|
self,
|
||||||
open_id: Optional[str] = None,
|
open_id: Optional[str] = None,
|
||||||
user_id: Optional[str] = None,
|
user_id: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""记住用户对应的飞书 ID 类型,避免回消息时误用 open_id/user_id。"""
|
"""记住用户对应的飞书 ID 类型,避免回消息时误用 open_id/user_id。"""
|
||||||
normalized_open_id = (open_id or "").strip()
|
normalized_open_id = (open_id or "").strip()
|
||||||
@@ -513,10 +513,10 @@ class Feishu:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _resolve_target(
|
def _resolve_target(
|
||||||
self,
|
self,
|
||||||
userid: Optional[str] = None,
|
userid: Optional[str] = None,
|
||||||
chat_id: Optional[str] = None,
|
chat_id: Optional[str] = None,
|
||||||
receive_id_type: Optional[str] = None,
|
receive_id_type: Optional[str] = None,
|
||||||
) -> Tuple[str, str]:
|
) -> Tuple[str, str]:
|
||||||
"""解析飞书发送目标,优先走显式用户,其次回退默认配置。"""
|
"""解析飞书发送目标,优先走显式用户,其次回退默认配置。"""
|
||||||
resolved_userid = (userid or "").strip() or None
|
resolved_userid = (userid or "").strip() or None
|
||||||
@@ -608,7 +608,8 @@ class Feishu:
|
|||||||
card_rows.append({"tag": "action", "actions": elements})
|
card_rows.append({"tag": "action", "actions": elements})
|
||||||
return card_rows
|
return card_rows
|
||||||
|
|
||||||
def _build_card(self, title: Optional[str], text: Optional[str], link: Optional[str], buttons: Optional[List[List[dict]]]) -> Dict[str, Any]:
|
def _build_card(self, title: Optional[str], text: Optional[str], link: Optional[str],
|
||||||
|
buttons: Optional[List[List[dict]]]) -> Dict[str, Any]:
|
||||||
"""构建飞书交互卡片结构。"""
|
"""构建飞书交互卡片结构。"""
|
||||||
elements: List[dict] = []
|
elements: List[dict] = []
|
||||||
title_section = self._build_markdown_section(title, text_size="heading")
|
title_section = self._build_markdown_section(title, text_size="heading")
|
||||||
@@ -632,9 +633,9 @@ class Feishu:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _build_streaming_card_payload(
|
def _build_streaming_card_payload(
|
||||||
self,
|
self,
|
||||||
title: Optional[str],
|
title: Optional[str],
|
||||||
text: Optional[str],
|
text: Optional[str],
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""构建支持 CardKit 流式更新的飞书卡片 JSON 2.0。"""
|
"""构建支持 CardKit 流式更新的飞书卡片 JSON 2.0。"""
|
||||||
elements: List[dict] = []
|
elements: List[dict] = []
|
||||||
@@ -702,13 +703,13 @@ class Feishu:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _send_streaming_card_message(
|
def _send_streaming_card_message(
|
||||||
self,
|
self,
|
||||||
title: Optional[str],
|
title: Optional[str],
|
||||||
text: Optional[str],
|
text: Optional[str],
|
||||||
userid: Optional[str] = None,
|
userid: Optional[str] = None,
|
||||||
chat_id: Optional[str] = None,
|
chat_id: Optional[str] = None,
|
||||||
receive_id_type: Optional[str] = None,
|
receive_id_type: Optional[str] = None,
|
||||||
original_message_id: Optional[str] = None,
|
original_message_id: Optional[str] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
card_id = self._create_streaming_card(title=title, text=text)
|
card_id = self._create_streaming_card(title=title, text=text)
|
||||||
if not card_id:
|
if not card_id:
|
||||||
@@ -745,11 +746,11 @@ class Feishu:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _update_streaming_card_content(
|
def _update_streaming_card_content(
|
||||||
self,
|
self,
|
||||||
card_id: str,
|
card_id: str,
|
||||||
element_id: str,
|
element_id: str,
|
||||||
content: str,
|
content: str,
|
||||||
sequence: int,
|
sequence: int,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if not self._api_client:
|
if not self._api_client:
|
||||||
return False
|
return False
|
||||||
@@ -843,11 +844,11 @@ class Feishu:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _reply_message(
|
def _reply_message(
|
||||||
self,
|
self,
|
||||||
message_id: str,
|
message_id: str,
|
||||||
msg_type: str,
|
msg_type: str,
|
||||||
content: dict,
|
content: dict,
|
||||||
reply_in_thread: bool = False,
|
reply_in_thread: bool = False,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""按原消息回复,保持飞书会话中的引用关系。"""
|
"""按原消息回复,保持飞书会话中的引用关系。"""
|
||||||
if not self._api_client:
|
if not self._api_client:
|
||||||
@@ -923,7 +924,8 @@ class Feishu:
|
|||||||
data = getattr(response, "data", None)
|
data = getattr(response, "data", None)
|
||||||
return getattr(data, "image_key", None)
|
return getattr(data, "image_key", None)
|
||||||
|
|
||||||
def _upload_file(self, file_path: Path, file_name: Optional[str] = None, duration: Optional[int] = None) -> Optional[str]:
|
def _upload_file(self, file_path: Path, file_name: Optional[str] = None, duration: Optional[int] = None) -> \
|
||||||
|
Optional[str]:
|
||||||
if not self._api_client:
|
if not self._api_client:
|
||||||
return None
|
return None
|
||||||
with file_path.open("rb") as fp:
|
with file_path.open("rb") as fp:
|
||||||
@@ -949,7 +951,7 @@ class Feishu:
|
|||||||
data = getattr(response, "data", None)
|
data = getattr(response, "data", None)
|
||||||
return getattr(data, "file_key", None)
|
return getattr(data, "file_key", None)
|
||||||
|
|
||||||
def _download_image_bytes(self, image_key: str) -> Optional[Tuple[bytes, Optional[str], Optional[str]]]:
|
def download_image_bytes(self, image_key: str) -> Optional[Tuple[bytes, Optional[str], Optional[str]]]:
|
||||||
if not self._api_client or not image_key:
|
if not self._api_client or not image_key:
|
||||||
return None
|
return None
|
||||||
response = self._api_client.im.v1.image.get(
|
response = self._api_client.im.v1.image.get(
|
||||||
@@ -962,7 +964,7 @@ class Feishu:
|
|||||||
content_type = response.raw.headers.get("Content-Type")
|
content_type = response.raw.headers.get("Content-Type")
|
||||||
return response.file.read(), response.file_name, content_type
|
return response.file.read(), response.file_name, content_type
|
||||||
|
|
||||||
def _download_file_bytes(self, file_key: str) -> Optional[Tuple[bytes, Optional[str], Optional[str]]]:
|
def download_file_bytes(self, file_key: str) -> Optional[Tuple[bytes, Optional[str], Optional[str]]]:
|
||||||
if not self._api_client or not file_key:
|
if not self._api_client or not file_key:
|
||||||
return None
|
return None
|
||||||
response = self._api_client.im.v1.file.get(
|
response = self._api_client.im.v1.file.get(
|
||||||
@@ -975,7 +977,8 @@ class Feishu:
|
|||||||
content_type = response.raw.headers.get("Content-Type")
|
content_type = response.raw.headers.get("Content-Type")
|
||||||
return response.file.read(), response.file_name, content_type
|
return response.file.read(), response.file_name, content_type
|
||||||
|
|
||||||
def _download_message_resource_bytes(self, message_id: str, file_key: str, resource_type: str) -> Optional[Tuple[bytes, Optional[str], Optional[str]]]:
|
def download_message_resource_bytes(self, message_id: str, file_key: str, resource_type: str) -> Optional[
|
||||||
|
Tuple[bytes, Optional[str], Optional[str]]]:
|
||||||
if not self._api_client or not message_id or not file_key:
|
if not self._api_client or not message_id or not file_key:
|
||||||
return None
|
return None
|
||||||
response = self._api_client.im.v1.message_resource.get(
|
response = self._api_client.im.v1.message_resource.get(
|
||||||
@@ -993,12 +996,12 @@ class Feishu:
|
|||||||
return response.file.read(), response.file_name, content_type
|
return response.file.read(), response.file_name, content_type
|
||||||
|
|
||||||
def send_text(
|
def send_text(
|
||||||
self,
|
self,
|
||||||
text: str,
|
text: str,
|
||||||
userid: Optional[str] = None,
|
userid: Optional[str] = None,
|
||||||
chat_id: Optional[str] = None,
|
chat_id: Optional[str] = None,
|
||||||
receive_id_type: Optional[str] = None,
|
receive_id_type: Optional[str] = None,
|
||||||
original_message_id: Optional[str] = None,
|
original_message_id: Optional[str] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""发送纯文本消息。"""
|
"""发送纯文本消息。"""
|
||||||
try:
|
try:
|
||||||
@@ -1026,19 +1029,20 @@ class Feishu:
|
|||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return {"success": False}
|
return {"success": False}
|
||||||
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(userid or "") or self._default_chat_id
|
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(
|
||||||
|
userid or "") or self._default_chat_id
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def send_file(
|
def send_file(
|
||||||
self,
|
self,
|
||||||
file_path: str,
|
file_path: str,
|
||||||
userid: Optional[str] = None,
|
userid: Optional[str] = None,
|
||||||
chat_id: Optional[str] = None,
|
chat_id: Optional[str] = None,
|
||||||
title: Optional[str] = None,
|
title: Optional[str] = None,
|
||||||
text: Optional[str] = None,
|
text: Optional[str] = None,
|
||||||
file_name: Optional[str] = None,
|
file_name: Optional[str] = None,
|
||||||
receive_id_type: Optional[str] = None,
|
receive_id_type: Optional[str] = None,
|
||||||
original_message_id: Optional[str] = None,
|
original_message_id: Optional[str] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""发送本地图片或文件。"""
|
"""发送本地图片或文件。"""
|
||||||
local_file = Path(file_path)
|
local_file = Path(file_path)
|
||||||
@@ -1107,17 +1111,18 @@ class Feishu:
|
|||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return {"success": False}
|
return {"success": False}
|
||||||
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(userid or "") or self._default_chat_id
|
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(
|
||||||
|
userid or "") or self._default_chat_id
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def send_voice(
|
def send_voice(
|
||||||
self,
|
self,
|
||||||
voice_path: str,
|
voice_path: str,
|
||||||
userid: Optional[str] = None,
|
userid: Optional[str] = None,
|
||||||
chat_id: Optional[str] = None,
|
chat_id: Optional[str] = None,
|
||||||
caption: Optional[str] = None,
|
caption: Optional[str] = None,
|
||||||
receive_id_type: Optional[str] = None,
|
receive_id_type: Optional[str] = None,
|
||||||
original_message_id: Optional[str] = None,
|
original_message_id: Optional[str] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""发送飞书语音消息。"""
|
"""发送飞书语音消息。"""
|
||||||
local_file = Path(voice_path)
|
local_file = Path(voice_path)
|
||||||
@@ -1161,22 +1166,23 @@ class Feishu:
|
|||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return {"success": False}
|
return {"success": False}
|
||||||
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(userid or "") or self._default_chat_id
|
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(
|
||||||
|
userid or "") or self._default_chat_id
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def send_notification(
|
def send_notification(
|
||||||
self,
|
self,
|
||||||
message: Notification,
|
message: Notification,
|
||||||
userid: Optional[str] = None,
|
userid: Optional[str] = None,
|
||||||
chat_id: Optional[str] = None,
|
chat_id: Optional[str] = None,
|
||||||
receive_id_type: Optional[str] = None,
|
receive_id_type: Optional[str] = None,
|
||||||
original_message_id: Optional[str] = None,
|
original_message_id: Optional[str] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""发送通知消息,优先使用交互卡片承载按钮。"""
|
"""发送通知消息,优先使用交互卡片承载按钮。"""
|
||||||
is_streaming_agent_text = (
|
is_streaming_agent_text = (
|
||||||
message.mtype == NotificationType.Agent
|
message.mtype == NotificationType.Agent
|
||||||
and not message.buttons
|
and not message.buttons
|
||||||
and not message.link
|
and not message.link
|
||||||
)
|
)
|
||||||
if is_streaming_agent_text:
|
if is_streaming_agent_text:
|
||||||
try:
|
try:
|
||||||
@@ -1193,7 +1199,8 @@ class Feishu:
|
|||||||
return {"success": False}
|
return {"success": False}
|
||||||
if not result:
|
if not result:
|
||||||
return {"success": False}
|
return {"success": False}
|
||||||
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(userid or "") or self._default_chat_id
|
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(
|
||||||
|
userid or "") or self._default_chat_id
|
||||||
return result
|
return result
|
||||||
|
|
||||||
payload = self._build_card(
|
payload = self._build_card(
|
||||||
@@ -1227,10 +1234,12 @@ class Feishu:
|
|||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return {"success": False}
|
return {"success": False}
|
||||||
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(userid or "") or self._default_chat_id
|
result["chat_id"] = result.get("chat_id") or chat_id or self._user_chat_mapping.get(
|
||||||
|
userid or "") or self._default_chat_id
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def edit_message(self, message_id: str, title: Optional[str] = None, text: Optional[str] = None, buttons: Optional[List[List[dict]]] = None, metadata: Optional[dict] = None) -> bool:
|
def edit_message(self, message_id: str, title: Optional[str] = None, text: Optional[str] = None,
|
||||||
|
buttons: Optional[List[List[dict]]] = None, metadata: Optional[dict] = None) -> bool:
|
||||||
"""编辑已发送的飞书交互卡片消息。"""
|
"""编辑已发送的飞书交互卡片消息。"""
|
||||||
if not self._api_client:
|
if not self._api_client:
|
||||||
return False
|
return False
|
||||||
@@ -1243,10 +1252,10 @@ class Feishu:
|
|||||||
# 无论远端是否响应成功都自增 sequence,防止某次超时导致后续 sequence 一直因为没有递增而被拒绝
|
# 无论远端是否响应成功都自增 sequence,防止某次超时导致后续 sequence 一直因为没有递增而被拒绝
|
||||||
stream_meta["sequence"] = sequence
|
stream_meta["sequence"] = sequence
|
||||||
if card_id and element_id and self._update_streaming_card_content(
|
if card_id and element_id and self._update_streaming_card_content(
|
||||||
card_id=card_id,
|
card_id=card_id,
|
||||||
element_id=element_id,
|
element_id=element_id,
|
||||||
content=self._escape_card_text(text).strip() or " ",
|
content=self._escape_card_text(text).strip() or " ",
|
||||||
sequence=sequence,
|
sequence=sequence,
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1275,9 +1284,9 @@ class Feishu:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def add_message_reaction(
|
def add_message_reaction(
|
||||||
self,
|
self,
|
||||||
message_id: str,
|
message_id: str,
|
||||||
emoji_type: str,
|
emoji_type: str,
|
||||||
) -> Optional[str]:
|
) -> Optional[str]:
|
||||||
"""为指定消息添加表情回应,并返回 reaction_id。"""
|
"""为指定消息添加表情回应,并返回 reaction_id。"""
|
||||||
if not self._api_client or not message_id or not emoji_type:
|
if not self._api_client or not message_id or not emoji_type:
|
||||||
@@ -1339,12 +1348,12 @@ class Feishu:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def send_medias_message(
|
def send_medias_message(
|
||||||
self,
|
self,
|
||||||
message: Notification,
|
message: Notification,
|
||||||
medias: List[MediaInfo],
|
medias: List[MediaInfo],
|
||||||
userid: Optional[str] = None,
|
userid: Optional[str] = None,
|
||||||
chat_id: Optional[str] = None,
|
chat_id: Optional[str] = None,
|
||||||
receive_id_type: Optional[str] = None,
|
receive_id_type: Optional[str] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""发送媒体列表消息,复用通知发送链路。"""
|
"""发送媒体列表消息,复用通知发送链路。"""
|
||||||
lines = []
|
lines = []
|
||||||
@@ -1367,12 +1376,12 @@ class Feishu:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def send_torrents_message(
|
def send_torrents_message(
|
||||||
self,
|
self,
|
||||||
message: Notification,
|
message: Notification,
|
||||||
torrents: List[Context],
|
torrents: List[Context],
|
||||||
userid: Optional[str] = None,
|
userid: Optional[str] = None,
|
||||||
chat_id: Optional[str] = None,
|
chat_id: Optional[str] = None,
|
||||||
receive_id_type: Optional[str] = None,
|
receive_id_type: Optional[str] = None,
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""发送种子列表消息,复用通知发送链路。"""
|
"""发送种子列表消息,复用通知发送链路。"""
|
||||||
lines = []
|
lines = []
|
||||||
|
|||||||
@@ -543,9 +543,9 @@ class TestFeishu(unittest.TestCase):
|
|||||||
message_resource_response=self._resource_response(b"resource-bytes", file_name="voice.opus", content_type="audio/ogg"),
|
message_resource_response=self._resource_response(b"resource-bytes", file_name="voice.opus", content_type="audio/ogg"),
|
||||||
)
|
)
|
||||||
|
|
||||||
image_download = client._download_image_bytes("img_v2_test")
|
image_download = client.download_image_bytes("img_v2_test")
|
||||||
file_download = client._download_file_bytes("file_test")
|
file_download = client.download_file_bytes("file_test")
|
||||||
resource_download = client._download_message_resource_bytes("om_test", "file_test", "audio")
|
resource_download = client.download_message_resource_bytes("om_test", "file_test", "audio")
|
||||||
|
|
||||||
self.assertEqual(image_download[0], b"image-bytes")
|
self.assertEqual(image_download[0], b"image-bytes")
|
||||||
self.assertEqual(file_download[0], b"file-bytes")
|
self.assertEqual(file_download[0], b"file-bytes")
|
||||||
@@ -633,9 +633,9 @@ class TestFeishu(unittest.TestCase):
|
|||||||
def test_module_download_helpers_delegate_to_client(self):
|
def test_module_download_helpers_delegate_to_client(self):
|
||||||
module = FeishuModule()
|
module = FeishuModule()
|
||||||
client = MagicMock()
|
client = MagicMock()
|
||||||
client._download_image_bytes.return_value = (b"image", "poster.png", "image/png")
|
client.download_image_bytes.return_value = (b"image", "poster.png", "image/png")
|
||||||
client._download_file_bytes.return_value = (b"file", "note.txt", "text/plain")
|
client.download_file_bytes.return_value = (b"file", "note.txt", "text/plain")
|
||||||
client._download_message_resource_bytes.return_value = (b"image", "poster.png", "image/png")
|
client.download_message_resource_bytes.return_value = (b"image", "poster.png", "image/png")
|
||||||
|
|
||||||
with patch.object(module, "get_config", return_value=SimpleNamespace(name="feishu-main")), patch.object(
|
with patch.object(module, "get_config", return_value=SimpleNamespace(name="feishu-main")), patch.object(
|
||||||
module, "get_instance", return_value=client
|
module, "get_instance", return_value=client
|
||||||
@@ -645,7 +645,7 @@ class TestFeishu(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertTrue(data_url.startswith("data:image/png;base64,"))
|
self.assertTrue(data_url.startswith("data:image/png;base64,"))
|
||||||
self.assertEqual(file_bytes, b"file")
|
self.assertEqual(file_bytes, b"file")
|
||||||
client._download_message_resource_bytes.assert_called_once_with(
|
client.download_message_resource_bytes.assert_called_once_with(
|
||||||
message_id="om_msg",
|
message_id="om_msg",
|
||||||
file_key="img_v2_xxx",
|
file_key="img_v2_xxx",
|
||||||
resource_type="image",
|
resource_type="image",
|
||||||
|
|||||||
Reference in New Issue
Block a user