smart-bot-factory


Namesmart-bot-factory JSON
Version 0.2.0 PyPI version JSON
download
home_pageNone
SummaryБиблиотека для создания умных чат-ботов
upload_time2025-10-07 14:45:34
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT
keywords chatbot cli openai supabase telegram
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Smart Bot Factory

Современная библиотека для создания умных чат-ботов на Python с использованием OpenAI, Telegram и Supabase.

## 🚀 Возможности

- **🤖 AI Integration** - Полная интеграция с OpenAI GPT для умных диалогов
- **📱 Telegram Bot API** - Поддержка через aiogram 3.x
- **💾 Supabase Backend** - Хранение данных, сессий и аналитики
- **🎯 Router System** - Модульная система обработчиков событий
- **⏰ Smart Scheduler** - Умное планирование задач с проверкой активности пользователей
- **🌍 Global Handlers** - Массовые рассылки и глобальные события
- **🧪 Testing Suite** - Встроенная система тестирования ботов
- **🛠️ CLI Tools** - Удобный интерфейс командной строки
- **👥 Admin Panel** - Система администрирования через Telegram
- **📊 Analytics** - Встроенная аналитика и отчеты

## 📦 Установка

### Системные требования

Перед установкой убедитесь, что у вас установлено:

- **Python 3.9+** (рекомендуется 3.11+)
- **pip** или **uv** для управления пакетами
- Доступ к интернету для установки зависимостей

### Из PyPI (рекомендуется)

```bash
pip install smart_bot_factory
```

### С помощью uv (современный менеджер пакетов)

```bash
uv add smart_bot_factory
```

### Из исходников (для разработки)

```bash
# Клонируйте репозиторий
git clone https://github.com/yourusername/chat-bots.git
cd chat-bots

# Установите зависимости через uv
uv sync

# Или через pip
pip install -e .
```

### Установка определенной версии

```bash
pip install smart_bot_factory==0.1.8
```

### Проверка установки

После установки проверьте доступность CLI:

```bash
sbf --help
```

Вы должны увидеть список доступных команд.

### Настройка внешних сервисов

Для работы бота вам понадобятся:

