Добавил retry для скачивания аудиосообщений

This commit is contained in:
2026-02-09 17:10:48 +03:00
parent b41e735b22
commit f11e6ca826
5 changed files with 82 additions and 21 deletions

22
main.py
View File

@@ -5,8 +5,14 @@ from telegram.ext import (
MessageHandler,
filters,
)
from telegram.request import HTTPXRequest
from src.config import BOT_TOKEN
from src.config import (
BOT_TOKEN,
TELEGRAM_CONNECT_TIMEOUT,
TELEGRAM_READ_TIMEOUT,
TELEGRAM_WRITE_TIMEOUT,
)
from src.database import init_db
from src.handlers import (
admin_command,
@@ -41,7 +47,19 @@ def main() -> None:
init_db()
app = ApplicationBuilder().token(BOT_TOKEN).build()
app = (
ApplicationBuilder()
.token(BOT_TOKEN)
.request(
HTTPXRequest(
connection_pool_size=8,
connect_timeout=TELEGRAM_CONNECT_TIMEOUT,
read_timeout=TELEGRAM_READ_TIMEOUT,
write_timeout=TELEGRAM_WRITE_TIMEOUT,
)
)
.build()
)
# Команды
app.add_handler(CommandHandler("start", start_command))

View File

@@ -1,6 +1,9 @@
from telegram import Bot
import asyncio
from src.config import DATA_PARTIAL_DIR
from telegram import Bot
from telegram.error import NetworkError, TimedOut
from src.config import DATA_PARTIAL_DIR, MAX_RETRY_ATTEMPTS, RETRY_DELAYS
from src.database import upsert_recording
from src.logger import logger
from src.scenarios import get_audio_filename
@@ -15,22 +18,38 @@ async def save_voice_message(
replica_index: int,
duration: int,
) -> None:
"""Сохраняет голосовое сообщение в data_partial/."""
# Создаём директорию если нужно
"""Сохраняет голосовое сообщение в data_partial/ с retry логикой."""
scenario_dir = DATA_PARTIAL_DIR / scenario_id
scenario_dir.mkdir(parents=True, exist_ok=True)
# Скачиваем файл
file = await bot.get_file(file_id)
filename = get_audio_filename(replica_index, speaker_id, user_id)
filepath = scenario_dir / filename
await file.download_to_drive(filepath)
for attempt in range(MAX_RETRY_ATTEMPTS):
try:
file = await bot.get_file(file_id)
await file.download_to_drive(filepath)
# Записываем в БД (duration из метаданных Telegram)
upsert_recording(user_id, scenario_id, replica_index, float(duration))
# Успех - записываем в БД
upsert_recording(user_id, scenario_id, replica_index, float(duration))
logger.debug(f"Saved voice: {filepath} ({duration}s)")
return
logger.debug(f"Saved voice: {filepath} ({duration}s)")
except (TimedOut, NetworkError) as e:
if attempt < MAX_RETRY_ATTEMPTS - 1:
delay = RETRY_DELAYS[attempt]
logger.warning(
f"Network error on attempt {attempt + 1}/"
f"{MAX_RETRY_ATTEMPTS}: {e}. "
f"Retrying in {delay}s..."
)
await asyncio.sleep(delay)
else:
logger.error(
f"Failed to save voice after {MAX_RETRY_ATTEMPTS} "
f"attempts: {e}"
)
raise
def format_duration(seconds: float) -> str:

View File

@@ -24,3 +24,12 @@ DATA_PARTIAL_DIR: Path = BASE_DIR / "data_partial"
SCENARIOS_DIR: Path = BASE_DIR / "scenarios"
DB_DIR: Path = BASE_DIR / "db"
DB_PATH: Path = DB_DIR / "bot.db"
# Таймауты для Telegram API (в секундах)
TELEGRAM_CONNECT_TIMEOUT: int = 10
TELEGRAM_READ_TIMEOUT: int = 30
TELEGRAM_WRITE_TIMEOUT: int = 30
# Настройки retry
MAX_RETRY_ATTEMPTS: int = 3
RETRY_DELAYS: tuple[float, ...] = (1.0, 2.0, 4.0)

View File

@@ -6,6 +6,7 @@ from telegram import (
ReplyKeyboardRemove,
Update,
)
from telegram.error import TelegramError
from telegram.ext import ContextTypes
from src.config import ADMIN_LOGIN
@@ -47,6 +48,7 @@ from src.messages import (
SPECIFY_GENDER_TEXT,
TRACK_SAVED_TEXT,
VOICE_EXPECTED_TEXT,
VOICE_SAVE_ERROR_TEXT,
)
from src.scenarios import find_available_track
@@ -869,15 +871,20 @@ async def handle_voice_message(
real_replica_index = replicas[session.replica_index].replica_index
voice = update.message.voice
await save_voice_message(
context.bot,
voice.file_id,
user.id,
session.scenario_id,
session.speaker_id,
real_replica_index,
max(1, voice.duration), # телеграм возвращает с точностью до секунд
)
try:
await save_voice_message(
context.bot,
voice.file_id,
user.id,
session.scenario_id,
session.speaker_id,
real_replica_index,
max(1, voice.duration), # телеграм возвращает с точностью до секунд
)
except TelegramError as e:
logger.error(f"Failed to save voice for user {user.id}: {e}")
await update.message.reply_text(VOICE_SAVE_ERROR_TEXT)
return
track_length = get_track_length(session.scenario_id, session.speaker_id)

View File

@@ -69,6 +69,14 @@ REPEAT_REPLICA_TEXT = "🔄 Перезапись реплики {num}/{total}:"
VOICE_EXPECTED_TEXT = "❌ Пожалуйста, отправьте голосовое сообщение с озвучкой реплики."
VOICE_SAVE_ERROR_TEXT = """❌ Произошла ошибка при сохранении аудиозаписи.
Возможные причины:
• Временные проблемы с сетью
• Проблемы с серверами Telegram
Пожалуйста, попробуйте отправить голосовое сообщение ещё раз."""
TRACK_SAVED_TEXT = """✅ Дорожка сохранена!
🙏 Спасибо большое за вашу помощь! Если у вас есть желание и возможность, вы можете озвучить ещё несколько дорожек!