mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 15:38:19 +08:00
feat(agent): support file attachments and local file replies
This commit is contained in:
@@ -221,12 +221,14 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
if msg_json:
|
||||
images = None
|
||||
audio_refs = None
|
||||
files = None
|
||||
if msg_json.get("type") == "message":
|
||||
userid = msg_json.get("user")
|
||||
text = msg_json.get("text")
|
||||
username = msg_json.get("user")
|
||||
images = self._extract_images(msg_json)
|
||||
audio_refs = self._extract_audio_refs(msg_json)
|
||||
files = self._extract_files(msg_json)
|
||||
elif msg_json.get("type") == "block_actions":
|
||||
userid = msg_json.get("user", {}).get("id")
|
||||
callback_data = msg_json.get("actions")[0].get("value")
|
||||
@@ -270,6 +272,7 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
username = ""
|
||||
images = self._extract_images(msg_json.get("event", {}))
|
||||
audio_refs = self._extract_audio_refs(msg_json.get("event", {}))
|
||||
files = self._extract_files(msg_json.get("event", {}))
|
||||
elif msg_json.get("type") == "shortcut":
|
||||
userid = msg_json.get("user", {}).get("id")
|
||||
text = msg_json.get("callback_id")
|
||||
@@ -282,7 +285,8 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
return None
|
||||
logger.info(
|
||||
f"收到来自 {client_config.name} 的Slack消息:userid={userid}, username={username}, "
|
||||
f"text={text}, images={len(images) if images else 0}, audios={len(audio_refs) if audio_refs else 0}"
|
||||
f"text={text}, images={len(images) if images else 0}, audios={len(audio_refs) if audio_refs else 0}, "
|
||||
f"files={len(files) if files else 0}"
|
||||
)
|
||||
return CommingMessage(
|
||||
channel=MessageChannel.Slack,
|
||||
@@ -292,6 +296,7 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
text=text,
|
||||
images=images,
|
||||
audio_refs=audio_refs,
|
||||
files=files,
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -341,6 +346,48 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
audio_refs.append(f"slack://file/{quote(url, safe='')}")
|
||||
return audio_refs if audio_refs else None
|
||||
|
||||
@classmethod
|
||||
def _extract_files(
|
||||
cls, msg_json: dict
|
||||
) -> Optional[List[CommingMessage.MessageAttachment]]:
|
||||
"""
|
||||
从 Slack 消息中提取非图片/非音频文件。
|
||||
"""
|
||||
files = msg_json.get("files", [])
|
||||
if not files:
|
||||
return None
|
||||
|
||||
attachments = []
|
||||
for file in files:
|
||||
file_type = str(file.get("type", "")).lower()
|
||||
file_ext = f".{str(file.get('filetype', '')).lower().lstrip('.')}"
|
||||
mime_type = str(file.get("mimetype", "")).lower()
|
||||
is_image = (
|
||||
file_type == "image"
|
||||
or file_ext in (".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp")
|
||||
or mime_type.startswith("image/")
|
||||
)
|
||||
is_audio = (
|
||||
file_type == "audio"
|
||||
or mime_type.startswith("audio/")
|
||||
or file_ext in cls._AUDIO_SUFFIXES
|
||||
)
|
||||
if is_image or is_audio:
|
||||
continue
|
||||
|
||||
url = file.get("url_private_download") or file.get("url_private")
|
||||
if not url:
|
||||
continue
|
||||
attachments.append(
|
||||
CommingMessage.MessageAttachment(
|
||||
ref=f"slack://file/{quote(url, safe='')}",
|
||||
name=file.get("name") or file.get("title"),
|
||||
mime_type=file.get("mimetype"),
|
||||
size=file.get("size"),
|
||||
)
|
||||
)
|
||||
return attachments or None
|
||||
|
||||
def download_slack_file_to_data_url(self, file_url: str, source: str) -> Optional[str]:
|
||||
"""
|
||||
下载Slack文件并转为data URL
|
||||
@@ -399,16 +446,25 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
return
|
||||
client: Slack = self.get_instance(conf.name)
|
||||
if client:
|
||||
client.send_msg(
|
||||
title=message.title,
|
||||
text=message.text,
|
||||
image=message.image,
|
||||
userid=userid,
|
||||
link=message.link,
|
||||
buttons=message.buttons,
|
||||
original_message_id=message.original_message_id,
|
||||
original_chat_id=message.original_chat_id,
|
||||
)
|
||||
if message.file_path:
|
||||
client.send_file(
|
||||
file_path=message.file_path,
|
||||
file_name=message.file_name,
|
||||
title=message.title,
|
||||
text=message.text,
|
||||
userid=userid,
|
||||
)
|
||||
else:
|
||||
client.send_msg(
|
||||
title=message.title,
|
||||
text=message.text,
|
||||
image=message.image,
|
||||
userid=userid,
|
||||
link=message.link,
|
||||
buttons=message.buttons,
|
||||
original_message_id=message.original_message_id,
|
||||
original_chat_id=message.original_chat_id,
|
||||
)
|
||||
|
||||
def post_medias_message(
|
||||
self, message: Notification, medias: List[MediaInfo]
|
||||
@@ -538,26 +594,40 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
return None
|
||||
client: Slack = self.get_instance(conf.name)
|
||||
if client:
|
||||
result = client.send_msg(
|
||||
title=message.title or "",
|
||||
text=message.text,
|
||||
userid=userid,
|
||||
)
|
||||
if message.file_path:
|
||||
result = client.send_file(
|
||||
file_path=message.file_path,
|
||||
file_name=message.file_name,
|
||||
title=message.title,
|
||||
text=message.text,
|
||||
userid=userid,
|
||||
)
|
||||
else:
|
||||
result = client.send_msg(
|
||||
title=message.title or "",
|
||||
text=message.text,
|
||||
userid=userid,
|
||||
)
|
||||
if result and result[0]:
|
||||
# Slack 使用时间戳作为 message_id,chat_id 是频道ID
|
||||
# 注意:这里返回的是发送后的结果,需要获取实际的 message_id
|
||||
# 由于 Slack API 返回的是 result[1],包含完整响应,我们需要从中提取
|
||||
response_data = result[1]
|
||||
message_id = (
|
||||
response_data.get("ts")
|
||||
if isinstance(response_data, dict)
|
||||
else None
|
||||
)
|
||||
channel_id = (
|
||||
response_data.get("channel")
|
||||
if isinstance(response_data, dict)
|
||||
else None
|
||||
)
|
||||
message_id = None
|
||||
channel_id = None
|
||||
if hasattr(response_data, "get"):
|
||||
message_id = response_data.get("ts")
|
||||
channel_id = response_data.get("channel")
|
||||
if not message_id and hasattr(response_data, "data"):
|
||||
files = (response_data.data or {}).get("files") or []
|
||||
if files:
|
||||
message_id = files[0].get("id")
|
||||
shares = (
|
||||
files[0].get("shares", {})
|
||||
.get("private", {})
|
||||
)
|
||||
if shares:
|
||||
channel_id = next(iter(shares.keys()), None)
|
||||
return MessageResponse(
|
||||
message_id=message_id,
|
||||
chat_id=channel_id,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import re
|
||||
from threading import Lock
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Tuple
|
||||
from urllib.parse import quote
|
||||
|
||||
@@ -246,6 +247,48 @@ class Slack:
|
||||
logger.error(f"Slack消息发送失败: {msg_e}")
|
||||
return False, str(msg_e)
|
||||
|
||||
def send_file(
|
||||
self,
|
||||
file_path: str,
|
||||
title: Optional[str] = None,
|
||||
text: Optional[str] = None,
|
||||
userid: Optional[str] = None,
|
||||
file_name: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
发送本地文件到 Slack。
|
||||
"""
|
||||
if not self._client:
|
||||
return False, "消息客户端未就绪"
|
||||
if not file_path:
|
||||
return False, "文件路径不能为空"
|
||||
|
||||
local_file = Path(file_path)
|
||||
if not local_file.exists() or not local_file.is_file():
|
||||
return False, f"文件不存在: {local_file}"
|
||||
|
||||
try:
|
||||
if userid:
|
||||
channel = userid
|
||||
else:
|
||||
channel = self.__find_public_channel()
|
||||
|
||||
comment_parts = [part for part in [title, text] if part]
|
||||
initial_comment = "\n".join(comment_parts) if comment_parts else None
|
||||
|
||||
with local_file.open("rb") as fp:
|
||||
result = self._client.files_upload_v2(
|
||||
channel=channel,
|
||||
file=fp,
|
||||
filename=file_name or local_file.name,
|
||||
title=title or (file_name or local_file.name),
|
||||
initial_comment=initial_comment,
|
||||
)
|
||||
return True, result
|
||||
except Exception as err:
|
||||
logger.error(f"Slack文件发送失败: {err}")
|
||||
return False, str(err)
|
||||
|
||||
def send_medias_msg(self, medias: List[MediaInfo], userid: Optional[str] = None, title: Optional[str] = None,
|
||||
buttons: Optional[List[List[dict]]] = None,
|
||||
original_message_id: Optional[str] = None,
|
||||
|
||||
Reference in New Issue
Block a user