feat: добавлена система чата с модерацией
Some checks reported errors
continuous-integration/drone/push Build encountered an error

Реализована полнофункциональная система чата с двумя режимами работы:

## Режимы работы:
- Broadcast: рассылка сообщений всем пользователям
- Forward: пересылка сообщений в указанную группу/канал

## Функционал:
- Поддержка всех типов сообщений: text, photo, video, document, animation, sticker, voice
- Система банов: личные баны пользователей и глобальный бан чата
- Модерация: удаление сообщений с отслеживанием в БД
- История сообщений с сохранением ID пересланных сообщений

## Структура БД (миграция 005):
- chat_settings: настройки чата (режим, ID канала, глобальный бан)
- banned_users: история банов с причинами и информацией о модераторе
- chat_messages: история сообщений с типами, файлами и картой доставки (JSONB)

## Сервисы:
- ChatSettingsService: управление настройками чата
- BanService: управление банами пользователей
- ChatMessageService: работа с историей сообщений
- ChatPermissionService: проверка прав на отправку сообщений

## Обработчики:
- chat_handlers.py: обработка сообщений пользователей (7 типов контента)
- admin_chat_handlers.py: админские команды управления чатом

## Админские команды:
- /chat_mode - переключение режима (broadcast/forward)
- /set_forward <chat_id> - установка ID канала для пересылки
- /ban <user_id> [причина] - бан пользователя
- /unban <user_id> - разбан пользователя
- /banlist - список забаненных
- /global_ban - включение/выключение глобального бана
- /delete_msg - удаление сообщения (ответ на сообщение)
- /chat_stats - статистика чата

## Документация:
- docs/CHAT_SYSTEM.md: полное описание системы с примерами использования

Изменено файлов: 7 (2 modified, 5 new)
- main.py: подключены chat_router и admin_chat_router
- src/core/models.py: добавлены модели ChatSettings, BannedUser, ChatMessage
- migrations/versions/005_add_chat_system.py: миграция создания таблиц
- src/core/chat_services.py: сервисный слой для чата (267 строк)
- src/handlers/chat_handlers.py: обработчики сообщений (447 строк)
- src/handlers/admin_chat_handlers.py: админские команды (369 строк)
- docs/CHAT_SYSTEM.md: документация (390 строк)
This commit is contained in:
2025-11-16 14:25:09 +09:00
parent 505d26f0e9
commit b6c27b7b70
7 changed files with 1592 additions and 1 deletions

View File

@@ -0,0 +1,91 @@
"""Add chat system tables
Revision ID: 005
Revises: 004
Create Date: 2025-11-16 14:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '005'
down_revision = '004'
branch_labels = None
depends_on = None
def upgrade():
# Таблица настроек чата
op.create_table(
'chat_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('mode', sa.String(), nullable=False, server_default='broadcast'), # broadcast или forward
sa.Column('forward_chat_id', sa.String(), nullable=True), # ID группы/канала для пересылки
sa.Column('global_ban', sa.Boolean(), nullable=False, server_default='false'), # Глобальный бан чата
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.PrimaryKeyConstraint('id')
)
# Вставляем дефолтные настройки
op.execute(
"INSERT INTO chat_settings (id, mode, global_ban) VALUES (1, 'broadcast', false)"
)
# Таблица забаненных пользователей
op.create_table(
'banned_users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False), # ID пользователя в системе
sa.Column('telegram_id', sa.BigInteger(), nullable=False), # Telegram ID
sa.Column('banned_by', sa.Integer(), nullable=False), # ID админа
sa.Column('reason', sa.Text(), nullable=True), # Причина бана
sa.Column('banned_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'), # Активен ли бан
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['banned_by'], ['users.id'], ondelete='SET NULL')
)
# Индексы для быстрого поиска
op.create_index('ix_banned_users_telegram_id', 'banned_users', ['telegram_id'])
op.create_index('ix_banned_users_is_active', 'banned_users', ['is_active'])
# Таблица сообщений чата (для хранения истории и модерации)
op.create_table(
'chat_messages',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False), # Отправитель
sa.Column('telegram_message_id', sa.Integer(), nullable=False), # ID сообщения в Telegram
sa.Column('message_type', sa.String(), nullable=False), # text, photo, video, document, etc.
sa.Column('text', sa.Text(), nullable=True), # Текст сообщения
sa.Column('file_id', sa.String(), nullable=True), # ID файла в Telegram
sa.Column('forwarded_message_ids', postgresql.JSONB(), nullable=True), # Список ID пересланных сообщений
sa.Column('is_deleted', sa.Boolean(), nullable=False, server_default='false'),
sa.Column('deleted_by', sa.Integer(), nullable=True), # Кто удалил
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['deleted_by'], ['users.id'], ondelete='SET NULL')
)
# Индексы
op.create_index('ix_chat_messages_user_id', 'chat_messages', ['user_id'])
op.create_index('ix_chat_messages_created_at', 'chat_messages', ['created_at'])
op.create_index('ix_chat_messages_is_deleted', 'chat_messages', ['is_deleted'])
def downgrade():
op.drop_index('ix_chat_messages_is_deleted', table_name='chat_messages')
op.drop_index('ix_chat_messages_created_at', table_name='chat_messages')
op.drop_index('ix_chat_messages_user_id', table_name='chat_messages')
op.drop_table('chat_messages')
op.drop_index('ix_banned_users_is_active', table_name='banned_users')
op.drop_index('ix_banned_users_telegram_id', table_name='banned_users')
op.drop_table('banned_users')
op.drop_table('chat_settings')