Merge PR #815: reply to original Telegram message using message_id
This commit is contained in:
@@ -134,11 +134,11 @@ class AgentLoop:
|
|||||||
await self._mcp_stack.__aenter__()
|
await self._mcp_stack.__aenter__()
|
||||||
await connect_mcp_servers(self._mcp_servers, self.tools, self._mcp_stack)
|
await connect_mcp_servers(self._mcp_servers, self.tools, self._mcp_stack)
|
||||||
|
|
||||||
def _set_tool_context(self, channel: str, chat_id: str) -> None:
|
def _set_tool_context(self, channel: str, chat_id: str, message_id: str | None = None) -> None:
|
||||||
"""Update context for all tools that need routing info."""
|
"""Update context for all tools that need routing info."""
|
||||||
if message_tool := self.tools.get("message"):
|
if message_tool := self.tools.get("message"):
|
||||||
if isinstance(message_tool, MessageTool):
|
if isinstance(message_tool, MessageTool):
|
||||||
message_tool.set_context(channel, chat_id)
|
message_tool.set_context(channel, chat_id, message_id)
|
||||||
|
|
||||||
if spawn_tool := self.tools.get("spawn"):
|
if spawn_tool := self.tools.get("spawn"):
|
||||||
if isinstance(spawn_tool, SpawnTool):
|
if isinstance(spawn_tool, SpawnTool):
|
||||||
@@ -342,7 +342,7 @@ class AgentLoop:
|
|||||||
|
|
||||||
asyncio.create_task(_consolidate_and_unlock())
|
asyncio.create_task(_consolidate_and_unlock())
|
||||||
|
|
||||||
self._set_tool_context(msg.channel, msg.chat_id)
|
self._set_tool_context(msg.channel, msg.chat_id, msg.metadata.get("message_id"))
|
||||||
initial_messages = self.context.build_messages(
|
initial_messages = self.context.build_messages(
|
||||||
history=session.get_history(max_messages=self.memory_window),
|
history=session.get_history(max_messages=self.memory_window),
|
||||||
current_message=msg.content,
|
current_message=msg.content,
|
||||||
@@ -400,7 +400,7 @@ class AgentLoop:
|
|||||||
|
|
||||||
session_key = f"{origin_channel}:{origin_chat_id}"
|
session_key = f"{origin_channel}:{origin_chat_id}"
|
||||||
session = self.sessions.get_or_create(session_key)
|
session = self.sessions.get_or_create(session_key)
|
||||||
self._set_tool_context(origin_channel, origin_chat_id)
|
self._set_tool_context(origin_channel, origin_chat_id, msg.metadata.get("message_id"))
|
||||||
initial_messages = self.context.build_messages(
|
initial_messages = self.context.build_messages(
|
||||||
history=session.get_history(max_messages=self.memory_window),
|
history=session.get_history(max_messages=self.memory_window),
|
||||||
current_message=msg.content,
|
current_message=msg.content,
|
||||||
|
|||||||
@@ -13,16 +13,19 @@ class MessageTool(Tool):
|
|||||||
self,
|
self,
|
||||||
send_callback: Callable[[OutboundMessage], Awaitable[None]] | None = None,
|
send_callback: Callable[[OutboundMessage], Awaitable[None]] | None = None,
|
||||||
default_channel: str = "",
|
default_channel: str = "",
|
||||||
default_chat_id: str = ""
|
default_chat_id: str = "",
|
||||||
|
default_message_id: str | None = None
|
||||||
):
|
):
|
||||||
self._send_callback = send_callback
|
self._send_callback = send_callback
|
||||||
self._default_channel = default_channel
|
self._default_channel = default_channel
|
||||||
self._default_chat_id = default_chat_id
|
self._default_chat_id = default_chat_id
|
||||||
|
self._default_message_id = default_message_id
|
||||||
|
|
||||||
def set_context(self, channel: str, chat_id: str) -> None:
|
def set_context(self, channel: str, chat_id: str, message_id: str | None = None) -> None:
|
||||||
"""Set the current message context."""
|
"""Set the current message context."""
|
||||||
self._default_channel = channel
|
self._default_channel = channel
|
||||||
self._default_chat_id = chat_id
|
self._default_chat_id = chat_id
|
||||||
|
self._default_message_id = message_id
|
||||||
|
|
||||||
def set_send_callback(self, callback: Callable[[OutboundMessage], Awaitable[None]]) -> None:
|
def set_send_callback(self, callback: Callable[[OutboundMessage], Awaitable[None]]) -> None:
|
||||||
"""Set the callback for sending messages."""
|
"""Set the callback for sending messages."""
|
||||||
@@ -67,11 +70,13 @@ class MessageTool(Tool):
|
|||||||
content: str,
|
content: str,
|
||||||
channel: str | None = None,
|
channel: str | None = None,
|
||||||
chat_id: str | None = None,
|
chat_id: str | None = None,
|
||||||
|
message_id: str | None = None,
|
||||||
media: list[str] | None = None,
|
media: list[str] | None = None,
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
) -> str:
|
) -> str:
|
||||||
channel = channel or self._default_channel
|
channel = channel or self._default_channel
|
||||||
chat_id = chat_id or self._default_chat_id
|
chat_id = chat_id or self._default_chat_id
|
||||||
|
message_id = message_id or self._default_message_id
|
||||||
|
|
||||||
if not channel or not chat_id:
|
if not channel or not chat_id:
|
||||||
return "Error: No target channel/chat specified"
|
return "Error: No target channel/chat specified"
|
||||||
@@ -83,7 +88,10 @@ class MessageTool(Tool):
|
|||||||
channel=channel,
|
channel=channel,
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
content=content,
|
content=content,
|
||||||
media=media or []
|
media=media or [],
|
||||||
|
metadata={
|
||||||
|
"message_id": message_id,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
import re
|
import re
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from telegram import BotCommand, Update
|
from telegram import BotCommand, Update, ReplyParameters
|
||||||
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
|
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
|
||||||
from telegram.request import HTTPXRequest
|
from telegram.request import HTTPXRequest
|
||||||
|
|
||||||
@@ -224,6 +224,15 @@ class TelegramChannel(BaseChannel):
|
|||||||
logger.error("Invalid chat_id: {}", msg.chat_id)
|
logger.error("Invalid chat_id: {}", msg.chat_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Build reply parameters (Will reply to the message if it exists)
|
||||||
|
reply_to_message_id = msg.metadata.get("message_id")
|
||||||
|
reply_params = None
|
||||||
|
if reply_to_message_id:
|
||||||
|
reply_params = ReplyParameters(
|
||||||
|
message_id=reply_to_message_id,
|
||||||
|
allow_sending_without_reply=True
|
||||||
|
)
|
||||||
|
|
||||||
# Send media files
|
# Send media files
|
||||||
for media_path in (msg.media or []):
|
for media_path in (msg.media or []):
|
||||||
try:
|
try:
|
||||||
@@ -235,22 +244,39 @@ class TelegramChannel(BaseChannel):
|
|||||||
}.get(media_type, self._app.bot.send_document)
|
}.get(media_type, self._app.bot.send_document)
|
||||||
param = "photo" if media_type == "photo" else media_type if media_type in ("voice", "audio") else "document"
|
param = "photo" if media_type == "photo" else media_type if media_type in ("voice", "audio") else "document"
|
||||||
with open(media_path, 'rb') as f:
|
with open(media_path, 'rb') as f:
|
||||||
await sender(chat_id=chat_id, **{param: f})
|
await sender(
|
||||||
|
chat_id=chat_id,
|
||||||
|
**{param: f},
|
||||||
|
reply_parameters=reply_params
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
filename = media_path.rsplit("/", 1)[-1]
|
filename = media_path.rsplit("/", 1)[-1]
|
||||||
logger.error("Failed to send media {}: {}", media_path, e)
|
logger.error("Failed to send media {}: {}", media_path, e)
|
||||||
await self._app.bot.send_message(chat_id=chat_id, text=f"[Failed to send: {filename}]")
|
await self._app.bot.send_message(
|
||||||
|
chat_id=chat_id,
|
||||||
|
text=f"[Failed to send: {filename}]",
|
||||||
|
reply_parameters=reply_params
|
||||||
|
)
|
||||||
|
|
||||||
# Send text content
|
# Send text content
|
||||||
if msg.content and msg.content != "[empty message]":
|
if msg.content and msg.content != "[empty message]":
|
||||||
for chunk in _split_message(msg.content):
|
for chunk in _split_message(msg.content):
|
||||||
try:
|
try:
|
||||||
html = _markdown_to_telegram_html(chunk)
|
html = _markdown_to_telegram_html(chunk)
|
||||||
await self._app.bot.send_message(chat_id=chat_id, text=html, parse_mode="HTML")
|
await self._app.bot.send_message(
|
||||||
|
chat_id=chat_id,
|
||||||
|
text=html,
|
||||||
|
parse_mode="HTML",
|
||||||
|
reply_parameters=reply_params
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("HTML parse failed, falling back to plain text: {}", e)
|
logger.warning("HTML parse failed, falling back to plain text: {}", e)
|
||||||
try:
|
try:
|
||||||
await self._app.bot.send_message(chat_id=chat_id, text=chunk)
|
await self._app.bot.send_message(
|
||||||
|
chat_id=chat_id,
|
||||||
|
text=chunk,
|
||||||
|
reply_parameters=reply_params
|
||||||
|
)
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
logger.error("Error sending Telegram message: {}", e2)
|
logger.error("Error sending Telegram message: {}", e2)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user