diff --git a/tg_bot/infrastructure/telegram/__init__.py b/tg_bot/infrastructure/telegram/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tg_bot/infrastructure/telegram/bot.py b/tg_bot/infrastructure/telegram/bot.py
new file mode 100644
index 0000000..9f7a44b
--- /dev/null
+++ b/tg_bot/infrastructure/telegram/bot.py
@@ -0,0 +1,51 @@
+import logging
+from aiogram import Bot, Dispatcher
+from aiogram.enums import ParseMode
+from aiogram.client.default import DefaultBotProperties
+
+from tg_bot.config.settings import settings
+from tg_bot.infrastructure.telegram.handlers import (
+ start_handler,
+ help_handler,
+ stats_handler
+)
+
+logger = logging.getLogger(__name__)
+
+
+async def create_bot() -> tuple[Bot, Dispatcher]:
+ bot = Bot(
+ token=settings.TELEGRAM_BOT_TOKEN,
+ default=DefaultBotProperties(parse_mode=ParseMode.HTML)
+ )
+ dp = Dispatcher()
+ dp.include_router(start_handler.router)
+ dp.include_router(help_handler.router)
+ dp.include_router(stats_handler.router)
+ return bot, dp
+
+
+async def start_bot():
+ try:
+ bot, dp = await create_bot()
+
+ try:
+ webhook_info = await bot.get_webhook_info()
+ if webhook_info.url:
+ await bot.delete_webhook(drop_pending_updates=True)
+ except Exception:
+ pass
+ print("=" * 50)
+ print("Telegram бот запускается")
+ print(f"Бот: @vibelawyer_bot")
+ print(f"Лимит: {settings.FREE_QUESTIONS_LIMIT} вопросов")
+ print(f"Команды: /start, /help, /buy, /stats, /mycollections, /search")
+ print("=" * 50)
+
+ await dp.start_polling(bot, drop_pending_updates=True)
+
+ except Exception as e:
+ logger.error(f"Ошибка запуска: {e}")
+ raise
+ finally:
+ await bot.session.close()
\ No newline at end of file
diff --git a/tg_bot/infrastructure/telegram/handlers/__init__.py b/tg_bot/infrastructure/telegram/handlers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tg_bot/infrastructure/telegram/handlers/help_handler.py b/tg_bot/infrastructure/telegram/handlers/help_handler.py
new file mode 100644
index 0000000..8a92920
--- /dev/null
+++ b/tg_bot/infrastructure/telegram/handlers/help_handler.py
@@ -0,0 +1,82 @@
+
+from aiogram import Router, types
+from aiogram.filters import Command
+from aiogram.types import Message
+
+from tg_bot.config.settings import settings
+
+router = Router()
+
+
+@router.message(Command("help"))
+async def cmd_help(message: Message):
+ help_text = (
+ f"VibeLawyerBot - помощь\n\n"
+
+ f"Основные команды:\n"
+ f"• /start - начать работу с ботом\n"
+ f"• /help - показать это сообщение\n"
+ f"• /buy - купить подписку\n"
+ f"• /stats - статистика и лимиты\n"
+ f"• /mypayments - история платежей\n\n"
+ f"Работа с коллекциями:\n"
+ f"• /mycollections - показать мои коллекции документов\n"
+ f"• /search - поиск документов в коллекции\n\n"
+
+ f"Как работает бот:\n"
+ f"1. У вас есть {settings.FREE_QUESTIONS_LIMIT} бесплатных вопросов\n"
+ f"2. Бот ищет ответы в ваших коллекциях документов\n"
+ f"3. После исчерпания лимита нужна подписка\n"
+ f"4. Подписка даёт неограниченный доступ\n\n"
+ f"О коллекциях:\n"
+ f"• Администратор загружает документы в коллекции\n"
+ f"• Вам предоставляется доступ к коллекциям\n"
+ f"• При задаче вопроса бот ищет ответы в ваших коллекциях\n"
+ f"• Используйте /mycollections для просмотра коллекций\n\n"
+
+ f"Оплата (тестовый режим):\n"
+ f"• Безопасно через ЮKассу\n"
+ f"• Сразу после оплаты доступ открывается\n"
+ f"• Тестовые карты для проверки:\n"
+ f" Успешная оплата: 5555 5555 5555 4477\n"
+ f" Срок: любой будущий (напр. 12/30)\n"
+ f" CVV: любой 3 цифры (напр. 123)\n\n"
+ f" Отказ в оплате: 5555 5555 5555 4445\n"
+ f" Срок: любой будущий\n"
+ f" CVV: любой 3 цифры\n\n"
+ f"• Поддержка: @vibelawyer_support\n\n"
+
+ f"Задавайте юридические вопросы, и бот поможет с ответами!"
+ )
+
+ await message.answer(help_text, parse_mode="HTML")
+
+
+@router.message(Command("testcards"))
+async def cmd_testcards(message: Message):
+ testcards_text = (
+ f"Тестовые банковские карты для оплаты\n\n"
+
+ f"Для тестирования оплаты используйте:\n\n"
+
+ f"Карта для успешной оплаты:\n"
+ f"• Номер: 5555 5555 5555 4477\n"
+ f"• Срок действия: ЛЮБОЙ будущий (например: 12/30)\n"
+ f"• CVV код: ЛЮБЫЕ 3 цифры (например: 123)\n"
+ f"• Результат: Оплата пройдёт успешно\n\n"
+
+ f"Карта для отказа в оплате:\n"
+ f"• Номер: 5555 5555 5555 4445\n"
+ f"• Срок действия: ЛЮБОЙ будущий\n"
+ f"• CVV код: ЛЮБЫЕ 3 цифры\n"
+ f"• Результат: Оплата будет отклонена\n\n"
+
+ f"Важно:\n"
+ f"• Это тестовые карты, реальные деньги не списываются\n"
+ f"• Используются только для проверки работы оплаты\n"
+ f"• После успешной тестовой оплаты premium активируется\n\n"
+
+ f"Для оплаты подписки используйте команду /buy"
+ )
+
+ await message.answer(testcards_text, parse_mode="HTML")
diff --git a/tg_bot/infrastructure/telegram/handlers/start_handler.py b/tg_bot/infrastructure/telegram/handlers/start_handler.py
new file mode 100644
index 0000000..71be4c8
--- /dev/null
+++ b/tg_bot/infrastructure/telegram/handlers/start_handler.py
@@ -0,0 +1,70 @@
+from aiogram import Router, types
+from aiogram.filters import Command
+from aiogram.types import Message
+from datetime import datetime
+
+from tg_bot.config.settings import settings
+from tg_bot.infrastructure.database.database import SessionLocal
+from tg_bot.infrastructure.database.models import UserModel
+
+router = Router()
+
+@router.message(Command("start"))
+async def cmd_start(message: Message):
+
+ user_id = message.from_user.id
+ username = message.from_user.username or ""
+ first_name = message.from_user.first_name or ""
+ last_name = message.from_user.last_name or ""
+ session = SessionLocal()
+ try:
+ user = session.query(UserModel).filter_by(
+ telegram_id=str(user_id)
+ ).first()
+
+ if not user:
+ user = UserModel(
+ telegram_id=str(user_id),
+ username=username,
+ first_name=first_name,
+ last_name=last_name
+ )
+ session.add(user)
+ session.commit()
+ print(f"Новый пользователь: {user_id}")
+ else:
+ user.username = username
+ user.first_name = first_name
+ user.last_name = last_name
+ session.commit()
+
+ except Exception as e:
+ print(f"Ошибка сохранения пользователя: {e}")
+ session.rollback()
+ finally:
+ session.close()
+ welcome_text = (
+ f"Привет, {first_name}!\n\n"
+ f"Я VibeLawyerBot - ваш помощник в юридических вопросах.\n\n"
+
+ f"Как я работаю:\n"
+ f"1. Администратор загружает документы в коллекции\n"
+ f"2. Вы задаёте вопрос на любую юридическую тему\n"
+ f"3. Я ищу ответы в ваших коллекциях документов\n"
+ f"4. Даю развернутый ответ на основе найденных документов\n"
+ f"5. Первые {settings.FREE_QUESTIONS_LIMIT} вопросов - бесплатно\n"
+ f"6. Для продолжения нужна подписка (/buy)\n\n"
+
+ f"Основные команды:\n"
+ f"• /help - подробная помощь\n"
+ f"• /buy - купить подписку\n"
+ f"• /stats - ваша статистика\n"
+ f"• /mypayments - история платежей\n"
+ f"• /mycollections - мои коллекции документов\n"
+ f"• /search - поиск в коллекции\n\n"
+
+ f"Готовы начать? Просто напишите ваш вопрос!\n\n"
+ f"Для получения полного доступа используйте /buy"
+ )
+
+ await message.answer(welcome_text, parse_mode="HTML")
diff --git a/tg_bot/infrastructure/telegram/handlers/stats_handler.py b/tg_bot/infrastructure/telegram/handlers/stats_handler.py
new file mode 100644
index 0000000..3ce7534
--- /dev/null
+++ b/tg_bot/infrastructure/telegram/handlers/stats_handler.py
@@ -0,0 +1,65 @@
+
+from aiogram import Router, types
+from aiogram.filters import Command
+from aiogram.types import Message
+
+from tg_bot.config.settings import settings
+from tg_bot.infrastructure.database.database import SessionLocal
+from tg_bot.infrastructure.database.models import UserModel
+
+router = Router()
+
+
+@router.message(Command("stats"))
+async def cmd_stats(message: Message):
+ user_id = message.from_user.id
+
+ session = SessionLocal()
+ try:
+ user = session.query(UserModel).filter_by(
+ telegram_id=str(user_id)
+ ).first()
+
+ if user:
+ stats_text = (
+ f"Ваша статистика\n\n"
+ f"Основное:\n"
+ f"• ID: {user_id}\n"
+ f"• Premium: {'Да' if user.is_premium else 'Нет'}\n"
+ f"• Вопросов использовано: {user.questions_used}/{settings.FREE_QUESTIONS_LIMIT}\n\n"
+ )
+
+ if user.is_premium:
+ stats_text += (
+ f"Premium статус:\n"
+ f"• Активен до: {user.premium_until.strftime('%d.%m.%Y') if user.premium_until else 'Не указано'}\n"
+ f"• Лимит вопросов: безлимитно\n\n"
+ )
+ else:
+ remaining = max(0, settings.FREE_QUESTIONS_LIMIT - user.questions_used)
+ stats_text += (
+ f"Бесплатный доступ:\n"
+ f"• Осталось вопросов: {remaining}\n"
+ f"• Для безлимита: /buy\n\n"
+ )
+
+ else:
+ stats_text = (
+ f"Добро пожаловать!\n\n"
+ f"Вы новый пользователь.\n"
+ f"• Ваш ID: {user_id}\n"
+ f"• Бесплатных вопросов: {settings.FREE_QUESTIONS_LIMIT}\n"
+ f"• Для начала работы просто задайте вопрос!\n\n"
+ f"Используйте /buy для получения полного доступа"
+ )
+
+ await message.answer(stats_text, parse_mode="HTML")
+
+ except Exception as e:
+ await message.answer(
+ f"Ошибка получения статистики\n\n"
+ f"Попробуйте позже.",
+ parse_mode="HTML"
+ )
+ finally:
+ session.close()
diff --git a/tg_bot/main.py b/tg_bot/main.py
new file mode 100644
index 0000000..fa162e7
--- /dev/null
+++ b/tg_bot/main.py
@@ -0,0 +1,43 @@
+import asyncio
+import logging
+import sys
+import os
+
+current_dir = os.path.dirname(os.path.abspath(__file__))
+parent_dir = os.path.dirname(current_dir)
+sys.path.insert(0, parent_dir)
+
+from tg_bot.config.settings import settings
+
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ handlers=[
+ logging.FileHandler(settings.LOG_FILE),
+ logging.StreamHandler()
+ ]
+)
+logger = logging.getLogger("vibelawyer_bot")
+
+
+async def main():
+ logger.info("=" * 50)
+ logger.info(f"Запуск {settings.APP_NAME} v{settings.VERSION}")
+ logger.info(f"Режим: {'РАЗРАБОТКА' if settings.DEBUG else 'ПРОДАКШН'}")
+ logger.info(f"Лимит вопросов: {settings.FREE_QUESTIONS_LIMIT}")
+ logger.info("=" * 50)
+
+ try:
+ from tg_bot.infrastructure.telegram.bot import start_bot
+ await start_bot()
+
+ except KeyboardInterrupt:
+ logger.info("Бот остановлен пользователем")
+ print("\nБот остановлен")
+ except Exception as e:
+ logger.error(f"Ошибка запуска: {e}")
+ print(f"Ошибка запуска: {e}")
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
\ No newline at end of file