1414from tests .fixtures .mocks .telegram import create_mock_telegram_modules
1515
1616_TELEGRAM_PLATFORM_ADAPTER = None
17+ _TELEGRAM_PLATFORM_EVENT = None
18+ _TELEGRAM_MODULES : dict [str , object ] = {}
1719
1820
19- def _load_telegram_adapter ():
20- global _TELEGRAM_PLATFORM_ADAPTER
21- if _TELEGRAM_PLATFORM_ADAPTER is not None :
22- return _TELEGRAM_PLATFORM_ADAPTER
23-
21+ def _build_telegram_patched_modules ():
2422 mocks = create_mock_telegram_modules ()
25- patched_modules = {
23+ return {
2624 "telegram" : mocks ["telegram" ],
2725 "telegram.constants" : mocks ["telegram" ].constants ,
2826 "telegram.error" : mocks ["telegram" ].error ,
@@ -33,12 +31,41 @@ def _load_telegram_adapter():
3331 "apscheduler.schedulers.asyncio" : mocks ["apscheduler" ].schedulers .asyncio ,
3432 "apscheduler.schedulers.background" : mocks ["apscheduler" ].schedulers .background ,
3533 }
36- with patch .dict (sys .modules , patched_modules ):
37- sys .modules .pop ("astrbot.core.platform.sources.telegram.tg_adapter" , None )
38- module = importlib .import_module ("astrbot.core.platform.sources.telegram.tg_adapter" )
39- _TELEGRAM_PLATFORM_ADAPTER = module .TelegramPlatformAdapter
34+
35+
36+ def _load_telegram_module (module_name : str ):
37+ module = _TELEGRAM_MODULES .get (module_name )
38+ if module is not None :
39+ return module
40+
41+ with patch .dict (sys .modules , _build_telegram_patched_modules ()):
42+ sys .modules .pop (module_name , None )
43+ module = importlib .import_module (module_name )
44+
45+ sys .modules [module_name ] = module
46+ _TELEGRAM_MODULES [module_name ] = module
47+ return module
48+
49+
50+ def _load_telegram_adapter ():
51+ global _TELEGRAM_PLATFORM_ADAPTER
52+ if _TELEGRAM_PLATFORM_ADAPTER is not None :
4053 return _TELEGRAM_PLATFORM_ADAPTER
4154
55+ module = _load_telegram_module ("astrbot.core.platform.sources.telegram.tg_adapter" )
56+ _TELEGRAM_PLATFORM_ADAPTER = module .TelegramPlatformAdapter
57+ return _TELEGRAM_PLATFORM_ADAPTER
58+
59+
60+ def _load_telegram_platform_event ():
61+ global _TELEGRAM_PLATFORM_EVENT
62+ if _TELEGRAM_PLATFORM_EVENT is not None :
63+ return _TELEGRAM_PLATFORM_EVENT
64+
65+ module = _load_telegram_module ("astrbot.core.platform.sources.telegram.tg_event" )
66+ _TELEGRAM_PLATFORM_EVENT = module .TelegramPlatformEvent
67+ return _TELEGRAM_PLATFORM_EVENT
68+
4269
4370def _build_context () -> MagicMock :
4471 context = MagicMock ()
@@ -71,8 +98,7 @@ async def test_telegram_document_caption_populates_message_text_and_plain():
7198 assert result .message_str == "@alice 请总结这份文档"
7299 assert any (isinstance (component , Comp .File ) for component in result .message )
73100 assert any (
74- isinstance (component , Comp .Plain )
75- and component .text == "@alice 请总结这份文档"
101+ isinstance (component , Comp .Plain ) and component .text == "@alice 请总结这份文档"
76102 for component in result .message
77103 )
78104 assert any (
@@ -140,3 +166,49 @@ async def test_telegram_voice_message_creates_record_component(tmp_path):
140166 assert result .message [0 ].file == str (wav_path )
141167 assert result .message [0 ].path == str (wav_path )
142168 assert result .message [0 ].url == str (wav_path )
169+
170+
171+ @pytest .mark .asyncio
172+ async def test_telegram_final_segment_splits_long_markdown_messages ():
173+ TelegramPlatformEvent = _load_telegram_platform_event ()
174+ client = MagicMock ()
175+ client .send_message = AsyncMock ()
176+ event = TelegramPlatformEvent ("msg" , MagicMock (), MagicMock (), "session" , client )
177+
178+ delta = "A" * (TelegramPlatformEvent .MAX_MESSAGE_LENGTH + 32 )
179+ payload = {"chat_id" : "123456" }
180+
181+ await event ._send_final_segment (delta , payload )
182+
183+ assert client .send_message .await_count == 2
184+ first_call = client .send_message .await_args_list [0 ].kwargs
185+ second_call = client .send_message .await_args_list [1 ].kwargs
186+ assert len (first_call ["text" ]) == TelegramPlatformEvent .MAX_MESSAGE_LENGTH
187+ assert len (second_call ["text" ]) == 32
188+ assert first_call ["parse_mode" ] == "MarkdownV2"
189+ assert second_call ["parse_mode" ] == "MarkdownV2"
190+
191+
192+ @pytest .mark .asyncio
193+ async def test_telegram_final_segment_splits_long_plaintext_when_markdown_fails ():
194+ TelegramPlatformEvent = _load_telegram_platform_event ()
195+ client = MagicMock ()
196+ client .send_message = AsyncMock ()
197+ event = TelegramPlatformEvent ("msg" , MagicMock (), MagicMock (), "session" , client )
198+
199+ delta = "B" * (TelegramPlatformEvent .MAX_MESSAGE_LENGTH + 18 )
200+ payload = {"chat_id" : "123456" }
201+
202+ with patch (
203+ "astrbot.core.platform.sources.telegram.tg_event.telegramify_markdown.markdownify" ,
204+ side_effect = Exception ("boom" ),
205+ ):
206+ await event ._send_final_segment (delta , payload )
207+
208+ assert client .send_message .await_count == 2
209+ first_call = client .send_message .await_args_list [0 ].kwargs
210+ second_call = client .send_message .await_args_list [1 ].kwargs
211+ assert len (first_call ["text" ]) == TelegramPlatformEvent .MAX_MESSAGE_LENGTH
212+ assert len (second_call ["text" ]) == 18
213+ assert "parse_mode" not in first_call
214+ assert "parse_mode" not in second_call
0 commit comments