Enhance bot functionality and refactor database interactions
- Added `ca-certificates` installation to Dockerfile for improved network security. - Updated health check command in Dockerfile to include better timeout handling. - Refactored `run_helper.py` to implement proper signal handling and logging during shutdown. - Transitioned database operations to an asynchronous model in `async_db.py`, improving performance and responsiveness. - Updated database schema to support new foreign key relationships and optimized indexing for better query performance. - Enhanced various bot handlers to utilize async database methods, improving overall efficiency and user experience. - Removed obsolete database and fix scripts to streamline the project structure.
This commit is contained in:
@@ -22,7 +22,6 @@ class VoiceMessage:
|
||||
self.date_added = date_added
|
||||
self.file_id = file_id
|
||||
|
||||
|
||||
class VoiceBotService:
|
||||
"""Сервис для работы с голосовыми сообщениями"""
|
||||
|
||||
@@ -141,10 +140,10 @@ class VoiceBotService:
|
||||
logger.error(f"Ошибка при отправке приветственных сообщений: {e}")
|
||||
raise VoiceMessageError(f"Не удалось отправить приветственные сообщения: {e}")
|
||||
|
||||
def get_random_audio(self, user_id: int) -> Optional[Tuple[str, str, str]]:
|
||||
async def get_random_audio(self, user_id: int) -> Optional[Tuple[str, str, str]]:
|
||||
"""Получить случайное аудио для прослушивания"""
|
||||
try:
|
||||
check_audio = self.bot_db.check_listen_audio(user_id=user_id)
|
||||
check_audio = await self.bot_db.check_listen_audio(user_id=user_id)
|
||||
list_audio = list(check_audio)
|
||||
|
||||
if not list_audio:
|
||||
@@ -155,9 +154,9 @@ class VoiceBotService:
|
||||
audio_for_user = check_audio[number_element]
|
||||
|
||||
# Получаем информацию об авторе
|
||||
user_id_author = self.bot_db.get_user_id_by_file_name(audio_for_user)
|
||||
date_added = self.bot_db.get_date_by_file_name(audio_for_user)
|
||||
user_emoji = self.bot_db.check_emoji_for_user(user_id_author)
|
||||
user_id_author = await self.bot_db.get_user_id_by_file_name(audio_for_user)
|
||||
date_added = await self.bot_db.get_date_by_file_name(audio_for_user)
|
||||
user_emoji = await self.bot_db.get_user_emoji(user_id_author)
|
||||
|
||||
return audio_for_user, date_added, user_emoji
|
||||
|
||||
@@ -165,26 +164,26 @@ class VoiceBotService:
|
||||
logger.error(f"Ошибка при получении случайного аудио: {e}")
|
||||
raise AudioProcessingError(f"Не удалось получить случайное аудио: {e}")
|
||||
|
||||
def mark_audio_as_listened(self, file_name: str, user_id: int) -> None:
|
||||
async def mark_audio_as_listened(self, file_name: str, user_id: int) -> None:
|
||||
"""Пометить аудио как прослушанное"""
|
||||
try:
|
||||
self.bot_db.mark_listened_audio(file_name, user_id=user_id)
|
||||
await self.bot_db.mark_listened_audio(file_name, user_id=user_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при пометке аудио как прослушанного: {e}")
|
||||
raise DatabaseError(f"Не удалось пометить аудио как прослушанное: {e}")
|
||||
|
||||
def clear_user_listenings(self, user_id: int) -> None:
|
||||
async def clear_user_listenings(self, user_id: int) -> None:
|
||||
"""Очистить прослушивания пользователя"""
|
||||
try:
|
||||
self.bot_db.delete_listen_count_for_user(user_id)
|
||||
await self.bot_db.delete_listen_count_for_user(user_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при очистке прослушиваний: {e}")
|
||||
raise DatabaseError(f"Не удалось очистить прослушивания: {e}")
|
||||
|
||||
def get_remaining_audio_count(self, user_id: int) -> int:
|
||||
async def get_remaining_audio_count(self, user_id: int) -> int:
|
||||
"""Получить количество оставшихся непрослушанных аудио"""
|
||||
try:
|
||||
check_audio = self.bot_db.check_listen_audio(user_id=user_id)
|
||||
check_audio = await self.bot_db.check_listen_audio(user_id=user_id)
|
||||
return len(list(check_audio))
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при получении количества аудио: {e}")
|
||||
@@ -215,23 +214,29 @@ class AudioFileService:
|
||||
def __init__(self, bot_db):
|
||||
self.bot_db = bot_db
|
||||
|
||||
def generate_file_name(self, user_id: int) -> str:
|
||||
async def generate_file_name(self, user_id: int) -> str:
|
||||
"""Сгенерировать имя файла для аудио"""
|
||||
try:
|
||||
# Проверяем есть ли запись о файле в базе данных
|
||||
is_having_audio_from_user = self.bot_db.get_last_user_audio_record(user_id=user_id)
|
||||
user_audio_count = await self.bot_db.get_user_audio_records_count(user_id=user_id)
|
||||
|
||||
if is_having_audio_from_user is False:
|
||||
if user_audio_count == 0:
|
||||
# Если нет, то генерируем имя файла
|
||||
file_name = f'message_from_{user_id}_number_1'
|
||||
else:
|
||||
# Иначе берем последнюю запись из БД, добавляем к ней 1
|
||||
file_name = self.bot_db.get_path_for_audio_record(user_id=user_id)
|
||||
file_id = self.bot_db.get_id_for_audio_record(user_id) + 1
|
||||
path = Path(f'voice_users/{file_name}.ogg')
|
||||
file_name = await self.bot_db.get_path_for_audio_record(user_id=user_id)
|
||||
if file_name:
|
||||
# Извлекаем номер из имени файла и увеличиваем на 1
|
||||
try:
|
||||
current_number = int(file_name.split('_')[-1])
|
||||
new_number = current_number + 1
|
||||
except (ValueError, IndexError):
|
||||
new_number = user_audio_count + 1
|
||||
else:
|
||||
new_number = user_audio_count + 1
|
||||
|
||||
if path.exists():
|
||||
file_name = f'message_from_{user_id}_number_{file_id}'
|
||||
file_name = f'message_from_{user_id}_number_{new_number}'
|
||||
|
||||
return file_name
|
||||
|
||||
@@ -239,23 +244,31 @@ class AudioFileService:
|
||||
logger.error(f"Ошибка при генерации имени файла: {e}")
|
||||
raise FileOperationError(f"Не удалось сгенерировать имя файла: {e}")
|
||||
|
||||
def save_audio_file(self, file_name: str, user_id: int, date_added: datetime) -> None:
|
||||
async def save_audio_file(self, file_name: str, user_id: int, date_added: datetime, file_id: str) -> None:
|
||||
"""Сохранить информацию об аудио файле в базу данных"""
|
||||
try:
|
||||
self.bot_db.add_audio_record(file_name, user_id, date_added)
|
||||
await self.bot_db.add_audio_record_simple(file_name, user_id, date_added)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при сохранении аудио файла в БД: {e}")
|
||||
raise DatabaseError(f"Не удалось сохранить аудио файл в БД: {e}")
|
||||
|
||||
async def download_and_save_audio(self, bot, message_id: int, file_name: str) -> None:
|
||||
async def download_and_save_audio(self, bot, message, file_name: str) -> None:
|
||||
"""Скачать и сохранить аудио файл"""
|
||||
try:
|
||||
# Получаем информацию о файле
|
||||
file_info = await bot.get_file(file_id=bot.get_message(message_id).voice.file_id)
|
||||
# Проверяем наличие голосового сообщения
|
||||
if not message or not message.voice:
|
||||
raise FileOperationError("Сообщение или голосовое сообщение не найдено")
|
||||
|
||||
file_id = message.voice.file_id
|
||||
file_info = await bot.get_file(file_id=file_id)
|
||||
downloaded_file = await bot.download_file(file_path=file_info.file_path)
|
||||
|
||||
# Создаем директорию если она не существует
|
||||
import os
|
||||
os.makedirs(VOICE_USERS_DIR, exist_ok=True)
|
||||
|
||||
# Сохраняем файл
|
||||
with open(f'voice_users/{file_name}.ogg', 'wb') as new_file:
|
||||
with open(f'{VOICE_USERS_DIR}/{file_name}.ogg', 'wb') as new_file:
|
||||
new_file.write(downloaded_file.read())
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -70,26 +70,30 @@ def plural_time(type: int, n: float) -> str:
|
||||
return str(new_number) + ' ' + word[p]
|
||||
|
||||
|
||||
def get_last_message_text(bot_db) -> Optional[str]:
|
||||
async def get_last_message_text(bot_db) -> Optional[str]:
|
||||
"""Получить текст сообщения о времени последней записи"""
|
||||
try:
|
||||
date_from_db = bot_db.last_date_audio()
|
||||
return format_time_ago(date_from_db)
|
||||
date_from_db = await bot_db.last_date_audio()
|
||||
if date_from_db is None:
|
||||
return None
|
||||
# Преобразуем UNIX timestamp в строку для format_time_ago
|
||||
date_string = datetime.fromtimestamp(date_from_db).strftime("%Y-%m-%d %H:%M:%S")
|
||||
return format_time_ago(date_string)
|
||||
except Exception as e:
|
||||
logger.error(f"Не удалось получить дату последнего сообщения - {e}")
|
||||
return None
|
||||
|
||||
|
||||
def validate_voice_message(message) -> bool:
|
||||
async def validate_voice_message(message) -> bool:
|
||||
"""Проверить валидность голосового сообщения"""
|
||||
return message.content_type == 'voice'
|
||||
|
||||
|
||||
def get_user_emoji_safe(bot_db, user_id: int) -> str:
|
||||
async def get_user_emoji_safe(bot_db, user_id: int) -> str:
|
||||
"""Безопасно получить эмодзи пользователя"""
|
||||
try:
|
||||
user_emoji = bot_db.check_emoji_for_user(user_id)
|
||||
return user_emoji if user_emoji else "😊"
|
||||
user_emoji = await bot_db.get_user_emoji(user_id)
|
||||
return user_emoji if user_emoji and user_emoji != "Смайл еще не определен" else "😊"
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при получении эмодзи пользователя {user_id}: {e}")
|
||||
return "😊"
|
||||
|
||||
@@ -98,7 +98,7 @@ class VoiceHandlers:
|
||||
"""Обработчик кнопки 'Голосовой бот' из основной клавиатуры"""
|
||||
try:
|
||||
# Проверяем, получал ли пользователь приветственное сообщение
|
||||
welcome_received = bot_db.check_voice_bot_welcome_received(message.from_user.id)
|
||||
welcome_received = await bot_db.check_voice_bot_welcome_received(message.from_user.id)
|
||||
logger.info(f"Пользователь {message.from_user.id}: welcome_received = {welcome_received}")
|
||||
|
||||
if welcome_received:
|
||||
@@ -124,7 +124,7 @@ class VoiceHandlers:
|
||||
logger.info(f"Пользователь {message.from_user.id}: вызывается функция restart_function")
|
||||
await message.forward(chat_id=settings['Telegram']['group_for_logs'])
|
||||
await update_user_info(VOICE_BOT_NAME, message)
|
||||
check_user_emoji(message)
|
||||
await check_user_emoji(message)
|
||||
markup = get_main_keyboard()
|
||||
await message.answer(text='🎤 Записывайся или слушай!', reply_markup=markup)
|
||||
await state.set_state(STATE_START)
|
||||
@@ -136,7 +136,7 @@ class VoiceHandlers:
|
||||
settings: MagicData("settings")
|
||||
):
|
||||
await message.forward(chat_id=settings['Telegram']['group_for_logs'])
|
||||
user_emoji = check_user_emoji(message)
|
||||
user_emoji = await check_user_emoji(message)
|
||||
await state.set_state(STATE_START)
|
||||
if user_emoji is not None:
|
||||
await message.answer(f'Твоя эмодзя - {user_emoji}', parse_mode='HTML')
|
||||
@@ -167,7 +167,7 @@ class VoiceHandlers:
|
||||
await state.set_state(STATE_START)
|
||||
await message.forward(chat_id=settings['Telegram']['group_for_logs'])
|
||||
await update_user_info(VOICE_BOT_NAME, message)
|
||||
user_emoji = get_user_emoji_safe(bot_db, message.from_user.id)
|
||||
user_emoji = await get_user_emoji_safe(bot_db, message.from_user.id)
|
||||
|
||||
# Создаем сервис и отправляем приветственные сообщения
|
||||
voice_service = VoiceBotService(bot_db, settings)
|
||||
@@ -175,7 +175,7 @@ class VoiceHandlers:
|
||||
|
||||
# Отмечаем, что пользователь получил приветственное сообщение
|
||||
try:
|
||||
bot_db.mark_voice_bot_welcome_received(message.from_user.id)
|
||||
await bot_db.mark_voice_bot_welcome_received(message.from_user.id)
|
||||
logger.info(f"Пользователь {message.from_user.id}: отмечен как получивший приветствие")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при отметке получения приветствия: {e}")
|
||||
@@ -194,7 +194,7 @@ class VoiceHandlers:
|
||||
|
||||
# Очищаем прослушивания через сервис
|
||||
voice_service = VoiceBotService(bot_db, settings)
|
||||
voice_service.clear_user_listenings(message.from_user.id)
|
||||
await voice_service.clear_user_listenings(message.from_user.id)
|
||||
|
||||
listenings_cleared_message = messages.get_message(get_first_name(message), 'LISTENINGS_CLEARED_MESSAGE')
|
||||
await message.answer(
|
||||
@@ -218,7 +218,7 @@ class VoiceHandlers:
|
||||
await message.answer(text=record_voice_message, reply_markup=markup)
|
||||
|
||||
try:
|
||||
message_with_date = get_last_message_text(bot_db)
|
||||
message_with_date = await get_last_message_text(bot_db)
|
||||
if message_with_date:
|
||||
await message.answer(text=message_with_date, parse_mode="html")
|
||||
except Exception as e:
|
||||
@@ -240,7 +240,7 @@ class VoiceHandlers:
|
||||
await message.forward(chat_id=settings['Telegram']['group_for_logs'])
|
||||
markup = get_main_keyboard()
|
||||
|
||||
if validate_voice_message(message):
|
||||
if await validate_voice_message(message):
|
||||
markup_for_voice = get_reply_keyboard_for_voice()
|
||||
|
||||
# Отправляем аудио в приватный канал
|
||||
@@ -252,7 +252,7 @@ class VoiceHandlers:
|
||||
)
|
||||
|
||||
# Сохраняем в базу инфо о посте
|
||||
bot_db.set_user_id_and_message_id_for_voice_bot(sent_message.message_id, message.from_user.id)
|
||||
await bot_db.set_user_id_and_message_id_for_voice_bot(sent_message.message_id, message.from_user.id)
|
||||
|
||||
# Отправляем юзеру ответ и возвращаем его в меню
|
||||
voice_saved_message = messages.get_message(get_first_name(message), 'VOICE_SAVED_MESSAGE')
|
||||
@@ -278,13 +278,13 @@ class VoiceHandlers:
|
||||
|
||||
try:
|
||||
# Получаем случайное аудио
|
||||
audio_data = voice_service.get_random_audio(message.from_user.id)
|
||||
audio_data = await voice_service.get_random_audio(message.from_user.id)
|
||||
|
||||
if not audio_data:
|
||||
no_audio_message = messages.get_message(get_first_name(message), 'NO_AUDIO_MESSAGE')
|
||||
await message.answer(text=no_audio_message, reply_markup=markup)
|
||||
try:
|
||||
message_with_date = get_last_message_text(bot_db)
|
||||
message_with_date = await get_last_message_text(bot_db)
|
||||
if message_with_date:
|
||||
await message.answer(text=message_with_date, parse_mode="html")
|
||||
except Exception as e:
|
||||
@@ -331,10 +331,10 @@ class VoiceHandlers:
|
||||
)
|
||||
|
||||
# Маркируем сообщение как прослушанное только после успешной отправки
|
||||
voice_service.mark_audio_as_listened(audio_for_user, message.from_user.id)
|
||||
await voice_service.mark_audio_as_listened(audio_for_user, message.from_user.id)
|
||||
|
||||
# Получаем количество оставшихся аудио только после успешной отправки
|
||||
remaining_count = voice_service.get_remaining_audio_count(message.from_user.id) - 1
|
||||
remaining_count = await voice_service.get_remaining_audio_count(message.from_user.id) - 1
|
||||
await message.answer(
|
||||
text=f'Осталось непрослушанных: <b>{remaining_count}</b>',
|
||||
reply_markup=markup
|
||||
|
||||
Reference in New Issue
Block a user