mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-10 23:11:47 +08:00
feat(agent): add audio message extraction and download support for Slack, QQ, Discord, SynologyChat, and VoceChat
This commit is contained in:
@@ -5,6 +5,7 @@ QQ Bot 通知模块
|
||||
"""
|
||||
|
||||
import json
|
||||
from urllib.parse import quote, unquote
|
||||
from typing import Optional, List, Tuple, Union, Any
|
||||
|
||||
from app.core.context import MediaInfo, Context
|
||||
@@ -13,6 +14,7 @@ from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.qqbot.qqbot import QQBot
|
||||
from app.schemas import CommingMessage, MessageChannel, Notification
|
||||
from app.schemas.types import ModuleType
|
||||
from app.utils.http import RequestUtils
|
||||
|
||||
|
||||
class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
@@ -28,6 +30,20 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
".tiff",
|
||||
".svg",
|
||||
)
|
||||
_AUDIO_SUFFIXES = (
|
||||
".mp3",
|
||||
".m4a",
|
||||
".wav",
|
||||
".ogg",
|
||||
".oga",
|
||||
".opus",
|
||||
".aac",
|
||||
".amr",
|
||||
".flac",
|
||||
".mpga",
|
||||
".mpeg",
|
||||
".webm",
|
||||
)
|
||||
|
||||
def init_module(self) -> None:
|
||||
self.stop()
|
||||
@@ -90,7 +106,8 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
msg_type = msg_body.get("type")
|
||||
content = (msg_body.get("content") or "").strip()
|
||||
images = self._extract_images(msg_body)
|
||||
if not content and not images:
|
||||
audio_refs = self._extract_audio_refs(msg_body)
|
||||
if not content and not images and not audio_refs:
|
||||
return None
|
||||
|
||||
if msg_type == "C2C_MESSAGE_CREATE":
|
||||
@@ -100,7 +117,8 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
return None
|
||||
logger.info(
|
||||
f"收到 QQ 私聊消息: userid={user_openid}, "
|
||||
f"text={(content or '')[:50]}..., images={len(images) if images else 0}"
|
||||
f"text={(content or '')[:50]}..., images={len(images) if images else 0}, "
|
||||
f"audios={len(audio_refs) if audio_refs else 0}"
|
||||
)
|
||||
return CommingMessage(
|
||||
channel=MessageChannel.QQ,
|
||||
@@ -109,6 +127,7 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
username=user_openid,
|
||||
text=content,
|
||||
images=images,
|
||||
audio_refs=audio_refs,
|
||||
)
|
||||
elif msg_type == "GROUP_AT_MESSAGE_CREATE":
|
||||
author = msg_body.get("author", {})
|
||||
@@ -118,7 +137,8 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
userid = f"group:{group_openid}" if group_openid else member_openid
|
||||
logger.info(
|
||||
f"收到 QQ 群消息: group={group_openid}, userid={member_openid}, "
|
||||
f"text={(content or '')[:50]}..., images={len(images) if images else 0}"
|
||||
f"text={(content or '')[:50]}..., images={len(images) if images else 0}, "
|
||||
f"audios={len(audio_refs) if audio_refs else 0}"
|
||||
)
|
||||
return CommingMessage(
|
||||
channel=MessageChannel.QQ,
|
||||
@@ -127,6 +147,7 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
username=member_openid or group_openid,
|
||||
text=content,
|
||||
images=images,
|
||||
audio_refs=audio_refs,
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -175,6 +196,50 @@ class QQBotModule(_ModuleBase, _MessageBase[QQBot]):
|
||||
deduped.append(image)
|
||||
return deduped or None
|
||||
|
||||
@classmethod
|
||||
def _extract_audio_refs(cls, msg_body: dict) -> Optional[List[str]]:
|
||||
audio_refs: List[str] = []
|
||||
attachments = msg_body.get("attachments") or []
|
||||
if isinstance(attachments, list):
|
||||
for attachment in attachments:
|
||||
if not isinstance(attachment, dict):
|
||||
continue
|
||||
url = attachment.get("url") or attachment.get("proxy_url")
|
||||
if not url:
|
||||
continue
|
||||
content_type = (
|
||||
attachment.get("content_type")
|
||||
or attachment.get("mime_type")
|
||||
or ""
|
||||
).lower()
|
||||
filename = (
|
||||
attachment.get("filename")
|
||||
or attachment.get("name")
|
||||
or ""
|
||||
).lower()
|
||||
if content_type.startswith("audio/") or filename.endswith(cls._AUDIO_SUFFIXES):
|
||||
audio_refs.append(f"qq://file/{quote(url, safe='')}")
|
||||
|
||||
deduped = []
|
||||
for audio_ref in audio_refs:
|
||||
if audio_ref not in deduped:
|
||||
deduped.append(audio_ref)
|
||||
return deduped or None
|
||||
|
||||
def download_qq_file_bytes(self, file_ref: str, source: str) -> Optional[bytes]:
|
||||
"""
|
||||
下载QQ音频附件并返回原始字节
|
||||
"""
|
||||
if not file_ref or not file_ref.startswith("qq://file/"):
|
||||
return None
|
||||
if not self.get_config(source):
|
||||
return None
|
||||
file_url = unquote(file_ref.replace("qq://file/", "", 1))
|
||||
resp = RequestUtils(timeout=30).get_res(file_url)
|
||||
if resp and resp.content:
|
||||
return resp.content
|
||||
return None
|
||||
|
||||
def post_message(self, message: Notification, **kwargs) -> None:
|
||||
for conf in self.get_configs().values():
|
||||
if not self.check_message(message, conf.name):
|
||||
|
||||
Reference in New Issue
Block a user