Skip to content

Commit a2b61e2

Browse files
authored
refactor: extract Voice_messages_forbidden fallback into shared helper with typed BadRequest exception (#5204)
- Add _send_voice_with_fallback helper to deduplicate voice forbidden handling - Catch telegram.error.BadRequest instead of bare Exception with string matching - Add text field to Record component to preserve TTS source text - Store original text in Record during TTS conversion for use as document caption - Skip _send_chat_action when chat_id is empty to avoid unnecessary warnings
1 parent c6289d8 commit a2b61e2

File tree

3 files changed

+75
-6
lines changed

3 files changed

+75
-6
lines changed

astrbot/core/message/components.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ class Record(BaseMessageComponent):
119119
cache: bool | None = True
120120
proxy: bool | None = True
121121
timeout: int | None = 0
122+
# Original text content (e.g. TTS source text), used as caption in fallback scenarios
123+
text: str | None = None
122124
# 额外
123125
path: str | None
124126

astrbot/core/pipeline/result_decorate/stage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ async def process(
315315
Record(
316316
file=url or audio_path,
317317
url=url or audio_path,
318+
text=comp.text,
318319
),
319320
)
320321
if dual_output:

astrbot/core/platform/sources/telegram/tg_event.py

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import telegramify_markdown
77
from telegram import ReactionTypeCustomEmoji, ReactionTypeEmoji
88
from telegram.constants import ChatAction
9+
from telegram.error import BadRequest
910
from telegram.ext import ExtBot
1011

1112
from astrbot import logger
@@ -119,6 +120,65 @@ async def _send_media_with_action(
119120
client, user_name, ChatAction.TYPING, message_thread_id
120121
)
121122

123+
@classmethod
124+
async def _send_voice_with_fallback(
125+
cls,
126+
client: ExtBot,
127+
path: str,
128+
payload: dict[str, Any],
129+
*,
130+
caption: str | None = None,
131+
user_name: str = "",
132+
message_thread_id: str | None = None,
133+
use_media_action: bool = False,
134+
) -> None:
135+
"""Send a voice message, falling back to a document if the user's
136+
privacy settings forbid voice messages (``BadRequest`` with
137+
``Voice_messages_forbidden``).
138+
139+
When *use_media_action* is ``True`` the helper wraps the send calls
140+
with ``_send_media_with_action`` (used by the streaming path).
141+
"""
142+
try:
143+
if use_media_action:
144+
await cls._send_media_with_action(
145+
client,
146+
ChatAction.UPLOAD_VOICE,
147+
client.send_voice,
148+
user_name=user_name,
149+
message_thread_id=message_thread_id,
150+
voice=path,
151+
**cast(Any, payload),
152+
)
153+
else:
154+
await client.send_voice(voice=path, **cast(Any, payload))
155+
except BadRequest as e:
156+
# python-telegram-bot raises BadRequest for Voice_messages_forbidden;
157+
# distinguish the voice-privacy case via the API error message.
158+
if "Voice_messages_forbidden" not in e.message:
159+
raise
160+
logger.warning(
161+
"User privacy settings prevent receiving voice messages, falling back to sending an audio file. "
162+
"To enable voice messages, go to Telegram Settings → Privacy and Security → Voice Messages → set to 'Everyone'."
163+
)
164+
if use_media_action:
165+
await cls._send_media_with_action(
166+
client,
167+
ChatAction.UPLOAD_DOCUMENT,
168+
client.send_document,
169+
user_name=user_name,
170+
message_thread_id=message_thread_id,
171+
document=path,
172+
caption=caption,
173+
**cast(Any, payload),
174+
)
175+
else:
176+
await client.send_document(
177+
document=path,
178+
caption=caption,
179+
**cast(Any, payload),
180+
)
181+
122182
async def _ensure_typing(
123183
self,
124184
user_name: str,
@@ -211,7 +271,13 @@ async def send_with_client(
211271
)
212272
elif isinstance(i, Record):
213273
path = await i.convert_to_file_path()
214-
await client.send_voice(voice=path, **cast(Any, payload))
274+
await cls._send_voice_with_fallback(
275+
client,
276+
path,
277+
payload,
278+
caption=i.text or None,
279+
use_media_action=False,
280+
)
215281

216282
async def send(self, message: MessageChain) -> None:
217283
if self.get_message_type() == MessageType.GROUP_MESSAGE:
@@ -330,14 +396,14 @@ async def send_streaming(self, generator, use_fallback: bool = False):
330396
continue
331397
elif isinstance(i, Record):
332398
path = await i.convert_to_file_path()
333-
await self._send_media_with_action(
399+
await self._send_voice_with_fallback(
334400
self.client,
335-
ChatAction.UPLOAD_VOICE,
336-
self.client.send_voice,
401+
path,
402+
payload,
403+
caption=i.text or delta or None,
337404
user_name=user_name,
338405
message_thread_id=message_thread_id,
339-
voice=path,
340-
**cast(Any, payload),
406+
use_media_action=True,
341407
)
342408
continue
343409
else:

0 commit comments

Comments
 (0)