Добавил retry для скачивания аудиосообщений
This commit is contained in:
22
main.py
22
main.py
@@ -5,8 +5,14 @@ from telegram.ext import (
|
|||||||
MessageHandler,
|
MessageHandler,
|
||||||
filters,
|
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.database import init_db
|
||||||
from src.handlers import (
|
from src.handlers import (
|
||||||
admin_command,
|
admin_command,
|
||||||
@@ -41,7 +47,19 @@ def main() -> None:
|
|||||||
|
|
||||||
init_db()
|
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))
|
app.add_handler(CommandHandler("start", start_command))
|
||||||
|
|||||||
35
src/audio.py
35
src/audio.py
@@ -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.database import upsert_recording
|
||||||
from src.logger import logger
|
from src.logger import logger
|
||||||
from src.scenarios import get_audio_filename
|
from src.scenarios import get_audio_filename
|
||||||
@@ -15,22 +18,38 @@ async def save_voice_message(
|
|||||||
replica_index: int,
|
replica_index: int,
|
||||||
duration: int,
|
duration: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Сохраняет голосовое сообщение в data_partial/."""
|
"""Сохраняет голосовое сообщение в data_partial/ с retry логикой."""
|
||||||
# Создаём директорию если нужно
|
|
||||||
scenario_dir = DATA_PARTIAL_DIR / scenario_id
|
scenario_dir = DATA_PARTIAL_DIR / scenario_id
|
||||||
scenario_dir.mkdir(parents=True, exist_ok=True)
|
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)
|
filename = get_audio_filename(replica_index, speaker_id, user_id)
|
||||||
filepath = scenario_dir / filename
|
filepath = scenario_dir / filename
|
||||||
|
|
||||||
|
for attempt in range(MAX_RETRY_ATTEMPTS):
|
||||||
|
try:
|
||||||
|
file = await bot.get_file(file_id)
|
||||||
await file.download_to_drive(filepath)
|
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)")
|
logger.debug(f"Saved voice: {filepath} ({duration}s)")
|
||||||
|
return
|
||||||
|
|
||||||
|
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:
|
def format_duration(seconds: float) -> str:
|
||||||
|
|||||||
@@ -24,3 +24,12 @@ DATA_PARTIAL_DIR: Path = BASE_DIR / "data_partial"
|
|||||||
SCENARIOS_DIR: Path = BASE_DIR / "scenarios"
|
SCENARIOS_DIR: Path = BASE_DIR / "scenarios"
|
||||||
DB_DIR: Path = BASE_DIR / "db"
|
DB_DIR: Path = BASE_DIR / "db"
|
||||||
DB_PATH: Path = DB_DIR / "bot.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)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from telegram import (
|
|||||||
ReplyKeyboardRemove,
|
ReplyKeyboardRemove,
|
||||||
Update,
|
Update,
|
||||||
)
|
)
|
||||||
|
from telegram.error import TelegramError
|
||||||
from telegram.ext import ContextTypes
|
from telegram.ext import ContextTypes
|
||||||
|
|
||||||
from src.config import ADMIN_LOGIN
|
from src.config import ADMIN_LOGIN
|
||||||
@@ -47,6 +48,7 @@ from src.messages import (
|
|||||||
SPECIFY_GENDER_TEXT,
|
SPECIFY_GENDER_TEXT,
|
||||||
TRACK_SAVED_TEXT,
|
TRACK_SAVED_TEXT,
|
||||||
VOICE_EXPECTED_TEXT,
|
VOICE_EXPECTED_TEXT,
|
||||||
|
VOICE_SAVE_ERROR_TEXT,
|
||||||
)
|
)
|
||||||
from src.scenarios import find_available_track
|
from src.scenarios import find_available_track
|
||||||
|
|
||||||
@@ -869,6 +871,7 @@ async def handle_voice_message(
|
|||||||
real_replica_index = replicas[session.replica_index].replica_index
|
real_replica_index = replicas[session.replica_index].replica_index
|
||||||
|
|
||||||
voice = update.message.voice
|
voice = update.message.voice
|
||||||
|
try:
|
||||||
await save_voice_message(
|
await save_voice_message(
|
||||||
context.bot,
|
context.bot,
|
||||||
voice.file_id,
|
voice.file_id,
|
||||||
@@ -878,6 +881,10 @@ async def handle_voice_message(
|
|||||||
real_replica_index,
|
real_replica_index,
|
||||||
max(1, voice.duration), # телеграм возвращает с точностью до секунд
|
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)
|
track_length = get_track_length(session.scenario_id, session.speaker_id)
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ REPEAT_REPLICA_TEXT = "🔄 Перезапись реплики {num}/{total}:"
|
|||||||
|
|
||||||
VOICE_EXPECTED_TEXT = "❌ Пожалуйста, отправьте голосовое сообщение с озвучкой реплики."
|
VOICE_EXPECTED_TEXT = "❌ Пожалуйста, отправьте голосовое сообщение с озвучкой реплики."
|
||||||
|
|
||||||
|
VOICE_SAVE_ERROR_TEXT = """❌ Произошла ошибка при сохранении аудиозаписи.
|
||||||
|
|
||||||
|
Возможные причины:
|
||||||
|
• Временные проблемы с сетью
|
||||||
|
• Проблемы с серверами Telegram
|
||||||
|
|
||||||
|
Пожалуйста, попробуйте отправить голосовое сообщение ещё раз."""
|
||||||
|
|
||||||
TRACK_SAVED_TEXT = """✅ Дорожка сохранена!
|
TRACK_SAVED_TEXT = """✅ Дорожка сохранена!
|
||||||
|
|
||||||
🙏 Спасибо большое за вашу помощь! Если у вас есть желание и возможность, вы можете озвучить ещё несколько дорожек!
|
🙏 Спасибо большое за вашу помощь! Если у вас есть желание и возможность, вы можете озвучить ещё несколько дорожек!
|
||||||
|
|||||||
Reference in New Issue
Block a user