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:
2025-09-02 18:22:02 +03:00
parent 013892dcb7
commit 1c6a37bc12
59 changed files with 5682 additions and 4204 deletions

View File

@@ -0,0 +1,258 @@
from datetime import datetime
from typing import Optional, List, Dict, Any
from database.base import DatabaseConnection
from database.models import User
class UserRepository(DatabaseConnection):
"""Репозиторий для работы с пользователями."""
async def create_tables(self):
"""Создание таблицы пользователей."""
query = '''
CREATE TABLE IF NOT EXISTS our_users (
user_id INTEGER NOT NULL PRIMARY KEY,
first_name TEXT,
full_name TEXT,
username TEXT,
is_bot BOOLEAN DEFAULT 0,
language_code TEXT,
has_stickers BOOLEAN DEFAULT 0 NOT NULL,
emoji TEXT,
date_added INTEGER NOT NULL,
date_changed INTEGER NOT NULL,
voice_bot_welcome_received BOOLEAN DEFAULT 0
)
'''
await self._execute_query(query)
self.logger.info("Таблица пользователей создана")
async def user_exists(self, user_id: int) -> bool:
"""Проверяет, существует ли пользователь в базе данных."""
query = "SELECT user_id FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
self.logger.info(f"Проверка существования пользователя: user_id={user_id}, результат={rows}")
return bool(len(rows))
async def add_user(self, user: User) -> None:
"""Добавление нового пользователя."""
if not user.date_added:
user.date_added = int(datetime.now().timestamp())
if not user.date_changed:
user.date_changed = int(datetime.now().timestamp())
query = """
INSERT INTO our_users (user_id, first_name, full_name, username, is_bot,
language_code, emoji, has_stickers, date_added, date_changed, voice_bot_welcome_received)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
params = (user.user_id, user.first_name, user.full_name, user.username,
user.is_bot, user.language_code, user.emoji, user.has_stickers,
user.date_added, user.date_changed, user.voice_bot_welcome_received)
await self._execute_query(query, params)
self.logger.info(f"Новый пользователь добавлен: {user.user_id}")
async def get_user_info(self, user_id: int) -> Optional[User]:
"""Получение информации о пользователе."""
query = "SELECT username, full_name, has_stickers, emoji FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
row = rows[0] if rows else None
if row:
return User(
user_id=user_id,
first_name="", # Не получаем из этого запроса
full_name=row[1],
username=row[0],
has_stickers=bool(row[2]) if row[2] is not None else False,
emoji=row[3]
)
return None
async def get_user_by_id(self, user_id: int) -> Optional[User]:
"""Получение пользователя по ID."""
query = "SELECT * FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
row = rows[0] if rows else None
if row:
return User(
user_id=row[0],
first_name=row[1],
full_name=row[2],
username=row[3],
is_bot=bool(row[4]),
language_code=row[5],
has_stickers=bool(row[6]),
emoji=row[7],
date_added=row[8],
date_changed=row[9],
voice_bot_welcome_received=bool(row[10]) if len(row) > 10 else False
)
return None
async def get_username(self, user_id: int) -> Optional[str]:
"""Возвращает username пользователя."""
query = "SELECT username FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
row = rows[0] if rows else None
if row:
username = row[0]
self.logger.info(f"Username пользователя найден: user_id={user_id}, username={username}")
return username
return None
async def get_user_id_by_username(self, username: str) -> Optional[int]:
"""Возвращает user_id пользователя по username."""
query = "SELECT user_id FROM our_users WHERE username = ?"
rows = await self._execute_query_with_result(query, (username,))
row = rows[0] if rows else None
if row:
user_id = row[0]
self.logger.info(f"User_id пользователя найден: username={username}, user_id={user_id}")
return user_id
return None
async def get_full_name_by_id(self, user_id: int) -> Optional[str]:
"""Возвращает full_name пользователя."""
query = "SELECT full_name FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
row = rows[0] if rows else None
if row:
full_name = row[0]
self.logger.info(f"Full_name пользователя найден: user_id={user_id}, full_name={full_name}")
return full_name
return None
async def get_user_first_name(self, user_id: int) -> Optional[str]:
"""Возвращает first_name пользователя."""
query = "SELECT first_name FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
row = rows[0] if rows else None
if row:
first_name = row[0]
self.logger.info(f"First_name пользователя найден: user_id={user_id}, first_name={first_name}")
return first_name
return None
async def get_all_user_ids(self) -> List[int]:
"""Возвращает список всех user_id."""
query = "SELECT user_id FROM our_users"
rows = await self._execute_query_with_result(query)
user_ids = [row[0] for row in rows]
self.logger.info(f"Получен список всех user_id: {user_ids}")
return user_ids
async def get_last_users(self, limit: int = 30) -> List[tuple]:
"""Получение последних пользователей."""
query = "SELECT full_name, user_id FROM our_users ORDER BY date_changed DESC LIMIT ?"
rows = await self._execute_query_with_result(query, (limit,))
return rows
async def update_user_date(self, user_id: int) -> None:
"""Обновление даты последнего изменения пользователя."""
date_changed = int(datetime.now().timestamp())
query = "UPDATE our_users SET date_changed = ? WHERE user_id = ?"
await self._execute_query(query, (date_changed, user_id))
async def update_user_info(self, user_id: int, username: str = None, full_name: str = None) -> None:
"""Обновление информации о пользователе."""
if username and full_name:
query = "UPDATE our_users SET username = ?, full_name = ? WHERE user_id = ?"
params = (username, full_name, user_id)
elif username:
query = "UPDATE our_users SET username = ? WHERE user_id = ?"
params = (username, user_id)
elif full_name:
query = "UPDATE our_users SET full_name = ? WHERE user_id = ?"
params = (full_name, user_id)
else:
return
await self._execute_query(query, params)
async def update_user_emoji(self, user_id: int, emoji: str) -> None:
"""Обновление эмодзи пользователя."""
query = "UPDATE our_users SET emoji = ? WHERE user_id = ?"
await self._execute_query(query, (emoji, user_id))
async def update_stickers_info(self, user_id: int) -> None:
"""Обновление информации о стикерах."""
query = "UPDATE our_users SET has_stickers = 1 WHERE user_id = ?"
await self._execute_query(query, (user_id,))
async def get_stickers_info(self, user_id: int) -> bool:
"""Получение информации о стикерах."""
query = "SELECT has_stickers FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
row = rows[0] if rows else None
return bool(row[0]) if row and row[0] is not None else False
async def check_emoji_exists(self, emoji: str) -> bool:
"""Проверка существования эмодзи."""
query = "SELECT 1 FROM our_users WHERE emoji = ?"
rows = await self._execute_query_with_result(query, (emoji,))
row = rows[0] if rows else None
return bool(row)
async def get_user_emoji(self, user_id: int) -> str:
"""
Получает эмодзи пользователя.
Args:
user_id: ID пользователя.
Returns:
str: Эмодзи пользователя или "Смайл еще не определен" если не установлен.
"""
query = "SELECT emoji FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
row = rows[0] if rows else None
if row and row[0]:
emoji = row[0]
self.logger.info(f"Эмодзи пользователя найден: user_id={user_id}, emoji={emoji}")
return str(emoji)
else:
self.logger.info(f"Эмодзи пользователя не найден: user_id={user_id}")
return "Смайл еще не определен"
async def check_emoji_for_user(self, user_id: int) -> str:
"""
Проверяет, есть ли уже у пользователя назначенный emoji.
Args:
user_id: ID пользователя.
Returns:
str: Эмодзи пользователя или "Смайл еще не определен" если не установлен.
"""
return await self.get_user_emoji(user_id)
async def check_voice_bot_welcome_received(self, user_id: int) -> bool:
"""Проверяет, получал ли пользователь приветственное сообщение от voice_bot."""
query = "SELECT voice_bot_welcome_received FROM our_users WHERE user_id = ?"
rows = await self._execute_query_with_result(query, (user_id,))
row = rows[0] if rows else None
if row:
welcome_received = bool(row[0])
self.logger.info(f"Пользователь {user_id} получал приветствие: {welcome_received}")
return welcome_received
return False
async def mark_voice_bot_welcome_received(self, user_id: int) -> bool:
"""Отмечает, что пользователь получил приветственное сообщение от voice_bot."""
try:
query = "UPDATE our_users SET voice_bot_welcome_received = 1 WHERE user_id = ?"
await self._execute_query(query, (user_id,))
self.logger.info(f"Пользователь {user_id} отмечен как получивший приветствие")
return True
except Exception as e:
self.logger.error(f"Ошибка при отметке получения приветствия: {e}")
return False