fix: handle MEDIA tags in send_message tool for native file delivery
The send_message tool's _send_telegram() sent MEDIA:<path> tags as literal text instead of delivering actual files. This fixes it by extracting MEDIA tags via BasePlatformAdapter.extract_media() and routing files to the appropriate Telegram Bot API method by extension. Changes: - send_message_tool: extract MEDIA tags and send files natively as photo/video/voice/audio/document based on file extension - send_message_tool: add per-file error handling and missing-file logging - send_message_tool: use cleaned text in fallback to avoid leaking tags - base.py extract_media: handle optional space after MEDIA: colon - base.py extract_media: strip surrounding backticks/quotes from paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9525db913f
commit
50d6659392
2 changed files with 63 additions and 9 deletions
|
|
@ -618,16 +618,16 @@ class BasePlatformAdapter(ABC):
|
||||||
has_voice_tag = "[[audio_as_voice]]" in content
|
has_voice_tag = "[[audio_as_voice]]" in content
|
||||||
cleaned = cleaned.replace("[[audio_as_voice]]", "")
|
cleaned = cleaned.replace("[[audio_as_voice]]", "")
|
||||||
|
|
||||||
# Extract MEDIA:<path> tags (path may contain spaces)
|
# Extract MEDIA:<path> tags
|
||||||
media_pattern = r'MEDIA:(\S+)'
|
media_pattern = r'MEDIA:\s*(\S+)'
|
||||||
for match in re.finditer(media_pattern, content):
|
for match in re.finditer(media_pattern, content):
|
||||||
path = match.group(1).strip()
|
path = match.group(1).strip().rstrip('`"\',)}')
|
||||||
if path:
|
if path:
|
||||||
media.append((path, has_voice_tag))
|
media.append((path, has_voice_tag))
|
||||||
|
|
||||||
# Remove MEDIA tags from content
|
# Remove MEDIA tags from content (including surrounding backticks/quotes)
|
||||||
if media:
|
if media:
|
||||||
cleaned = re.sub(media_pattern, '', cleaned)
|
cleaned = re.sub(r'[`"\']*MEDIA:\s*\S+[`"\']*', '', cleaned)
|
||||||
cleaned = re.sub(r'\n{3,}', '\n\n', cleaned).strip()
|
cleaned = re.sub(r'\n{3,}', '\n\n', cleaned).strip()
|
||||||
|
|
||||||
return media, cleaned
|
return media, cleaned
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_TELEGRAM_TOPIC_TARGET_RE = re.compile(r"^\s*(-?\d+)(?::(\d+))?\s*$")
|
_TELEGRAM_TOPIC_TARGET_RE = re.compile(r"^\s*(-?\d+)(?::(\d+))?\s*$")
|
||||||
|
|
||||||
|
_IMAGE_EXTS = {'.jpg', '.jpeg', '.png', '.webp', '.gif'}
|
||||||
|
_VIDEO_EXTS = {'.mp4', '.mov', '.avi', '.mkv', '.3gp'}
|
||||||
|
_AUDIO_EXTS = {'.ogg', '.opus', '.mp3', '.wav', '.m4a'}
|
||||||
|
_VOICE_EXTS = {'.ogg', '.opus'}
|
||||||
|
|
||||||
|
|
||||||
SEND_MESSAGE_SCHEMA = {
|
SEND_MESSAGE_SCHEMA = {
|
||||||
"name": "send_message",
|
"name": "send_message",
|
||||||
|
|
@ -195,12 +200,61 @@ async def _send_telegram(token, chat_id, message, thread_id=None):
|
||||||
"""Send via Telegram Bot API (one-shot, no polling needed)."""
|
"""Send via Telegram Bot API (one-shot, no polling needed)."""
|
||||||
try:
|
try:
|
||||||
from telegram import Bot
|
from telegram import Bot
|
||||||
|
from gateway.platforms.base import BasePlatformAdapter
|
||||||
bot = Bot(token=token)
|
bot = Bot(token=token)
|
||||||
send_kwargs = {"chat_id": int(chat_id), "text": message}
|
int_chat_id = int(chat_id)
|
||||||
|
thread_kwargs = {}
|
||||||
if thread_id is not None:
|
if thread_id is not None:
|
||||||
send_kwargs["message_thread_id"] = int(thread_id)
|
thread_kwargs["message_thread_id"] = int(thread_id)
|
||||||
msg = await bot.send_message(**send_kwargs)
|
|
||||||
return {"success": True, "platform": "telegram", "chat_id": chat_id, "message_id": str(msg.message_id)}
|
# Extract MEDIA:<path> tags and send files natively
|
||||||
|
media_files, cleaned = BasePlatformAdapter.extract_media(message)
|
||||||
|
|
||||||
|
last_msg = None
|
||||||
|
# Send text portion if any remains
|
||||||
|
if cleaned.strip():
|
||||||
|
last_msg = await bot.send_message(
|
||||||
|
chat_id=int_chat_id, text=cleaned, **thread_kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
# Send extracted media files
|
||||||
|
for media_path, is_voice in media_files:
|
||||||
|
if not os.path.exists(media_path):
|
||||||
|
logger.warning("Media file not found, skipping: %s", media_path)
|
||||||
|
continue
|
||||||
|
ext = os.path.splitext(media_path)[1].lower()
|
||||||
|
try:
|
||||||
|
with open(media_path, "rb") as f:
|
||||||
|
if ext in _IMAGE_EXTS:
|
||||||
|
last_msg = await bot.send_photo(
|
||||||
|
chat_id=int_chat_id, photo=f, **thread_kwargs
|
||||||
|
)
|
||||||
|
elif ext in _VIDEO_EXTS:
|
||||||
|
last_msg = await bot.send_video(
|
||||||
|
chat_id=int_chat_id, video=f, **thread_kwargs
|
||||||
|
)
|
||||||
|
elif ext in _VOICE_EXTS and is_voice:
|
||||||
|
last_msg = await bot.send_voice(
|
||||||
|
chat_id=int_chat_id, voice=f, **thread_kwargs
|
||||||
|
)
|
||||||
|
elif ext in _AUDIO_EXTS:
|
||||||
|
last_msg = await bot.send_audio(
|
||||||
|
chat_id=int_chat_id, audio=f, **thread_kwargs
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
last_msg = await bot.send_document(
|
||||||
|
chat_id=int_chat_id, document=f, **thread_kwargs
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to send media %s: %s", media_path, e)
|
||||||
|
|
||||||
|
# If no text and no media sent, send cleaned text as fallback
|
||||||
|
if last_msg is None:
|
||||||
|
last_msg = await bot.send_message(
|
||||||
|
chat_id=int_chat_id, text=cleaned if cleaned.strip() else message, **thread_kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"success": True, "platform": "telegram", "chat_id": chat_id, "message_id": str(last_msg.message_id)}
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return {"error": "python-telegram-bot not installed. Run: pip install python-telegram-bot"}
|
return {"error": "python-telegram-bot not installed. Run: pip install python-telegram-bot"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue