55import asyncio
66import contextlib
77import json
8- from collections .abc import Awaitable , Callable
8+ from collections .abc import AsyncGenerator , Awaitable , Callable
99from dataclasses import dataclass
1010from typing import Any , ClassVar
1111
@@ -124,9 +124,11 @@ def __init__(self, runtime: AppRuntime) -> None:
124124 proxy = settings .telegram_proxy ,
125125 )
126126 self ._app : Application | None = None
127- self ._typing_tasks : dict [str , asyncio .Task [None ]] = {}
128127 self ._on_receive : Callable [[Message ], Awaitable [None ]] | None = None
129128
129+ def is_mentioned (self , message : Message ) -> bool :
130+ return bool (MESSAGE_FILTER .filter (message ))
131+
130132 async def start (self , on_receive : Callable [[Message ], Awaitable [None ]]) -> None :
131133 self ._on_receive = on_receive
132134 proxy , _ = resolve_proxy (self ._config .proxy )
@@ -153,11 +155,6 @@ async def start(self, on_receive: Callable[[Message], Awaitable[None]]) -> None:
153155 try :
154156 await asyncio .Event ().wait () # Keep running until stopped
155157 finally :
156- for task in self ._typing_tasks .values ():
157- task .cancel ()
158- with contextlib .suppress (asyncio .CancelledError ):
159- await asyncio .gather (* self ._typing_tasks .values ())
160- self ._typing_tasks .clear ()
161158 updater = self ._app .updater
162159 with contextlib .suppress (Exception ):
163160 if updater is not None and updater .running :
@@ -167,9 +164,7 @@ async def start(self, on_receive: Callable[[Message], Awaitable[None]]) -> None:
167164 self ._app = None
168165 logger .info ("telegram.stopped" )
169166
170- async def get_session_prompt (self , message : Message ) -> tuple [str , str ] | None :
171- if MESSAGE_FILTER .filter (message ) is False :
172- return None
167+ async def get_session_prompt (self , message : Message ) -> tuple [str , str ]:
173168 chat_id = str (message .chat_id )
174169 session_id = f"{ self .name } :{ chat_id } "
175170 content , media = self ._parse_message (message )
@@ -208,9 +203,8 @@ async def get_session_prompt(self, message: Message) -> tuple[str, str] | None:
208203 if reply_meta :
209204 metadata ["reply_to_message" ] = reply_meta
210205
211- metadata_json = json .dumps ({"channel" : f"${ self .name } " , "chat_id" : chat_id , ** metadata }, ensure_ascii = False )
212- prompt = f"{ content } \n ———————\n { metadata_json } "
213- return session_id , prompt
206+ metadata_json = json .dumps ({"message" : content , "chat_id" : chat_id , ** metadata }, ensure_ascii = False )
207+ return session_id , metadata_json
214208
215209 async def process_output (self , session_id : str , output : LoopResult ) -> None :
216210 parts = [part for part in (output .immediate_output , output .assistant_output ) if part ]
@@ -259,30 +253,24 @@ async def _on_text(self, update: Update, _context: ContextTypes.DEFAULT_TYPE) ->
259253 if self ._on_receive is None :
260254 logger .warning ("telegram.inbound no handler for received messages" )
261255 return
262- await self ._start_typing (chat_id )
263- try :
256+ async with self ._start_typing (chat_id ):
264257 await self ._on_receive (update .message )
265- finally :
266- await self ._stop_typing (chat_id )
267258
268- async def _start_typing (self , chat_id : str ) -> None :
269- await self ._stop_typing (chat_id )
270- self ._typing_tasks [chat_id ] = asyncio .create_task (self ._typing_loop (chat_id ))
271-
272- async def _stop_typing (self , chat_id : str ) -> None :
273- task = self ._typing_tasks .pop (chat_id , None )
274- if task is not None :
275- task .cancel ()
259+ @contextlib .asynccontextmanager
260+ async def _start_typing (self , chat_id : str ) -> AsyncGenerator [None , None ]:
261+ typing_task = asyncio .create_task (self ._typing_loop (chat_id ))
262+ try :
263+ yield
264+ finally :
265+ typing_task .cancel ()
276266 with contextlib .suppress (asyncio .CancelledError ):
277- await task
267+ await typing_task
278268
279269 async def _typing_loop (self , chat_id : str ) -> None :
280270 try :
281271 while self ._app is not None :
282272 await self ._app .bot .send_chat_action (chat_id = int (chat_id ), action = "typing" )
283273 await asyncio .sleep (4 )
284- except asyncio .CancelledError :
285- return
286274 except Exception :
287275 logger .exception ("telegram.typing_loop.error chat_id={}" , chat_id )
288276 return
0 commit comments