# 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"
}