1. **Telegram Bot Token**
   - Создайте бота через [@BotFather](https://t.me/botfather)
   - Получите токен вида `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`

2. **OpenAI API Key**
   - Зарегистрируйтесь на [platform.openai.com](https://platform.openai.com)
   - Создайте API ключ в разделе API Keys
   - Ключ имеет вид `sk-...`

3. **Supabase Project**
   - Создайте проект на [supabase.com](https://supabase.com)
   - Получите URL проекта и `anon` ключ в Project Settings → API
   - Импортируйте SQL схему из `smart_bot_factory/database/schema.sql`

## ⚡ Быстрый старт

### 1. Создайте нового бота

```bash
sbf create my-bot
```

Это создаст:
- 📁 `bots/my-bot/` - папка с конфигурацией бота
- 📄 `my-bot.py` - основной файл запуска
- ⚙️ `bots/my-bot/.env` - конфигурация окружения
- 📝 `bots/my-bot/prompts/` - промпты для AI
- 🧪 `bots/my-bot/tests/` - тестовые сценарии

### 2. Настройте переменные окружения

Отредактируйте `bots/my-bot/.env`:

```env
# Telegram
TELEGRAM_BOT_TOKEN=your_bot_token

# Supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your_supabase_key

# OpenAI
OPENAI_API_KEY=sk-your-openai-key
OPENAI_MODEL=gpt-4o-mini

# Администраторы
ADMIN_TELEGRAM_IDS=123456789,987654321
```

### 3. Запустите бота

```bash
sbf run my-bot
```

## 📚 Архитектура

### Router System

Smart Bot Factory использует **два типа роутеров** для организации обработчиков:

1. **EventRouter** - для бизнес-логики (события, задачи, глобальные обработчики)
2. **TelegramRouter** - для Telegram команд, сообщений и callback'ов

```python
from smart_bot_factory.router import EventRouter, TelegramRouter
from smart_bot_factory.message import send_message_by_human
from smart_bot_factory.creation import BotBuilder
from aiogram import F
from aiogram.filters import Command
from aiogram.types import Message
from aiogram.fsm.context import FSMContext

# EventRouter - для бизнес-логики
event_router = EventRouter("my_bot_events")

@event_router.event_handler("appointment_booking", notify=True)
async def handle_booking(user_id: int, event_data: str):
    """Обработчик записи на прием"""
    await send_message_by_human(
        user_id=user_id,
        message_text=f"✅ Запись подтверждена! {event_data}"
    )
    return {"status": "success"}

# TelegramRouter - для Telegram команд и сообщений
telegram_router = TelegramRouter("my_bot_telegram")

@telegram_router.router.message(Command("price", "цена"))
async def handle_price(message: Message, state: FSMContext):
    """Обработчик команды /price"""
    await message.answer("💰 Наши цены...")

@telegram_router.router.message(F.text.contains("помощь"))
async def handle_help(message: Message, state: FSMContext):
    """Обработчик запросов помощи"""
    await message.answer("📖 Чем могу помочь?")

# Запуск бота
async def main():
    bot = BotBuilder("my-bot")
    
    # Регистрируем роутеры (можно по одному или несколько сразу)
    bot.register_routers(event_router)  # EventRouter
    bot.register_telegram_routers(telegram_router)  # TelegramRouter
    
    await bot.build()
    await bot.start()
```

### TelegramRouter - Обработчики Telegram

**TelegramRouter** позволяет добавлять пользовательские команды и обработчики сообщений в Telegram бота, используя стандартный aiogram API:

#### Команды

```python
from smart_bot_factory.router import TelegramRouter
from aiogram.filters import Command
from aiogram.types import Message
from aiogram.fsm.context import FSMContext

telegram_router = TelegramRouter("my_commands")

@telegram_router.router.message(Command("start", "старт"))
async def cmd_start(message: Message, state: FSMContext):
    """Обработчик команды /start"""
    await message.answer("👋 Привет!")

@telegram_router.router.message(Command("price"))
async def cmd_price(message: Message, state: FSMContext):
    """Обработчик команды /price"""
    await message.answer("💰 Цены:\n1. Базовый - 1000₽\n2. Премиум - 5000₽")
```

#### Фильтры по тексту

```python
from aiogram import F

@telegram_router.router.message(F.text.lower().contains("цена"))
async def handle_price_question(message: Message, state: FSMContext):
    """Когда пользователь спрашивает о цене"""
    await message.answer("💡 Используйте /price для просмотра цен")

@telegram_router.router.message(F.text.startswith("!"))
async def handle_commands(message: Message, state: FSMContext):
    """Обработка кастомных команд с !"""
    command = message.text[1:]
    await message.answer(f"Выполняю команду: {command}")
```

#### Callback Query

```python
from aiogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton

@telegram_router.router.callback_query(F.data.startswith("buy_"))
async def handle_buy(callback: CallbackQuery, state: FSMContext):
    """Обработка покупки"""
    product_id = callback.data.split("_")[1]
    await callback.answer("Оформляем...")
    await callback.message.answer(f"✅ Товар {product_id} добавлен в корзину")

@telegram_router.router.message(Command("catalog"))
async def show_catalog(message: Message, state: FSMContext):
    """Показывает каталог с кнопками"""
    keyboard = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="Купить товар 1", callback_data="buy_1")],
        [InlineKeyboardButton(text="Купить товар 2", callback_data="buy_2")]
    ])
    await message.answer("🛍️ Каталог товаров:", reply_markup=keyboard)
```

#### Регистрация нескольких роутеров

```python
# Создаем несколько TelegramRouter для разных модулей
commands_router = TelegramRouter("commands")
admin_router = TelegramRouter("admin_commands")
callbacks_router = TelegramRouter("callbacks")

# Регистрируем все сразу
bot_builder.register_telegram_routers(
    commands_router,
    admin_router,
    callbacks_router
)
```

**Важно:** TelegramRouter - это обертка над aiogram Router, предоставляющая доступ к полному функционалу aiogram через свойство `.router`.

### EventRouter - Типы обработчиков событий

#### 1. Event Handlers - Обработчики событий

Немедленная обработка событий от AI:

```python
@router.event_handler("phone_collected", notify=True, once_only=True)
async def handle_phone(user_id: int, event_data: str):
    """Вызывается когда AI собирает номер телефона"""
    # event_data содержит данные от AI
    phone = parse_phone(event_data)
    
    # Сохраняем в CRM
    await save_to_crm(user_id, phone)
    
    return {"status": "saved", "phone": phone}
```

**Параметры:**
- `notify` - уведомлять админов (default: False)
- `once_only` - выполнить только один раз (default: True)

#### 2. Scheduled Tasks - Запланированные задачи

Задачи с отложенным выполнением для конкретного пользователя:

```python
@router.schedule_task("send_reminder", delay="2h", smart_check=True)
async def send_reminder(user_id: int, reminder_text: str):
    """Отправит напоминание через 2 часа"""
    await send_message_by_human(
        user_id=user_id,
        message_text=f"🔔 {reminder_text}"
    )
    return {"status": "sent"}
```

**Параметры:**
- `delay` - задержка (обязательно): `"1h"`, `"30m"`, `"2h 15m"`, `3600`
- `smart_check` - умная проверка активности (default: True)
- `once_only` - выполнить только один раз (default: True)
- `event_type` - привязка к событию для напоминаний

**Smart Check:**
- Отменяет задачу если пользователь перешел на другой этап
- Переносит выполнение если пользователь был активен недавно
- Сохраняет session_id для точного отслеживания

#### 3. Global Handlers - Глобальные обработчики

Массовые действия для всех пользователей:

```python
@router.global_handler("mass_notification", delay="1h", notify=True)
async def send_announcement(announcement_text: str):
    """Отправит анонс всем пользователям через 1 час"""
    from smart_bot_factory.message import send_message_to_users_by_stage
    
    await send_message_to_users_by_stage(
        stage="introduction",
        message_text=announcement_text,
        bot_id="my-bot"
    )
    
    return {"status": "completed"}
```

**Параметры:**
- `delay` - задержка (обязательно)
- `notify` - уведомлять админов (default: False)
- `once_only` - выполнить только один раз (default: True)

### Event-Based Reminders

Напоминания о событиях за определенное время:

```python
# Сначала создаем обработчик события
@router.event_handler("appointment_booking")
async def handle_booking(user_id: int, event_data: str):
    """Сохраняет запись: имя, телефон, дата, время"""
    return {"status": "saved", "data": event_data}

# Затем создаем напоминание
@router.schedule_task(
    "appointment_reminder",
    delay="2h",
    event_type="appointment_booking"  # Привязка к событию
)
async def remind_about_appointment(user_id: int, reminder_text: str):
    """Отправит напоминание за 2 часа до записи"""
    await send_message_by_human(
        user_id=user_id,
        message_text=f"⏰ Напоминание о записи через 2 часа!"
    )
    return {"status": "sent"}
```

Система автоматически:
1. Извлечет дату/время из события `appointment_booking`
2. Вычислит время напоминания (за 2 часа до записи)
3. Запланирует отправку в правильное время

## 🛠️ CLI Команды

```bash
# Создание и управление ботами
sbf create <bot-id>              # Создать нового бота
sbf create <bot-id> <template>   # Создать из шаблона
sbf copy <source> <new-id>       # Копировать существующего бота
sbf list                         # Показать всех ботов
sbf rm <bot-id>                  # Удалить бота

# Запуск
sbf run <bot-id>                 # Запустить бота

# Тестирование
sbf test <bot-id>                # Запустить все тесты
sbf test <bot-id> --file quick_scenarios.yaml
sbf test <bot-id> -v             # Подробный вывод
sbf test <bot-id> --max-concurrent 10

# Промпты
sbf prompts <bot-id>             # Список промптов
sbf prompts <bot-id> --edit welcome_message
sbf prompts <bot-id> --add new_prompt

# Конфигурация
sbf config <bot-id>              # Редактировать .env
sbf path                         # Показать путь к проекту
sbf link                         # Генератор UTM-ссылок
```

## 📝 Система промптов

Промпты хранятся в `bots/<bot-id>/prompts/`:

- `welcome_message.txt` - Приветственное сообщение
- `help_message.txt` - Справка для пользователя
- `1sales_context.txt` - Контекст продаж
- `2product_info.txt` - Информация о продукте
- `3objection_handling.txt` - Работа с возражениями
- `final_instructions.txt` - Финальные инструкции для AI

AI автоматически получает доступ к зарегистрированным обработчикам через промпт.

## 🧪 Тестирование

Создайте тестовые сценарии в YAML:

```yaml
# bots/my-bot/tests/scenarios.yaml
scenarios:
  - name: "Запись на прием"
    steps:
      - user: "Привет!"
        expect_stage: "introduction"
      
      - user: "Хочу записаться на прием"
        expect_stage: "qualification"
        expect_events:
          - type: "appointment_request"
      
      - user: "Меня зовут Иван, +79991234567, завтра в 15:00"
        expect_events:
          - type: "appointment_booking"
          - type: "appointment_reminder"  # Должно запланироваться
        expect_quality: ">= 8"
```

Запуск:
```bash
sbf test my-bot --file scenarios.yaml -v
```

## 💬 Отправка сообщений

### Отправка пользователю

```python
from smart_bot_factory.message import send_message_by_human

await send_message_by_human(
    user_id=123456789,
    message_text="Привет! Это сообщение от системы",
    session_id="optional-session-id"
)
```

### Массовая рассылка по этапу

```python
from smart_bot_factory.message import send_message_to_users_by_stage

await send_message_to_users_by_stage(
    stage="introduction",
    message_text="📢 Важное объявление!",
    bot_id="my-bot"
)
```

## 🗄️ База данных

Smart Bot Factory использует Supabase со следующими таблицами:

- `sales_users` - Пользователи
- `sales_chat_sessions` - Сессии диалогов
- `sales_chat_messages` - История сообщений
- `scheduled_events` - Запланированные события и задачи
- `admin_sessions` - Сессии администраторов

SQL схема доступна в `smart_bot_factory/database/`.

## 👥 Система администрирования

Добавьте ID администраторов в `.env`:

```env
ADMIN_TELEGRAM_IDS=123456789,987654321
ADMIN_SESSION_TIMEOUT_MINUTES=30
```

Админы получают:
- 📊 Статистику и аналитику
- 🔔 Уведомления о важных событиях (если `notify=True`)
- 🛠️ Доступ к специальным командам

## 🔧 Продвинутое использование

### Множественные роутеры

#### Регистрация нескольких EventRouter

```python
# handlers/main.py
main_router = EventRouter("main")

# handlers/admin.py
admin_router = EventRouter("admin")

# handlers/payments.py
payments_router = EventRouter("payments")

# app.py
bot = BotBuilder("my-bot")

# Можно по одному
bot.register_router(main_router)
bot.register_router(admin_router)

# Или все сразу
bot.register_routers(main_router, admin_router, payments_router)
```

#### Регистрация нескольких TelegramRouter

```python
# telegram/commands.py
commands_router = TelegramRouter("commands")

@commands_router.router.message(Command("start"))
async def start(message: Message, state: FSMContext):
    await message.answer("Привет!")

# telegram/callbacks.py
callbacks_router = TelegramRouter("callbacks")

@callbacks_router.router.callback_query(F.data.startswith("action_"))
async def handle_action(callback: CallbackQuery, state: FSMContext):
    await callback.answer("Выполнено!")

# app.py
bot = BotBuilder("my-bot")

# Регистрируем несколько Telegram роутеров сразу
bot.register_telegram_routers(commands_router, callbacks_router)
```

#### Комбинирование EventRouter и TelegramRouter

```python
# Создаем роутеры для разных целей
event_router = EventRouter("events")
telegram_router = TelegramRouter("telegram_handlers")

# EventRouter - бизнес-логика
@event_router.event_handler("payment_success")
async def handle_payment(user_id: int, event_data: str):
    await send_message_by_human(user_id, "✅ Оплата получена!")
    return {"status": "success"}

# TelegramRouter - пользовательские команды
@telegram_router.router.message(Command("balance"))
async def check_balance(message: Message, state: FSMContext):
    await message.answer("💰 Ваш баланс: 1000₽")

# Регистрируем оба типа роутеров
bot_builder.register_routers(event_router)
bot_builder.register_telegram_routers(telegram_router)
```

### Вложенные роутеры (EventRouter)

```python
main_router = EventRouter("main")
payments_router = EventRouter("payments")

# Включаем роутер платежей в основной
main_router.include_router(payments_router)

bot.register_router(main_router)
```

### Работа с клиентами

```python
from smart_bot_factory.supabase import SupabaseClient

# Создаем клиент для вашего бота
supabase = SupabaseClient("my-bot")

# Используем напрямую
users = supabase.client.table('sales_users').select('*').eq('bot_id', 'my-bot').execute()
```

## 📊 Структура проекта

```
my-project/
├── bots/                      # Папка с ботами
│   ├── my-bot/
│   │   ├── .env              # Конфигурация
│   │   ├── prompts/          # AI промпты
│   │   ├── tests/            # Тестовые сценарии
│   │   ├── files/            # Файлы бота
│   │   ├── welcome_files/    # Приветственные файлы
│   │   └── reports/          # Отчеты тестов
│   └── another-bot/
│       └── ...
├── my-bot.py                 # Основной файл запуска
├── another-bot.py
└── .env                      # Глобальная конфигурация (опционально)
```

## 🔄 Примеры

### Полный пример бота

```python
import asyncio

from smart_bot_factory.router import EventRouter, TelegramRouter
from smart_bot_factory.message import send_message_by_human, send_message_to_users_by_stage
from smart_bot_factory.supabase import SupabaseClient
from smart_bot_factory.creation import BotBuilder
from aiogram import F
from aiogram.filters import Command
from aiogram.types import Message
from aiogram.fsm.context import FSMContext

# Инициализация
event_router = EventRouter("medical_bot_events")
telegram_router = TelegramRouter("medical_bot_telegram")
supabase_client = SupabaseClient("medical-bot")

# Пользовательские команды (TelegramRouter)
@telegram_router.router.message(Command("appointment", "запись"))
async def cmd_appointment(message: Message, state: FSMContext):
    """Команда для записи на прием"""
    await message.answer(
        "📅 Для записи на прием укажите:\n"
        "- Ваше имя\n"
        "- Телефон\n"
        "- Желаемая дата и время"
    )

@telegram_router.router.message(F.text.lower().contains("цена"))
async def handle_price_question(message: Message, state: FSMContext):
    """Вопросы о ценах"""
    await message.answer("💰 Используйте /appointment для записи и уточнения стоимости")

# Обработчик записи на прием (EventRouter)
@event_router.event_handler("appointment_booking", notify=True)
async def handle_appointment(user_id: int, event_data: str):
    """Обрабатывает запись на прием к врачу"""
    # event_data: "имя: Иван, телефон: +79991234567, дата: 2025-10-15, время: 14:00"
    
    await send_message_by_human(
        user_id=user_id,
        message_text="✅ Запись подтверждена! Ждем вас."
    )
    
    return {"status": "success", "data": event_data}

# Напоминание за 2 часа до приема (EventRouter)
@event_router.schedule_task(
    "appointment_reminder",
    delay="2h",
    event_type="appointment_booking"
)
async def remind_before_appointment(user_id: int, reminder_text: str):
    """Напоминание о записи"""
    await send_message_by_human(
        user_id=user_id,
        message_text="⏰ Напоминаем о вашей записи через 2 часа!"
    )
    return {"status": "sent"}

# Ночной дайджест для всех (EventRouter)
@event_router.global_handler("daily_digest", delay="24h")
async def send_daily_digest(digest_text: str):
    """Отправляет ежедневный дайджест всем активным пользователям"""
    await send_message_to_users_by_stage(
        stage="active",
        message_text=f"📊 Дайджест дня:\n\n{digest_text}",
        bot_id="medical-bot"
    )

# Запуск
async def main():
    bot = BotBuilder("medical-bot")
    
    # Регистрируем роутеры
    bot.register_routers(event_router)  # EventRouter
    bot.register_telegram_routers(telegram_router)  # TelegramRouter
    
    await bot.build()
    await bot.start()

if __name__ == "__main__":
    asyncio.run(main())
```

## 🐛 Отладка

Включите режим отладки в `.env`:

```env
DEBUG_MODE=true
LOG_LEVEL=DEBUG
```

Это покажет:
- JSON ответы от AI
- Детальные логи обработки
- Информацию о роутерах и обработчиках

## 📋 Требования

### Системные
- Python 3.9+ (рекомендуется 3.11+)
- pip или uv для управления пакетами

### Основные зависимости
- aiogram 3.4.1+ - Telegram Bot API
- supabase 2.3.4+ - База данных
- openai 1.12.0+ - AI модель
- click 8.0.0+ - CLI интерфейс
- python-dotenv 1.0.1+ - Управление переменными окружения

Все зависимости устанавливаются автоматически при установке библиотеки.

### Внешние сервисы
- Telegram Bot Token ([@BotFather](https://t.me/botfather))
- OpenAI API Key ([platform.openai.com](https://platform.openai.com))
- Supabase Project ([supabase.com](https://supabase.com))

Подробнее см. раздел [Установка](#-установка).

## 🤝 Вклад в проект

Мы приветствуем вклад в развитие проекта! 

## 📄 Лицензия

MIT License - см. [LICENSE](LICENSE)

## 🔗 Полезные ссылки

- [Документация Supabase](https://supabase.com/docs)
- [Документация OpenAI](https://platform.openai.com/docs)
- [Документация aiogram](https://docs.aiogram.dev/)

## 💡 Поддержка

Если у вас возникли вопросы или проблемы, создайте issue в репозитории.

---

Сделано с ❤️ для создания умных ботов

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "smart-bot-factory",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "chatbot, cli, openai, supabase, telegram",
    "author": null,
    "author_email": "Kopatych <kopatych@example.com>",
    "download_url": "https://files.pythonhosted.org/packages/b2/d8/889ef234f4220edb62573a160af03600ec93e3b9ee991da871fd8fc60781/smart_bot_factory-0.2.0.tar.gz",
    "platform": null,
    "description": "# Smart Bot Factory\n\n\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0443\u043c\u043d\u044b\u0445 \u0447\u0430\u0442-\u0431\u043e\u0442\u043e\u0432 \u043d\u0430 Python \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c OpenAI, Telegram \u0438 Supabase.\n\n## \ud83d\ude80 \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n\n- **\ud83e\udd16 AI Integration** - \u041f\u043e\u043b\u043d\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 OpenAI GPT \u0434\u043b\u044f \u0443\u043c\u043d\u044b\u0445 \u0434\u0438\u0430\u043b\u043e\u0433\u043e\u0432\n- **\ud83d\udcf1 Telegram Bot API** - \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 aiogram 3.x\n- **\ud83d\udcbe Supabase Backend** - \u0425\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441\u0435\u0441\u0441\u0438\u0439 \u0438 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0438\n- **\ud83c\udfaf Router System** - \u041c\u043e\u0434\u0443\u043b\u044c\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0441\u043e\u0431\u044b\u0442\u0438\u0439\n- **\u23f0 Smart Scheduler** - \u0423\u043c\u043d\u043e\u0435 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447 \u0441 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u043e\u0439 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439\n- **\ud83c\udf0d Global Handlers** - \u041c\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438 \u0438 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\n- **\ud83e\uddea Testing Suite** - \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u043e\u0442\u043e\u0432\n- **\ud83d\udee0\ufe0f CLI Tools** - \u0423\u0434\u043e\u0431\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438\n- **\ud83d\udc65 Admin Panel** - \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 Telegram\n- **\ud83d\udcca Analytics** - \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430 \u0438 \u043e\u0442\u0447\u0435\u0442\u044b\n\n## \ud83d\udce6 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430\n\n### \u0421\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\n\n\u041f\u0435\u0440\u0435\u0434 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u043e\u0439 \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e:\n\n- **Python 3.9+** (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f 3.11+)\n- **pip** \u0438\u043b\u0438 **uv** \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u043a\u0435\u0442\u0430\u043c\u0438\n- \u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0443 \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439\n\n### \u0418\u0437 PyPI (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f)\n\n```bash\npip install smart_bot_factory\n```\n\n### \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e uv (\u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u043f\u0430\u043a\u0435\u0442\u043e\u0432)\n\n```bash\nuv add smart_bot_factory\n```\n\n### \u0418\u0437 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u043e\u0432 (\u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438)\n\n```bash\n# \u041a\u043b\u043e\u043d\u0438\u0440\u0443\u0439\u0442\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439\ngit clone https://github.com/yourusername/chat-bots.git\ncd chat-bots\n\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 uv\nuv sync\n\n# \u0418\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 pip\npip install -e .\n```\n\n### \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438\n\n```bash\npip install smart_bot_factory==0.1.8\n```\n\n### \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438\n\n\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c CLI:\n\n```bash\nsbf --help\n```\n\n\u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043a\u043e\u043c\u0430\u043d\u0434.\n\n### \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432\n\n\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0431\u043e\u0442\u0430 \u0432\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f:\n\n1. **Telegram Bot Token**\n   - \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0431\u043e\u0442\u0430 \u0447\u0435\u0440\u0435\u0437 [@BotFather](https://t.me/botfather)\n   - \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0442\u043e\u043a\u0435\u043d \u0432\u0438\u0434\u0430 `123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11`\n\n2. **OpenAI API Key**\n   - \u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0442\u0435\u0441\u044c \u043d\u0430 [platform.openai.com](https://platform.openai.com)\n   - \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 API \u043a\u043b\u044e\u0447 \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 API Keys\n   - \u041a\u043b\u044e\u0447 \u0438\u043c\u0435\u0435\u0442 \u0432\u0438\u0434 `sk-...`\n\n3. **Supabase Project**\n   - \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 [supabase.com](https://supabase.com)\n   - \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 URL \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 `anon` \u043a\u043b\u044e\u0447 \u0432 Project Settings \u2192 API\n   - \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0439\u0442\u0435 SQL \u0441\u0445\u0435\u043c\u0443 \u0438\u0437 `smart_bot_factory/database/schema.sql`\n\n## \u26a1 \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\n\n### 1. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u0431\u043e\u0442\u0430\n\n```bash\nsbf create my-bot\n```\n\n\u042d\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0441\u0442:\n- \ud83d\udcc1 `bots/my-bot/` - \u043f\u0430\u043f\u043a\u0430 \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u0431\u043e\u0442\u0430\n- \ud83d\udcc4 `my-bot.py` - \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0430\u0439\u043b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\n- \u2699\ufe0f `bots/my-bot/.env` - \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\n- \ud83d\udcdd `bots/my-bot/prompts/` - \u043f\u0440\u043e\u043c\u043f\u0442\u044b \u0434\u043b\u044f AI\n- \ud83e\uddea `bots/my-bot/tests/` - \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438\n\n### 2. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\n\n\u041e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 `bots/my-bot/.env`:\n\n```env\n# Telegram\nTELEGRAM_BOT_TOKEN=your_bot_token\n\n# Supabase\nSUPABASE_URL=https://your-project.supabase.co\nSUPABASE_KEY=your_supabase_key\n\n# OpenAI\nOPENAI_API_KEY=sk-your-openai-key\nOPENAI_MODEL=gpt-4o-mini\n\n# \u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u044b\nADMIN_TELEGRAM_IDS=123456789,987654321\n```\n\n### 3. \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0431\u043e\u0442\u0430\n\n```bash\nsbf run my-bot\n```\n\n## \ud83d\udcda \u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430\n\n### Router System\n\nSmart Bot Factory \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 **\u0434\u0432\u0430 \u0442\u0438\u043f\u0430 \u0440\u043e\u0443\u0442\u0435\u0440\u043e\u0432** \u0434\u043b\u044f \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432:\n\n1. **EventRouter** - \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 (\u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u0437\u0430\u0434\u0430\u0447\u0438, \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438)\n2. **TelegramRouter** - \u0434\u043b\u044f Telegram \u043a\u043e\u043c\u0430\u043d\u0434, \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438 callback'\u043e\u0432\n\n```python\nfrom smart_bot_factory.router import EventRouter, TelegramRouter\nfrom smart_bot_factory.message import send_message_by_human\nfrom smart_bot_factory.creation import BotBuilder\nfrom aiogram import F\nfrom aiogram.filters import Command\nfrom aiogram.types import Message\nfrom aiogram.fsm.context import FSMContext\n\n# EventRouter - \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438\nevent_router = EventRouter(\"my_bot_events\")\n\n@event_router.event_handler(\"appointment_booking\", notify=True)\nasync def handle_booking(user_id: int, event_data: str):\n    \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430 \u043f\u0440\u0438\u0435\u043c\"\"\"\n    await send_message_by_human(\n        user_id=user_id,\n        message_text=f\"\u2705 \u0417\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0430! {event_data}\"\n    )\n    return {\"status\": \"success\"}\n\n# TelegramRouter - \u0434\u043b\u044f Telegram \u043a\u043e\u043c\u0430\u043d\u0434 \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439\ntelegram_router = TelegramRouter(\"my_bot_telegram\")\n\n@telegram_router.router.message(Command(\"price\", \"\u0446\u0435\u043d\u0430\"))\nasync def handle_price(message: Message, state: FSMContext):\n    \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a\u043e\u043c\u0430\u043d\u0434\u044b /price\"\"\"\n    await message.answer(\"\ud83d\udcb0 \u041d\u0430\u0448\u0438 \u0446\u0435\u043d\u044b...\")\n\n@telegram_router.router.message(F.text.contains(\"\u043f\u043e\u043c\u043e\u0449\u044c\"))\nasync def handle_help(message: Message, state: FSMContext):\n    \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u043e\u043c\u043e\u0449\u0438\"\"\"\n    await message.answer(\"\ud83d\udcd6 \u0427\u0435\u043c \u043c\u043e\u0433\u0443 \u043f\u043e\u043c\u043e\u0447\u044c?\")\n\n# \u0417\u0430\u043f\u0443\u0441\u043a \u0431\u043e\u0442\u0430\nasync def main():\n    bot = BotBuilder(\"my-bot\")\n    \n    # \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440\u044b (\u043c\u043e\u0436\u043d\u043e \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0440\u0430\u0437\u0443)\n    bot.register_routers(event_router)  # EventRouter\n    bot.register_telegram_routers(telegram_router)  # TelegramRouter\n    \n    await bot.build()\n    await bot.start()\n```\n\n### TelegramRouter - \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 Telegram\n\n**TelegramRouter** \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0432 Telegram \u0431\u043e\u0442\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 aiogram API:\n\n#### \u041a\u043e\u043c\u0430\u043d\u0434\u044b\n\n```python\nfrom smart_bot_factory.router import TelegramRouter\nfrom aiogram.filters import Command\nfrom aiogram.types import Message\nfrom aiogram.fsm.context import FSMContext\n\ntelegram_router = TelegramRouter(\"my_commands\")\n\n@telegram_router.router.message(Command(\"start\", \"\u0441\u0442\u0430\u0440\u0442\"))\nasync def cmd_start(message: Message, state: FSMContext):\n    \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a\u043e\u043c\u0430\u043d\u0434\u044b /start\"\"\"\n    await message.answer(\"\ud83d\udc4b \u041f\u0440\u0438\u0432\u0435\u0442!\")\n\n@telegram_router.router.message(Command(\"price\"))\nasync def cmd_price(message: Message, state: FSMContext):\n    \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a\u043e\u043c\u0430\u043d\u0434\u044b /price\"\"\"\n    await message.answer(\"\ud83d\udcb0 \u0426\u0435\u043d\u044b:\\n1. \u0411\u0430\u0437\u043e\u0432\u044b\u0439 - 1000\u20bd\\n2. \u041f\u0440\u0435\u043c\u0438\u0443\u043c - 5000\u20bd\")\n```\n\n#### \u0424\u0438\u043b\u044c\u0442\u0440\u044b \u043f\u043e \u0442\u0435\u043a\u0441\u0442\u0443\n\n```python\nfrom aiogram import F\n\n@telegram_router.router.message(F.text.lower().contains(\"\u0446\u0435\u043d\u0430\"))\nasync def handle_price_question(message: Message, state: FSMContext):\n    \"\"\"\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u043e \u0446\u0435\u043d\u0435\"\"\"\n    await message.answer(\"\ud83d\udca1 \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 /price \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0446\u0435\u043d\")\n\n@telegram_router.router.message(F.text.startswith(\"!\"))\nasync def handle_commands(message: Message, state: FSMContext):\n    \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u043a\u043e\u043c\u0430\u043d\u0434 \u0441 !\"\"\"\n    command = message.text[1:]\n    await message.answer(f\"\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u0443: {command}\")\n```\n\n#### Callback Query\n\n```python\nfrom aiogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton\n\n@telegram_router.router.callback_query(F.data.startswith(\"buy_\"))\nasync def handle_buy(callback: CallbackQuery, state: FSMContext):\n    \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u043e\u043a\u0443\u043f\u043a\u0438\"\"\"\n    product_id = callback.data.split(\"_\")[1]\n    await callback.answer(\"\u041e\u0444\u043e\u0440\u043c\u043b\u044f\u0435\u043c...\")\n    await callback.message.answer(f\"\u2705 \u0422\u043e\u0432\u0430\u0440 {product_id} \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0443\")\n\n@telegram_router.router.message(Command(\"catalog\"))\nasync def show_catalog(message: Message, state: FSMContext):\n    \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u0441 \u043a\u043d\u043e\u043f\u043a\u0430\u043c\u0438\"\"\"\n    keyboard = InlineKeyboardMarkup(inline_keyboard=[\n        [InlineKeyboardButton(text=\"\u041a\u0443\u043f\u0438\u0442\u044c \u0442\u043e\u0432\u0430\u0440 1\", callback_data=\"buy_1\")],\n        [InlineKeyboardButton(text=\"\u041a\u0443\u043f\u0438\u0442\u044c \u0442\u043e\u0432\u0430\u0440 2\", callback_data=\"buy_2\")]\n    ])\n    await message.answer(\"\ud83d\udecd\ufe0f \u041a\u0430\u0442\u0430\u043b\u043e\u0433 \u0442\u043e\u0432\u0430\u0440\u043e\u0432:\", reply_markup=keyboard)\n```\n\n#### \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0440\u043e\u0443\u0442\u0435\u0440\u043e\u0432\n\n```python\n# \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e TelegramRouter \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439\ncommands_router = TelegramRouter(\"commands\")\nadmin_router = TelegramRouter(\"admin_commands\")\ncallbacks_router = TelegramRouter(\"callbacks\")\n\n# \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u0441\u0440\u0430\u0437\u0443\nbot_builder.register_telegram_routers(\n    commands_router,\n    admin_router,\n    callbacks_router\n)\n```\n\n**\u0412\u0430\u0436\u043d\u043e:** TelegramRouter - \u044d\u0442\u043e \u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 aiogram Router, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0430\u044f \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043f\u043e\u043b\u043d\u043e\u043c\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0443 aiogram \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e `.router`.\n\n### EventRouter - \u0422\u0438\u043f\u044b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0441\u043e\u0431\u044b\u0442\u0438\u0439\n\n#### 1. Event Handlers - \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439\n\n\u041d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043e\u0442 AI:\n\n```python\n@router.event_handler(\"phone_collected\", notify=True, once_only=True)\nasync def handle_phone(user_id: int, event_data: str):\n    \"\"\"\u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0433\u0434\u0430 AI \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 \u043d\u043e\u043c\u0435\u0440 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430\"\"\"\n    # event_data \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442 AI\n    phone = parse_phone(event_data)\n    \n    # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432 CRM\n    await save_to_crm(user_id, phone)\n    \n    return {\"status\": \"saved\", \"phone\": phone}\n```\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `notify` - \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0442\u044c \u0430\u0434\u043c\u0438\u043d\u043e\u0432 (default: False)\n- `once_only` - \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 (default: True)\n\n#### 2. Scheduled Tasks - \u0417\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n\n\u0417\u0430\u0434\u0430\u0447\u0438 \u0441 \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:\n\n```python\n@router.schedule_task(\"send_reminder\", delay=\"2h\", smart_check=True)\nasync def send_reminder(user_id: int, reminder_text: str):\n    \"\"\"\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442 \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 2 \u0447\u0430\u0441\u0430\"\"\"\n    await send_message_by_human(\n        user_id=user_id,\n        message_text=f\"\ud83d\udd14 {reminder_text}\"\n    )\n    return {\"status\": \"sent\"}\n```\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `delay` - \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 (\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e): `\"1h\"`, `\"30m\"`, `\"2h 15m\"`, `3600`\n- `smart_check` - \u0443\u043c\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438 (default: True)\n- `once_only` - \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 (default: True)\n- `event_type` - \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0439\n\n**Smart Check:**\n- \u041e\u0442\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0443 \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0448\u0435\u043b \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u044d\u0442\u0430\u043f\n- \u041f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u044b\u043b \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u043d\u0435\u0434\u0430\u0432\u043d\u043e\n- \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 session_id \u0434\u043b\u044f \u0442\u043e\u0447\u043d\u043e\u0433\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f\n\n#### 3. Global Handlers - \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438\n\n\u041c\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439:\n\n```python\n@router.global_handler(\"mass_notification\", delay=\"1h\", notify=True)\nasync def send_announcement(announcement_text: str):\n    \"\"\"\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442 \u0430\u043d\u043e\u043d\u0441 \u0432\u0441\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0447\u0435\u0440\u0435\u0437 1 \u0447\u0430\u0441\"\"\"\n    from smart_bot_factory.message import send_message_to_users_by_stage\n    \n    await send_message_to_users_by_stage(\n        stage=\"introduction\",\n        message_text=announcement_text,\n        bot_id=\"my-bot\"\n    )\n    \n    return {\"status\": \"completed\"}\n```\n\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:**\n- `delay` - \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 (\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)\n- `notify` - \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0442\u044c \u0430\u0434\u043c\u0438\u043d\u043e\u0432 (default: False)\n- `once_only` - \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437 (default: True)\n\n### Event-Based Reminders\n\n\u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u044f \u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445 \u0437\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f:\n\n```python\n# \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044f\n@router.event_handler(\"appointment_booking\")\nasync def handle_booking(user_id: int, event_data: str):\n    \"\"\"\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u044c: \u0438\u043c\u044f, \u0442\u0435\u043b\u0435\u0444\u043e\u043d, \u0434\u0430\u0442\u0430, \u0432\u0440\u0435\u043c\u044f\"\"\"\n    return {\"status\": \"saved\", \"data\": event_data}\n\n# \u0417\u0430\u0442\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435\n@router.schedule_task(\n    \"appointment_reminder\",\n    delay=\"2h\",\n    event_type=\"appointment_booking\"  # \u041f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044e\n)\nasync def remind_about_appointment(user_id: int, reminder_text: str):\n    \"\"\"\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442 \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u0437\u0430 2 \u0447\u0430\u0441\u0430 \u0434\u043e \u0437\u0430\u043f\u0438\u0441\u0438\"\"\"\n    await send_message_by_human(\n        user_id=user_id,\n        message_text=f\"\u23f0 \u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u043e \u0437\u0430\u043f\u0438\u0441\u0438 \u0447\u0435\u0440\u0435\u0437 2 \u0447\u0430\u0441\u0430!\"\n    )\n    return {\"status\": \"sent\"}\n```\n\n\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438:\n1. \u0418\u0437\u0432\u043b\u0435\u0447\u0435\u0442 \u0434\u0430\u0442\u0443/\u0432\u0440\u0435\u043c\u044f \u0438\u0437 \u0441\u043e\u0431\u044b\u0442\u0438\u044f `appointment_booking`\n2. \u0412\u044b\u0447\u0438\u0441\u043b\u0438\u0442 \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u044f (\u0437\u0430 2 \u0447\u0430\u0441\u0430 \u0434\u043e \u0437\u0430\u043f\u0438\u0441\u0438)\n3. \u0417\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f\n\n## \ud83d\udee0\ufe0f CLI \u041a\u043e\u043c\u0430\u043d\u0434\u044b\n\n```bash\n# \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0431\u043e\u0442\u0430\u043c\u0438\nsbf create <bot-id>              # \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u043e\u0433\u043e \u0431\u043e\u0442\u0430\nsbf create <bot-id> <template>   # \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u0437 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\nsbf copy <source> <new-id>       # \u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0431\u043e\u0442\u0430\nsbf list                         # \u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0441\u0435\u0445 \u0431\u043e\u0442\u043e\u0432\nsbf rm <bot-id>                  # \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0431\u043e\u0442\u0430\n\n# \u0417\u0430\u043f\u0443\u0441\u043a\nsbf run <bot-id>                 # \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0431\u043e\u0442\u0430\n\n# \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\nsbf test <bot-id>                # \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u044b\nsbf test <bot-id> --file quick_scenarios.yaml\nsbf test <bot-id> -v             # \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434\nsbf test <bot-id> --max-concurrent 10\n\n# \u041f\u0440\u043e\u043c\u043f\u0442\u044b\nsbf prompts <bot-id>             # \u0421\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u043e\u043c\u043f\u0442\u043e\u0432\nsbf prompts <bot-id> --edit welcome_message\nsbf prompts <bot-id> --add new_prompt\n\n# \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\nsbf config <bot-id>              # \u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c .env\nsbf path                         # \u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u0443\u0442\u044c \u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0443\nsbf link                         # \u0413\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 UTM-\u0441\u0441\u044b\u043b\u043e\u043a\n```\n\n## \ud83d\udcdd \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0440\u043e\u043c\u043f\u0442\u043e\u0432\n\n\u041f\u0440\u043e\u043c\u043f\u0442\u044b \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 `bots/<bot-id>/prompts/`:\n\n- `welcome_message.txt` - \u041f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\n- `help_message.txt` - \u0421\u043f\u0440\u0430\u0432\u043a\u0430 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\n- `1sales_context.txt` - \u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u043f\u0440\u043e\u0434\u0430\u0436\n- `2product_info.txt` - \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0435\n- `3objection_handling.txt` - \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0432\u043e\u0437\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438\n- `final_instructions.txt` - \u0424\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0434\u043b\u044f AI\n\nAI \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u043c\u043f\u0442.\n\n## \ud83e\uddea \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\n\n\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0432 YAML:\n\n```yaml\n# bots/my-bot/tests/scenarios.yaml\nscenarios:\n  - name: \"\u0417\u0430\u043f\u0438\u0441\u044c \u043d\u0430 \u043f\u0440\u0438\u0435\u043c\"\n    steps:\n      - user: \"\u041f\u0440\u0438\u0432\u0435\u0442!\"\n        expect_stage: \"introduction\"\n      \n      - user: \"\u0425\u043e\u0447\u0443 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043f\u0440\u0438\u0435\u043c\"\n        expect_stage: \"qualification\"\n        expect_events:\n          - type: \"appointment_request\"\n      \n      - user: \"\u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0418\u0432\u0430\u043d, +79991234567, \u0437\u0430\u0432\u0442\u0440\u0430 \u0432 15:00\"\n        expect_events:\n          - type: \"appointment_booking\"\n          - type: \"appointment_reminder\"  # \u0414\u043e\u043b\u0436\u043d\u043e \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f\n        expect_quality: \">= 8\"\n```\n\n\u0417\u0430\u043f\u0443\u0441\u043a:\n```bash\nsbf test my-bot --file scenarios.yaml -v\n```\n\n## \ud83d\udcac \u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439\n\n### \u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e\n\n```python\nfrom smart_bot_factory.message import send_message_by_human\n\nawait send_message_by_human(\n    user_id=123456789,\n    message_text=\"\u041f\u0440\u0438\u0432\u0435\u0442! \u042d\u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u044b\",\n    session_id=\"optional-session-id\"\n)\n```\n\n### \u041c\u0430\u0441\u0441\u043e\u0432\u0430\u044f \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0430 \u043f\u043e \u044d\u0442\u0430\u043f\u0443\n\n```python\nfrom smart_bot_factory.message import send_message_to_users_by_stage\n\nawait send_message_to_users_by_stage(\n    stage=\"introduction\",\n    message_text=\"\ud83d\udce2 \u0412\u0430\u0436\u043d\u043e\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u0435!\",\n    bot_id=\"my-bot\"\n)\n```\n\n## \ud83d\uddc4\ufe0f \u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445\n\nSmart Bot Factory \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 Supabase \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043c\u0438:\n\n- `sales_users` - \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438\n- `sales_chat_sessions` - \u0421\u0435\u0441\u0441\u0438\u0438 \u0434\u0438\u0430\u043b\u043e\u0433\u043e\u0432\n- `sales_chat_messages` - \u0418\u0441\u0442\u043e\u0440\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439\n- `scheduled_events` - \u0417\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438 \u0437\u0430\u0434\u0430\u0447\u0438\n- `admin_sessions` - \u0421\u0435\u0441\u0441\u0438\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u0432\n\nSQL \u0441\u0445\u0435\u043c\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0432 `smart_bot_factory/database/`.\n\n## \ud83d\udc65 \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\n\n\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 ID \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u0432 `.env`:\n\n```env\nADMIN_TELEGRAM_IDS=123456789,987654321\nADMIN_SESSION_TIMEOUT_MINUTES=30\n```\n\n\u0410\u0434\u043c\u0438\u043d\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442:\n- \ud83d\udcca \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443 \u0438 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0443\n- \ud83d\udd14 \u0423\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u0432\u0430\u0436\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445 (\u0435\u0441\u043b\u0438 `notify=True`)\n- \ud83d\udee0\ufe0f \u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u043c\n\n## \ud83d\udd27 \u041f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\n\n### \u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0440\u043e\u0443\u0442\u0435\u0440\u044b\n\n#### \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 EventRouter\n\n```python\n# handlers/main.py\nmain_router = EventRouter(\"main\")\n\n# handlers/admin.py\nadmin_router = EventRouter(\"admin\")\n\n# handlers/payments.py\npayments_router = EventRouter(\"payments\")\n\n# app.py\nbot = BotBuilder(\"my-bot\")\n\n# \u041c\u043e\u0436\u043d\u043e \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443\nbot.register_router(main_router)\nbot.register_router(admin_router)\n\n# \u0418\u043b\u0438 \u0432\u0441\u0435 \u0441\u0440\u0430\u0437\u0443\nbot.register_routers(main_router, admin_router, payments_router)\n```\n\n#### \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 TelegramRouter\n\n```python\n# telegram/commands.py\ncommands_router = TelegramRouter(\"commands\")\n\n@commands_router.router.message(Command(\"start\"))\nasync def start(message: Message, state: FSMContext):\n    await message.answer(\"\u041f\u0440\u0438\u0432\u0435\u0442!\")\n\n# telegram/callbacks.py\ncallbacks_router = TelegramRouter(\"callbacks\")\n\n@callbacks_router.router.callback_query(F.data.startswith(\"action_\"))\nasync def handle_action(callback: CallbackQuery, state: FSMContext):\n    await callback.answer(\"\u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e!\")\n\n# app.py\nbot = BotBuilder(\"my-bot\")\n\n# \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e Telegram \u0440\u043e\u0443\u0442\u0435\u0440\u043e\u0432 \u0441\u0440\u0430\u0437\u0443\nbot.register_telegram_routers(commands_router, callbacks_router)\n```\n\n#### \u041a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 EventRouter \u0438 TelegramRouter\n\n```python\n# \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440\u044b \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0446\u0435\u043b\u0435\u0439\nevent_router = EventRouter(\"events\")\ntelegram_router = TelegramRouter(\"telegram_handlers\")\n\n# EventRouter - \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430\n@event_router.event_handler(\"payment_success\")\nasync def handle_payment(user_id: int, event_data: str):\n    await send_message_by_human(user_id, \"\u2705 \u041e\u043f\u043b\u0430\u0442\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0430!\")\n    return {\"status\": \"success\"}\n\n# TelegramRouter - \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b\n@telegram_router.router.message(Command(\"balance\"))\nasync def check_balance(message: Message, state: FSMContext):\n    await message.answer(\"\ud83d\udcb0 \u0412\u0430\u0448 \u0431\u0430\u043b\u0430\u043d\u0441: 1000\u20bd\")\n\n# \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u0430 \u0442\u0438\u043f\u0430 \u0440\u043e\u0443\u0442\u0435\u0440\u043e\u0432\nbot_builder.register_routers(event_router)\nbot_builder.register_telegram_routers(telegram_router)\n```\n\n### \u0412\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0440\u043e\u0443\u0442\u0435\u0440\u044b (EventRouter)\n\n```python\nmain_router = EventRouter(\"main\")\npayments_router = EventRouter(\"payments\")\n\n# \u0412\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440 \u043f\u043b\u0430\u0442\u0435\u0436\u0435\u0439 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439\nmain_router.include_router(payments_router)\n\nbot.register_router(main_router)\n```\n\n### \u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438\n\n```python\nfrom smart_bot_factory.supabase import SupabaseClient\n\n# \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u0431\u043e\u0442\u0430\nsupabase = SupabaseClient(\"my-bot\")\n\n# \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e\nusers = supabase.client.table('sales_users').select('*').eq('bot_id', 'my-bot').execute()\n```\n\n## \ud83d\udcca \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\n\n```\nmy-project/\n\u251c\u2500\u2500 bots/                      # \u041f\u0430\u043f\u043a\u0430 \u0441 \u0431\u043e\u0442\u0430\u043c\u0438\n\u2502   \u251c\u2500\u2500 my-bot/\n\u2502   \u2502   \u251c\u2500\u2500 .env              # \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\n\u2502   \u2502   \u251c\u2500\u2500 prompts/          # AI \u043f\u0440\u043e\u043c\u043f\u0442\u044b\n\u2502   \u2502   \u251c\u2500\u2500 tests/            # \u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438\n\u2502   \u2502   \u251c\u2500\u2500 files/            # \u0424\u0430\u0439\u043b\u044b \u0431\u043e\u0442\u0430\n\u2502   \u2502   \u251c\u2500\u2500 welcome_files/    # \u041f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b\n\u2502   \u2502   \u2514\u2500\u2500 reports/          # \u041e\u0442\u0447\u0435\u0442\u044b \u0442\u0435\u0441\u0442\u043e\u0432\n\u2502   \u2514\u2500\u2500 another-bot/\n\u2502       \u2514\u2500\u2500 ...\n\u251c\u2500\u2500 my-bot.py                 # \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0430\u0439\u043b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\n\u251c\u2500\u2500 another-bot.py\n\u2514\u2500\u2500 .env                      # \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f (\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e)\n```\n\n## \ud83d\udd04 \u041f\u0440\u0438\u043c\u0435\u0440\u044b\n\n### \u041f\u043e\u043b\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0431\u043e\u0442\u0430\n\n```python\nimport asyncio\n\nfrom smart_bot_factory.router import EventRouter, TelegramRouter\nfrom smart_bot_factory.message import send_message_by_human, send_message_to_users_by_stage\nfrom smart_bot_factory.supabase import SupabaseClient\nfrom smart_bot_factory.creation import BotBuilder\nfrom aiogram import F\nfrom aiogram.filters import Command\nfrom aiogram.types import Message\nfrom aiogram.fsm.context import FSMContext\n\n# \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\nevent_router = EventRouter(\"medical_bot_events\")\ntelegram_router = TelegramRouter(\"medical_bot_telegram\")\nsupabase_client = SupabaseClient(\"medical-bot\")\n\n# \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b (TelegramRouter)\n@telegram_router.router.message(Command(\"appointment\", \"\u0437\u0430\u043f\u0438\u0441\u044c\"))\nasync def cmd_appointment(message: Message, state: FSMContext):\n    \"\"\"\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430 \u043f\u0440\u0438\u0435\u043c\"\"\"\n    await message.answer(\n        \"\ud83d\udcc5 \u0414\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430 \u043f\u0440\u0438\u0435\u043c \u0443\u043a\u0430\u0436\u0438\u0442\u0435:\\n\"\n        \"- \u0412\u0430\u0448\u0435 \u0438\u043c\u044f\\n\"\n        \"- \u0422\u0435\u043b\u0435\u0444\u043e\u043d\\n\"\n        \"- \u0416\u0435\u043b\u0430\u0435\u043c\u0430\u044f \u0434\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f\"\n    )\n\n@telegram_router.router.message(F.text.lower().contains(\"\u0446\u0435\u043d\u0430\"))\nasync def handle_price_question(message: Message, state: FSMContext):\n    \"\"\"\u0412\u043e\u043f\u0440\u043e\u0441\u044b \u043e \u0446\u0435\u043d\u0430\u0445\"\"\"\n    await message.answer(\"\ud83d\udcb0 \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 /appointment \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0438 \u0443\u0442\u043e\u0447\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u0438\")\n\n# \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430 \u043f\u0440\u0438\u0435\u043c (EventRouter)\n@event_router.event_handler(\"appointment_booking\", notify=True)\nasync def handle_appointment(user_id: int, event_data: str):\n    \"\"\"\u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u044c \u043d\u0430 \u043f\u0440\u0438\u0435\u043c \u043a \u0432\u0440\u0430\u0447\u0443\"\"\"\n    # event_data: \"\u0438\u043c\u044f: \u0418\u0432\u0430\u043d, \u0442\u0435\u043b\u0435\u0444\u043e\u043d: +79991234567, \u0434\u0430\u0442\u0430: 2025-10-15, \u0432\u0440\u0435\u043c\u044f: 14:00\"\n    \n    await send_message_by_human(\n        user_id=user_id,\n        message_text=\"\u2705 \u0417\u0430\u043f\u0438\u0441\u044c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0430! \u0416\u0434\u0435\u043c \u0432\u0430\u0441.\"\n    )\n    \n    return {\"status\": \"success\", \"data\": event_data}\n\n# \u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u0437\u0430 2 \u0447\u0430\u0441\u0430 \u0434\u043e \u043f\u0440\u0438\u0435\u043c\u0430 (EventRouter)\n@event_router.schedule_task(\n    \"appointment_reminder\",\n    delay=\"2h\",\n    event_type=\"appointment_booking\"\n)\nasync def remind_before_appointment(user_id: int, reminder_text: str):\n    \"\"\"\u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u0435 \u043e \u0437\u0430\u043f\u0438\u0441\u0438\"\"\"\n    await send_message_by_human(\n        user_id=user_id,\n        message_text=\"\u23f0 \u041d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u043c \u043e \u0432\u0430\u0448\u0435\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0447\u0435\u0440\u0435\u0437 2 \u0447\u0430\u0441\u0430!\"\n    )\n    return {\"status\": \"sent\"}\n\n# \u041d\u043e\u0447\u043d\u043e\u0439 \u0434\u0430\u0439\u0434\u0436\u0435\u0441\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 (EventRouter)\n@event_router.global_handler(\"daily_digest\", delay=\"24h\")\nasync def send_daily_digest(digest_text: str):\n    \"\"\"\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0436\u0435\u0434\u043d\u0435\u0432\u043d\u044b\u0439 \u0434\u0430\u0439\u0434\u0436\u0435\u0441\u0442 \u0432\u0441\u0435\u043c \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\"\"\"\n    await send_message_to_users_by_stage(\n        stage=\"active\",\n        message_text=f\"\ud83d\udcca \u0414\u0430\u0439\u0434\u0436\u0435\u0441\u0442 \u0434\u043d\u044f:\\n\\n{digest_text}\",\n        bot_id=\"medical-bot\"\n    )\n\n# \u0417\u0430\u043f\u0443\u0441\u043a\nasync def main():\n    bot = BotBuilder(\"medical-bot\")\n    \n    # \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440\u044b\n    bot.register_routers(event_router)  # EventRouter\n    bot.register_telegram_routers(telegram_router)  # TelegramRouter\n    \n    await bot.build()\n    await bot.start()\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n## \ud83d\udc1b \u041e\u0442\u043b\u0430\u0434\u043a\u0430\n\n\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0440\u0435\u0436\u0438\u043c \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u0432 `.env`:\n\n```env\nDEBUG_MODE=true\nLOG_LEVEL=DEBUG\n```\n\n\u042d\u0442\u043e \u043f\u043e\u043a\u0430\u0436\u0435\u0442:\n- JSON \u043e\u0442\u0432\u0435\u0442\u044b \u043e\u0442 AI\n- \u0414\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043b\u043e\u0433\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438\n- \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0440\u043e\u0443\u0442\u0435\u0440\u0430\u0445 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u0445\n\n## \ud83d\udccb \u0422\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\n\n### \u0421\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435\n- Python 3.9+ (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f 3.11+)\n- pip \u0438\u043b\u0438 uv \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u043a\u0435\u0442\u0430\u043c\u0438\n\n### \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438\n- aiogram 3.4.1+ - Telegram Bot API\n- supabase 2.3.4+ - \u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445\n- openai 1.12.0+ - AI \u043c\u043e\u0434\u0435\u043b\u044c\n- click 8.0.0+ - CLI \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\n- python-dotenv 1.0.1+ - \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\n\n\u0412\u0441\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.\n\n### \u0412\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b\n- Telegram Bot Token ([@BotFather](https://t.me/botfather))\n- OpenAI API Key ([platform.openai.com](https://platform.openai.com))\n- Supabase Project ([supabase.com](https://supabase.com))\n\n\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441\u043c. \u0440\u0430\u0437\u0434\u0435\u043b [\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430](#-\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430).\n\n## \ud83e\udd1d \u0412\u043a\u043b\u0430\u0434 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\n\n\u041c\u044b \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u043c \u0432\u043a\u043b\u0430\u0434 \u0432 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430! \n\n## \ud83d\udcc4 \u041b\u0438\u0446\u0435\u043d\u0437\u0438\u044f\n\nMIT License - \u0441\u043c. [LICENSE](LICENSE)\n\n## \ud83d\udd17 \u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438\n\n- [\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f Supabase](https://supabase.com/docs)\n- [\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f OpenAI](https://platform.openai.com/docs)\n- [\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f aiogram](https://docs.aiogram.dev/)\n\n## \ud83d\udca1 \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430\n\n\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0438 \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u0438\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 issue \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438.\n\n---\n\n\u0421\u0434\u0435\u043b\u0430\u043d\u043e \u0441 \u2764\ufe0f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0443\u043c\u043d\u044b\u0445 \u0431\u043e\u0442\u043e\u0432\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0443\u043c\u043d\u044b\u0445 \u0447\u0430\u0442-\u0431\u043e\u0442\u043e\u0432",
    "version": "0.2.0",
    "project_urls": null,
    "split_keywords": [
        "chatbot",
        " cli",
        " openai",
        " supabase",
        " telegram"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "5d639ed087c8d67f985857225d6de3bff0760248f781d6fd362e18a0a4c263d8",
                "md5": "575ab29738e45f457355cd9534f21376",
                "sha256": "4528d05444ff34641b054a4f8759746028cb2e55779067ea06c56576fa34ffff"
            },
            "downloads": -1,
            "filename": "smart_bot_factory-0.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "575ab29738e45f457355cd9534f21376",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 3595137,
            "upload_time": "2025-10-07T14:43:48",
            "upload_time_iso_8601": "2025-10-07T14:43:48.638172Z",
            "url": "https://files.pythonhosted.org/packages/5d/63/9ed087c8d67f985857225d6de3bff0760248f781d6fd362e18a0a4c263d8/smart_bot_factory-0.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b2d8889ef234f4220edb62573a160af03600ec93e3b9ee991da871fd8fc60781",
                "md5": "4f8a865c3c901cd189b7b7336527ec5b",
                "sha256": "f65551fa08e301e0342015c4010ffbe38d1883d27eb88c4b9b2dd23675c918d6"
            },
            "downloads": -1,
            "filename": "smart_bot_factory-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "4f8a865c3c901cd189b7b7336527ec5b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 15003708,
            "upload_time": "2025-10-07T14:45:34",
            "upload_time_iso_8601": "2025-10-07T14:45:34.416748Z",
            "url": "https://files.pythonhosted.org/packages/b2/d8/889ef234f4220edb62573a160af03600ec93e3b9ee991da871fd8fc60781/smart_bot_factory-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-07 14:45:34",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "smart-bot-factory"
}
        
Elapsed time: 0.65811s