1. 整理多个文件的导入顺序,统一模块导入规范 2. 为多账号管理器添加线程安全的账号守卫和生命周期钩子 3. 新增账号增删、启停和配置变更的完整操作方法 4. 重构轮询偏移存储,使用适配器状态替代本地缓存 5. 优化BotCommandScope的参数构建逻辑
128 lines
3.1 KiB
Python
128 lines
3.1 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
from typing import Any
|
|
|
|
from telegram import Bot
|
|
from telegram.error import TelegramError
|
|
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
_VOICE_COMPATIBLE_FORMATS = frozenset({"ogg", "opus", "flac", "wav", "m4a", "mp3", "aac"})
|
|
|
|
|
|
def is_voice_compatible_audio(filename: str) -> bool:
|
|
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
|
|
return ext in _VOICE_COMPATIBLE_FORMATS
|
|
|
|
|
|
async def send_voice_from_bytes(
|
|
bot: Bot,
|
|
chat_id: str,
|
|
audio_bytes: bytes,
|
|
filename: str = "voice.ogg",
|
|
duration: int | None = None,
|
|
caption: str | None = None,
|
|
**kwargs,
|
|
) -> Any:
|
|
try:
|
|
buf = io.BytesIO(audio_bytes)
|
|
buf.name = filename
|
|
return await bot.send_voice(
|
|
chat_id=chat_id,
|
|
voice=buf,
|
|
duration=duration,
|
|
caption=caption,
|
|
**kwargs,
|
|
)
|
|
except TelegramError as e:
|
|
logger.error(f"[Telegram] Failed to send voice: {e}")
|
|
raise
|
|
|
|
|
|
async def send_audio_from_bytes(
|
|
bot: Bot,
|
|
chat_id: str,
|
|
audio_bytes: bytes,
|
|
filename: str = "audio.mp3",
|
|
title: str | None = None,
|
|
performer: str | None = None,
|
|
duration: int | None = None,
|
|
caption: str | None = None,
|
|
**kwargs,
|
|
) -> Any:
|
|
try:
|
|
buf = io.BytesIO(audio_bytes)
|
|
buf.name = filename
|
|
return await bot.send_audio(
|
|
chat_id=chat_id,
|
|
audio=buf,
|
|
title=title,
|
|
performer=performer,
|
|
duration=duration,
|
|
caption=caption,
|
|
**kwargs,
|
|
)
|
|
except TelegramError as e:
|
|
logger.error(f"[Telegram] Failed to send audio: {e}")
|
|
raise
|
|
|
|
|
|
async def send_voice_or_audio(
|
|
bot: Bot,
|
|
chat_id: str,
|
|
audio_bytes: bytes,
|
|
filename: str = "audio.ogg",
|
|
as_voice: bool = False,
|
|
duration: int | None = None,
|
|
caption: str | None = None,
|
|
title: str | None = None,
|
|
performer: str | None = None,
|
|
**kwargs,
|
|
) -> Any:
|
|
if as_voice or is_voice_compatible_audio(filename):
|
|
return await send_voice_from_bytes(
|
|
bot,
|
|
chat_id,
|
|
audio_bytes,
|
|
filename=filename or "voice.ogg",
|
|
duration=duration,
|
|
caption=caption,
|
|
**kwargs,
|
|
)
|
|
return await send_audio_from_bytes(
|
|
bot,
|
|
chat_id,
|
|
audio_bytes,
|
|
filename=filename or "audio.mp3",
|
|
title=title,
|
|
performer=performer,
|
|
duration=duration,
|
|
caption=caption,
|
|
**kwargs,
|
|
)
|
|
|
|
|
|
async def send_video_note_from_bytes(
|
|
bot: Bot,
|
|
chat_id: str,
|
|
video_bytes: bytes,
|
|
filename: str = "video_note.mp4",
|
|
duration: int | None = None,
|
|
length: int | None = None,
|
|
**kwargs,
|
|
) -> Any:
|
|
try:
|
|
buf = io.BytesIO(video_bytes)
|
|
buf.name = filename
|
|
return await bot.send_video_note(
|
|
chat_id=chat_id,
|
|
video_note=buf,
|
|
duration=duration,
|
|
length=length,
|
|
**kwargs,
|
|
)
|
|
except TelegramError as e:
|
|
logger.error(f"[Telegram] Failed to send video note: {e}")
|
|
raise
|