mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-07-09 17:08:29 +00:00
Send Telegram media attachments natively
This commit is contained in:
parent
a17cef5b03
commit
97ef4797e0
3 changed files with 207 additions and 0 deletions
|
|
@ -526,6 +526,12 @@ async def send_telegram_reply(
|
|||
local_path = files.fix_dev_path(path)
|
||||
if tc.is_image_file(local_path):
|
||||
await tc.send_photo(reply_bot, chat_id, local_path, reply_to_message_id=reply_to)
|
||||
elif tc.is_voice_file(local_path):
|
||||
await tc.send_voice(reply_bot, chat_id, local_path, reply_to_message_id=reply_to)
|
||||
elif tc.is_audio_file(local_path):
|
||||
await tc.send_audio(reply_bot, chat_id, local_path, reply_to_message_id=reply_to)
|
||||
elif tc.is_video_file(local_path):
|
||||
await tc.send_video(reply_bot, chat_id, local_path, reply_to_message_id=reply_to)
|
||||
else:
|
||||
await tc.send_file(reply_bot, chat_id, local_path, reply_to_message_id=reply_to)
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,81 @@ async def send_photo(
|
|||
return None
|
||||
|
||||
|
||||
async def send_voice(
|
||||
bot: Bot,
|
||||
chat_id: int,
|
||||
voice_path: str,
|
||||
caption: str = "",
|
||||
reply_to_message_id: int | None = None,
|
||||
) -> int | None:
|
||||
"""Send a voice message from local path. Returns message_id or None on error."""
|
||||
try:
|
||||
if not os.path.isfile(voice_path):
|
||||
PrintStyle.error(f"Telegram: voice file not found: {voice_path}")
|
||||
return None
|
||||
input_file = FSInputFile(voice_path)
|
||||
msg = await bot.send_voice(
|
||||
chat_id=chat_id,
|
||||
voice=input_file,
|
||||
caption=caption[:1024] if caption else None,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
)
|
||||
return msg.message_id
|
||||
except Exception as e:
|
||||
PrintStyle.error(f"Telegram send_voice failed: {format_error(e)}")
|
||||
return None
|
||||
|
||||
|
||||
async def send_audio(
|
||||
bot: Bot,
|
||||
chat_id: int,
|
||||
audio_path: str,
|
||||
caption: str = "",
|
||||
reply_to_message_id: int | None = None,
|
||||
) -> int | None:
|
||||
"""Send an audio message from local path. Returns message_id or None on error."""
|
||||
try:
|
||||
if not os.path.isfile(audio_path):
|
||||
PrintStyle.error(f"Telegram: audio file not found: {audio_path}")
|
||||
return None
|
||||
input_file = FSInputFile(audio_path)
|
||||
msg = await bot.send_audio(
|
||||
chat_id=chat_id,
|
||||
audio=input_file,
|
||||
caption=caption[:1024] if caption else None,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
)
|
||||
return msg.message_id
|
||||
except Exception as e:
|
||||
PrintStyle.error(f"Telegram send_audio failed: {format_error(e)}")
|
||||
return None
|
||||
|
||||
|
||||
async def send_video(
|
||||
bot: Bot,
|
||||
chat_id: int,
|
||||
video_path: str,
|
||||
caption: str = "",
|
||||
reply_to_message_id: int | None = None,
|
||||
) -> int | None:
|
||||
"""Send a video message from local path. Returns message_id or None on error."""
|
||||
try:
|
||||
if not os.path.isfile(video_path):
|
||||
PrintStyle.error(f"Telegram: video file not found: {video_path}")
|
||||
return None
|
||||
input_file = FSInputFile(video_path)
|
||||
msg = await bot.send_video(
|
||||
chat_id=chat_id,
|
||||
video=input_file,
|
||||
caption=caption[:1024] if caption else None,
|
||||
reply_to_message_id=reply_to_message_id,
|
||||
)
|
||||
return msg.message_id
|
||||
except Exception as e:
|
||||
PrintStyle.error(f"Telegram send_video failed: {format_error(e)}")
|
||||
return None
|
||||
|
||||
|
||||
# Inline keyboards
|
||||
|
||||
def build_inline_keyboard(
|
||||
|
|
@ -294,6 +369,9 @@ def _split_text(text: str, max_len: int) -> list[str]:
|
|||
|
||||
|
||||
_IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"}
|
||||
_VOICE_EXTENSIONS = {".ogg", ".oga", ".opus"}
|
||||
_AUDIO_EXTENSIONS = {".mp3", ".m4a", ".aac", ".wav", ".flac"}
|
||||
_VIDEO_EXTENSIONS = {".mp4", ".m4v", ".mov", ".webm"}
|
||||
|
||||
|
||||
def is_image_file(path: str) -> bool:
|
||||
|
|
@ -301,6 +379,21 @@ def is_image_file(path: str) -> bool:
|
|||
return ext in _IMAGE_EXTENSIONS
|
||||
|
||||
|
||||
def is_voice_file(path: str) -> bool:
|
||||
_, ext = os.path.splitext(path.lower())
|
||||
return ext in _VOICE_EXTENSIONS
|
||||
|
||||
|
||||
def is_audio_file(path: str) -> bool:
|
||||
_, ext = os.path.splitext(path.lower())
|
||||
return ext in _AUDIO_EXTENSIONS
|
||||
|
||||
|
||||
def is_video_file(path: str) -> bool:
|
||||
_, ext = os.path.splitext(path.lower())
|
||||
return ext in _VIDEO_EXTENSIONS
|
||||
|
||||
|
||||
def md_to_telegram_html(text: str) -> str:
|
||||
"""Convert Markdown to Telegram-compatible HTML."""
|
||||
stash: list[str] = []
|
||||
|
|
|
|||
108
tests/test_telegram_media_delivery.py
Normal file
108
tests/test_telegram_media_delivery.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import asyncio
|
||||
|
||||
from plugins._telegram_integration.helpers import handler
|
||||
from plugins._telegram_integration.helpers import telegram_client as tc
|
||||
from plugins._telegram_integration.helpers.constants import (
|
||||
CTX_TG_BOT,
|
||||
CTX_TG_CHAT_ID,
|
||||
CTX_TG_REPLY_TO,
|
||||
)
|
||||
|
||||
|
||||
class FakeBotInstance:
|
||||
class Bot:
|
||||
token = "token"
|
||||
|
||||
bot = Bot()
|
||||
|
||||
|
||||
class FakeContext:
|
||||
def __init__(self):
|
||||
self.data = {
|
||||
CTX_TG_BOT: "main",
|
||||
CTX_TG_CHAT_ID: 123,
|
||||
CTX_TG_REPLY_TO: 456,
|
||||
}
|
||||
|
||||
|
||||
class FakeTempBot:
|
||||
async def __aenter__(self):
|
||||
return object()
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
|
||||
class FakeMediaBot:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
async def send_voice(self, **kwargs):
|
||||
self.calls.append(("voice", kwargs))
|
||||
return type("Message", (), {"message_id": 11})()
|
||||
|
||||
async def send_audio(self, **kwargs):
|
||||
self.calls.append(("audio", kwargs))
|
||||
return type("Message", (), {"message_id": 12})()
|
||||
|
||||
async def send_video(self, **kwargs):
|
||||
self.calls.append(("video", kwargs))
|
||||
return type("Message", (), {"message_id": 13})()
|
||||
|
||||
|
||||
def test_telegram_client_native_media_helpers(monkeypatch):
|
||||
bot = FakeMediaBot()
|
||||
monkeypatch.setattr(tc.os.path, "isfile", lambda path: True)
|
||||
|
||||
voice_id = asyncio.run(tc.send_voice(bot, 123, "voice.ogg", reply_to_message_id=456))
|
||||
audio_id = asyncio.run(tc.send_audio(bot, 123, "song.mp3", reply_to_message_id=456))
|
||||
video_id = asyncio.run(tc.send_video(bot, 123, "clip.mp4", reply_to_message_id=456))
|
||||
|
||||
assert (voice_id, audio_id, video_id) == (11, 12, 13)
|
||||
assert [kind for kind, _ in bot.calls] == ["voice", "audio", "video"]
|
||||
assert bot.calls[0][1]["chat_id"] == 123
|
||||
assert bot.calls[0][1]["reply_to_message_id"] == 456
|
||||
|
||||
|
||||
def test_telegram_reply_routes_native_media_attachments(monkeypatch):
|
||||
calls = []
|
||||
|
||||
def fake_temp_bot(*args, **kwargs):
|
||||
return FakeTempBot()
|
||||
|
||||
async def record(kind, bot, chat_id, path, reply_to_message_id=None, **kwargs):
|
||||
calls.append(
|
||||
{
|
||||
"kind": kind,
|
||||
"chat_id": chat_id,
|
||||
"path": path,
|
||||
"reply_to_message_id": reply_to_message_id,
|
||||
}
|
||||
)
|
||||
return len(calls)
|
||||
|
||||
monkeypatch.setattr(handler, "get_bot", lambda name: FakeBotInstance())
|
||||
monkeypatch.setattr(handler, "_temp_bot", fake_temp_bot)
|
||||
monkeypatch.setattr(handler.files, "fix_dev_path", lambda path: path)
|
||||
monkeypatch.setattr(handler.tc, "send_photo", lambda *a, **kw: record("photo", *a, **kw))
|
||||
monkeypatch.setattr(handler.tc, "send_voice", lambda *a, **kw: record("voice", *a, **kw))
|
||||
monkeypatch.setattr(handler.tc, "send_audio", lambda *a, **kw: record("audio", *a, **kw))
|
||||
monkeypatch.setattr(handler.tc, "send_video", lambda *a, **kw: record("video", *a, **kw))
|
||||
monkeypatch.setattr(handler.tc, "send_file", lambda *a, **kw: record("document", *a, **kw))
|
||||
|
||||
error = asyncio.run(
|
||||
handler.send_telegram_reply(
|
||||
FakeContext(),
|
||||
"",
|
||||
["image.png", "voice.ogg", "song.mp3", "clip.mp4", "archive.zip"],
|
||||
)
|
||||
)
|
||||
|
||||
assert error is None
|
||||
assert calls == [
|
||||
{"kind": "photo", "chat_id": 123, "path": "image.png", "reply_to_message_id": 456},
|
||||
{"kind": "voice", "chat_id": 123, "path": "voice.ogg", "reply_to_message_id": 456},
|
||||
{"kind": "audio", "chat_id": 123, "path": "song.mp3", "reply_to_message_id": 456},
|
||||
{"kind": "video", "chat_id": 123, "path": "clip.mp4", "reply_to_message_id": 456},
|
||||
{"kind": "document", "chat_id": 123, "path": "archive.zip", "reply_to_message_id": 456},
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue