Managed Bots API 9.6 — 3 апреля 2026 AI-аватарки + фото-визитки 6 шаблонов профессий

Создай фабрику
ботов-визиток за 15 минут

Telegram Bot API 9.6 + OpenRouter + один Python-файл =
неограниченное количество персональных ботов-визиток
с AI-аватарками, CRM, QR-кодами и автоответчиком

Бот-визитка заменяет лендинг

Что мы строим

🪪

Бот-визитка

Один менеджер-бот, через которого любой пользователь за 2 минуты создаёт персонального бота-визитку. Бот заменяет лендинг.

Создание: 2 минуты
🚀

Что внутри

AI-аватарка, фото-визитка, кнопки «Услуги», «Цены», «Записаться», мини-CRM с заявками, QR-код, статистика, автоперевод, админка для редактирования.

Шаблонов: 6 профессий
⚙️

Под капотом

Telegram Bot API 9.6 (Managed Bots)
+ OpenRouter (Gemini 2.5 Flash)
+ SQLite
+ Один файл, один VPS

Документация API 9.6 →

Что понадобится

🖥️

VPS-сервер

Любой VPS с Ubuntu 22.04+. Подойдёт самый дешёвый тариф.
Рекомендуем: Beget VPS от 210₽/мес — быстрый, русскоязычная поддержка, оплата из РФ.

🤖

Telegram-аккаунт и @BotFather

Для создания бота-менеджера и включения функции Managed Bots.

🧠

OpenRouter API

Единый API для доступа к 300+ моделям (Gemini, Claude, GPT и др.).
Модели: Gemini 2.5 Flash (чат) + Gemini 3.1 Flash (картинки).
Стоимость: ~$0.04 за аватарку, ~$0.001 за сообщение. $5 хватит на сотни ботов.

💰

Бюджет на старт

VPS: от 210₽/месяц (Beget, самый дешёвый тариф)
OpenRouter: $5 (~500₽) — хватит на создание ~100 ботов с аватарками и фото-визитками + тысячи сообщений в чате.
Итого: ~700₽ на первый месяц.

Пошаговый урок

Каждая команда копируется одним кликом. Вставляй в терминал и работает с первого раза.

1

Создай менеджер-бота

  1. Откройте @BotFather в Telegram
  2. Отправьте /newbot
  3. Дайте имя, например: Vizitka Factory
  4. Дайте username, например: vizitka_factory_bot
  5. Скопируйте токен — он выглядит как 7123456789:AAF...
  6. Отправьте /mybots → выберите бота → Bot Settings → Managed Bots → Enable
  7. Убедитесь что видите Managed Bots: On
🔴
Важно! Частая ошибка:
Без включения Managed Bots ничего не заработает! Это самый частый источник ошибки.

Запишите токен — он понадобится в шаге 6.

2

Получи ключ OpenRouter

  1. Зарегистрируйтесь: openrouter.ai
  2. Создайте ключ: Create Key → имя vizitka
  3. Скопируйте ключ (формат sk-or-v1-...)
  4. Пополните баланс на $5: Настройки кредитов
🔵
Инфо:
$5 хватит на ~100 ботов с аватарками и фото-визитками + тысячи сообщений. Для демо более чем достаточно.

Запишите ключ — он понадобится в шаге 6.

3

Подготовь VPS

Подключитесь через PowerShell:

Подключение к VPS
ssh root@твой_IP

Выполните по одной команде:

Обновление системы
apt update && apt upgrade -y
Установка Python
apt install python3.12-venv python3-pip -y
🟡
Учтённая ошибка:
На свежих Ubuntu 24.04 пакет python3-venv не установлен по умолчанию. Без него команда python3 -m venv упадёт с ошибкой ensurepip is not available.
4

Создай папку и окружение

Создание проекта
mkdir ~/vizitka-factory && cd ~/vizitka-factory
Виртуальное окружение
python3 -m venv venv
Активация
source venv/bin/activate

Должно появиться (venv) в начале строки. Далее:

Установка библиотек
pip install aiohttp qrcode Pillow

Дождитесь Successfully installed...

5

Создай bot.py

🟡
Учтённая ошибка:
Мы не используем nano — файл создаётся одной командой cat. Это исключает ошибки при редактировании и проблемы с сохранением. Просто скопируйте всё целиком от cat до ENDOFFILE и вставьте в терминал.
bot.py
cat > bot.py << 'ENDOFFILE'
#!/usr/bin/env python3
"""
🪪 Bot-Vizitka Factory v3
Telegram Bot API 9.6 (Managed Bots) + OpenRouter (Nano Banana 2) + SQLite
Клуб Вайбкодеров | 2026
"""

import asyncio, aiohttp, json, logging, time, io, os, sqlite3, qrcode, base64
from datetime import datetime

# ═══════════════════════════════════════════
#  КОНФИГ
# ═══════════════════════════════════════════
MANAGER_BOT_TOKEN = "ВСТАВЬ_ТОКЕН_МЕНЕДЖЕРА"
OPENROUTER_API_KEY = "ВСТАВЬ_КЛЮЧ_OPENROUTER"
CHAT_MODEL = "google/gemini-2.5-flash"
IMAGE_MODEL = "google/gemini-3.1-flash-image-preview"
TG = "https://api.telegram.org/bot{token}/{method}"
OR_API = "https://openrouter.ai/api/v1/chat/completions"

log = logging.getLogger("vizitka")
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")

# ═══════════════════════════════════════════
#  БАЗА ДАННЫХ
# ═══════════════════════════════════════════
DB = "factory.db"

def init_db():
    conn = sqlite3.connect(DB)
    c = conn.cursor()
    c.execute("""CREATE TABLE IF NOT EXISTS bots (
        bot_id INTEGER PRIMARY KEY, bot_username TEXT, bot_token TEXT,
        owner_id INTEGER, template TEXT, data TEXT, created_at TEXT)""")
    c.execute("""CREATE TABLE IF NOT EXISTS leads (
        id INTEGER PRIMARY KEY AUTOINCREMENT, bot_id INTEGER, client_id INTEGER,
        client_name TEXT, message TEXT, created_at TEXT)""")
    c.execute("""CREATE TABLE IF NOT EXISTS visitors (
        id INTEGER PRIMARY KEY AUTOINCREMENT, bot_id INTEGER, user_id INTEGER,
        first_seen TEXT, UNIQUE(bot_id, user_id))""")
    conn.commit(); conn.close()
    log.info("📦 БД готова")

def db_save_bot(bot_id, uname, token, owner, tpl, data):
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("INSERT OR REPLACE INTO bots VALUES (?,?,?,?,?,?,?)",
        (bot_id, uname, token, owner, tpl, json.dumps(data, ensure_ascii=False), datetime.now().isoformat()))
    conn.commit(); conn.close()

def db_get_bot(bot_id):
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("SELECT * FROM bots WHERE bot_id=?", (bot_id,))
    r = c.fetchone(); conn.close()
    if r: return {"bot_id":r[0],"bot_username":r[1],"bot_token":r[2],"owner_id":r[3],
                  "template":r[4],"data":json.loads(r[5]),"created_at":r[6]}
    return None

def db_get_bots_by_owner(owner_id):
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("SELECT bot_id, bot_username, template, created_at FROM bots WHERE owner_id=?", (owner_id,))
    rows = c.fetchall(); conn.close(); return rows

def db_get_all_bots():
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("SELECT bot_id, bot_username, bot_token, owner_id, template, data FROM bots")
    rows = c.fetchall(); conn.close()
    return [{"bot_id":r[0],"bot_username":r[1],"bot_token":r[2],"owner_id":r[3],
             "template":r[4],"data":json.loads(r[5])} for r in rows]

def db_update_bot_data(bot_id, data):
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("UPDATE bots SET data=? WHERE bot_id=?", (json.dumps(data, ensure_ascii=False), bot_id))
    conn.commit(); conn.close()

def db_save_lead(bot_id, cid, name, msg):
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("INSERT INTO leads (bot_id,client_id,client_name,message,created_at) VALUES (?,?,?,?,?)",
        (bot_id, cid, name, msg, datetime.now().isoformat()))
    conn.commit(); conn.close()

def db_get_leads(bot_id):
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("SELECT client_name,message,created_at FROM leads WHERE bot_id=? ORDER BY created_at DESC LIMIT 20", (bot_id,))
    rows = c.fetchall(); conn.close(); return rows

def db_track_visitor(bot_id, uid):
    conn = sqlite3.connect(DB); c = conn.cursor()
    try:
        c.execute("INSERT INTO visitors (bot_id,user_id,first_seen) VALUES (?,?,?)",
            (bot_id, uid, datetime.now().isoformat()))
        conn.commit()
    except sqlite3.IntegrityError: pass
    conn.close()

def db_count_visitors(bot_id):
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("SELECT COUNT(*) FROM visitors WHERE bot_id=?", (bot_id,)); total = c.fetchone()[0]
    today = datetime.now().strftime("%Y-%m-%d")
    c.execute("SELECT COUNT(*) FROM visitors WHERE bot_id=? AND first_seen LIKE ?", (bot_id, today+"%"))
    td = c.fetchone()[0]; conn.close(); return total, td

def db_count_leads(bot_id):
    conn = sqlite3.connect(DB); c = conn.cursor()
    c.execute("SELECT COUNT(*) FROM leads WHERE bot_id=?", (bot_id,))
    t = c.fetchone()[0]; conn.close(); return t

# ═══════════════════════════════════════════
#  ШАБЛОНЫ
# ═══════════════════════════════════════════
TEMPLATES = {
    "photographer": {
        "emoji": "📸", "name": "Фотограф",
        "questions": [
            "Как тебя зовут? (Имя и фамилия)",
            "Какие жанры съёмки? (свадьбы, портреты, предметка...)",
            "Услуги и цены подробно",
            "О себе: опыт, стиль, регалии",
            "Рабочий график",
            "Контакты (телефон, Instagram, сайт)",
            "Ссылка на портфолио"
        ],
        "system_tpl": "Ты — бот-визитка фотографа {name}.\n\nЖанры: {q1}\nУслуги и цены: {q2}\nО мастере: {q3}\nГрафик: {q4}\nКонтакты: {q5}\nПортфолио: {q6}\n\nОтвечай дружелюбно, по делу, на языке клиента."
    },
    "tutor": {
        "emoji": "📚", "name": "Репетитор",
        "questions": [
            "Как тебя зовут? (Имя и фамилия)",
            "Предметы и уровни (классы, ЕГЭ, студенты...)",
            "Услуги и цены подробно",
            "О себе: образование, опыт, достижения учеников",
            "График и формат (онлайн/офлайн)",
            "Контакты (телефон, Telegram, email)",
            "Ссылка на отзывы"
        ],
        "system_tpl": "Ты — бот-визитка репетитора {name}.\n\nПредметы: {q1}\nУслуги и цены: {q2}\nО преподавателе: {q3}\nГрафик: {q4}\nКонтакты: {q5}\nОтзывы: {q6}\n\nОтвечай доброжелательно, на языке клиента."
    },
    "designer": {
        "emoji": "🎨", "name": "Дизайнер",
        "questions": [
            "Как тебя зовут? (Имя и фамилия)",
            "Направления дизайна (веб, UI/UX, интерьер, брендинг...)",
            "Услуги и цены подробно",
            "О себе: опыт, клиенты, стиль",
            "График и сроки",
            "Контакты (Telegram, Behance, сайт)",
            "Ссылка на портфолио"
        ],
        "system_tpl": "Ты — бот-визитка дизайнера {name}.\n\nНаправления: {q1}\nУслуги и цены: {q2}\nО мастере: {q3}\nГрафик: {q4}\nКонтакты: {q5}\nПортфолио: {q6}\n\nОтвечай профессионально, на языке клиента."
    },
    "nailmaster": {
        "emoji": "💅", "name": "Мастер маникюра",
        "questions": [
            "Как тебя зовут? (Имя и фамилия)",
            "Какие услуги? (маникюр, педикюр, наращивание, дизайн...)",
            "Услуги и цены подробно",
            "О себе: опыт, обучение, сертификаты",
            "График и адрес студии",
            "Контакты (телефон, Instagram, Telegram)",
            "Ссылка на примеры работ"
        ],
        "system_tpl": "Ты — бот-визитка мастера маникюра {name}.\n\nУслуги: {q1}\nЦены: {q2}\nО мастере: {q3}\nГрафик и адрес: {q4}\nКонтакты: {q5}\nРаботы: {q6}\n\nОтвечай тепло и дружелюбно, на языке клиента."
    },
    "psychologist": {
        "emoji": "🧠", "name": "Психолог / Коуч",
        "questions": [
            "Как тебя зовут? (Имя и фамилия)",
            "С какими запросами работаешь?",
            "Услуги и цены подробно",
            "О себе: образование, подход, сертификации",
            "График и формат (онлайн/офлайн)",
            "Контакты (Telegram, сайт, email)",
            "Ссылка на отзывы или публикации"
        ],
        "system_tpl": "Ты — бот-визитка психолога/коуча {name}.\n\nНаправления: {q1}\nУслуги и цены: {q2}\nО специалисте: {q3}\nГрафик: {q4}\nКонтакты: {q5}\nПубликации: {q6}\n\nОтвечай бережно. Не давай терапевтических советов — только информацию. На языке клиента."
    },
    "developer": {
        "emoji": "💻", "name": "Разработчик",
        "questions": [
            "Как тебя зовут? (Имя и фамилия)",
            "Технологии и направления (боты, сайты, AI, приложения...)",
            "Услуги и цены подробно",
            "О себе: опыт, проекты, стек",
            "График и сроки",
            "Контакты (Telegram, GitHub, сайт)",
            "Ссылка на портфолио или GitHub"
        ],
        "system_tpl": "Ты — бот-визитка разработчика {name}.\n\nТехнологии: {q1}\nУслуги и цены: {q2}\nО разработчике: {q3}\nГрафик: {q4}\nКонтакты: {q5}\nПортфолио: {q6}\n\nОтвечай технически грамотно но понятно, на языке клиента."
    }
}

# ═══════════════════════════════════════════
#  TELEGRAM HELPERS
# ═══════════════════════════════════════════
async def tg(session, token, method, **params):
    url = TG.format(token=token, method=method)
    async with session.post(url, json={k:v for k,v in params.items() if v is not None}) as r:
        d = await r.json()
        if not d.get("ok"): log.error(f"TG {method}: {d}")
        return d.get("result")

async def tg_upload(session, token, method, field, fbytes, fname="photo.jpg", **params):
    from PIL import Image
    url = TG.format(token=token, method=method)
    if method == "setMyProfilePhoto":
        try:
            pil_img = Image.open(io.BytesIO(fbytes)).convert("RGB")
            buf = io.BytesIO()
            pil_img.save(buf, format="JPEG", quality=90)
            fbytes = buf.getvalue()
            log.info(f"🖼️ Конвертировано в JPEG: {len(fbytes)} bytes")
        except Exception as e:
            log.error(f"Конвертация: {e}")
        form = aiohttp.FormData()
        photo_obj = json.dumps({"type": "static", "photo": "attach://file"})
        form.add_field("photo", photo_obj)
        form.add_field("file", fbytes, filename="photo.jpg", content_type="image/jpeg")
        async with session.post(url, data=form) as r:
            d = await r.json()
            if d.get("ok"):
                log.info("🖼️ Аватарка установлена!")
            else:
                log.error(f"setMyProfilePhoto: {d}")
            return d.get("result")
    form = aiohttp.FormData()
    form.add_field(field, fbytes, filename=fname, content_type="image/jpeg")
    for k,v in params.items():
        if v is not None: form.add_field(k, str(v) if not isinstance(v,str) else v)
    async with session.post(url, data=form) as r:
        d = await r.json()
        if not d.get("ok"): log.error(f"TG upload {method}: {d}")
        return d.get("result")

async def send(session, token, chat_id, text, reply_markup=None):
    return await tg(session, token, "sendMessage", chat_id=chat_id, text=text,
        parse_mode="HTML", reply_markup=json.dumps(reply_markup) if reply_markup else None)

async def send_photo(session, token, chat_id, photo_bytes, caption="", reply_markup=None):
    url = TG.format(token=token, method="sendPhoto")
    form = aiohttp.FormData()
    form.add_field("photo", photo_bytes, filename="photo.jpg", content_type="image/jpeg")
    form.add_field("chat_id", str(chat_id))
    if caption: form.add_field("caption", caption); form.add_field("parse_mode", "HTML")
    if reply_markup: form.add_field("reply_markup", json.dumps(reply_markup))
    async with session.post(url, data=form) as r:
        d = await r.json()
        if not d.get("ok"): log.error(f"TG sendPhoto: {d}")
        return d.get("result")

async def download_tg_photo(session, token, file_id):
    f = await tg(session, token, "getFile", file_id=file_id)
    if not f: return None
    url = f"https://api.telegram.org/file/bot{token}/{f['file_path']}"
    async with session.get(url) as r:
        if r.status == 200: return await r.read()
    return None

# ═══════════════════════════════════════════
#  OPENROUTER
# ═══════════════════════════════════════════
async def or_chat(session, messages, model=None):
    model = model or CHAT_MODEL
    headers = {"Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json"}
    body = {"model": model, "messages": messages, "max_tokens": 1024}
    try:
        async with session.post(OR_API, headers=headers, json=body) as r:
            d = await r.json()
            if "choices" in d: return d["choices"][0]["message"]["content"].strip()
            log.error(f"OR chat: {d}"); return None
    except Exception as e: log.error(f"OR error: {e}"); return None

async def generate_image(session, prompt, aspect="1:1", size="1K", ref_photo_b64=None):
    headers = {"Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json"}
    if ref_photo_b64:
        content = [
            {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{ref_photo_b64}"}},
            {"type": "text", "text": prompt}
        ]
    else:
        content = prompt
    body = {
        "model": IMAGE_MODEL,
        "messages": [{"role": "user", "content": content}],
        "modalities": ["image", "text"],
        "image_config": {"aspect_ratio": aspect, "image_size": size}
    }
    try:
        async with session.post(OR_API, headers=headers, json=body) as r:
            d = await r.json()
            if "choices" in d:
                msg = d["choices"][0].get("message", {})
                images = msg.get("images", [])
                if images:
                    b64url = images[0]["image_url"]["url"]
                    if "," in b64url: b64data = b64url.split(",", 1)[1]
                    else: b64data = b64url
                    img_bytes = base64.b64decode(b64data)
                    log.info(f"🎨 Изображение: {len(img_bytes)} bytes ({aspect}, {size})")
                    return img_bytes
                content_str = msg.get("content", "")
                if content_str and len(content_str) > 1000:
                    try:
                        img_bytes = base64.b64decode(content_str)
                        log.info(f"🎨 Изображение (content): {len(img_bytes)} bytes")
                        return img_bytes
                    except: pass
                if content_str and content_str.startswith("data:image"):
                    b64data = content_str.split(",", 1)[1] if "," in content_str else content_str
                    try:
                        img_bytes = base64.b64decode(b64data)
                        log.info(f"🎨 Изображение (data-url): {len(img_bytes)} bytes")
                        return img_bytes
                    except: pass
                fr = d["choices"][0].get("finish_reason", "")
                log.error(f"OR image: нет картинки, finish_reason={fr}")
                return None
            log.error(f"OR image: {d}"); return None
    except Exception as e: log.error(f"OR image error: {e}"); return None

# ═══════════════════════════════════════════
#  QR-КОД
# ═══════════════════════════════════════════
def make_qr(bot_username):
    qr = qrcode.QRCode(version=1, error_correction=qrcode.constants.ERROR_CORRECT_H, box_size=10, border=2)
    qr.add_data(f"https://t.me/{bot_username}"); qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white")
    buf = io.BytesIO(); img.save(buf, format="PNG"); return buf.getvalue()

# ═══════════════════════════════════════════
#  СОСТОЯНИЯ
# ═══════════════════════════════════════════
states = {}

# ═══════════════════════════════════════════
#  МЕНЕДЖЕР-БОТ
# ═══════════════════════════════════════════
async def handle_manager(session, update):

    # ─── Managed Bot создан ───
    if "managed_bot" in update:
        mb = update["managed_bot"]
        user_id = mb["user"]["id"]
        bot_user = mb["bot"]
        bot_id = bot_user["id"]
        bot_uname = bot_user.get("username", "unknown")
        log.info(f"🤖 managed_bot: @{bot_uname} (ID:{bot_id}) от {user_id}")

        token_result = await tg(session, MANAGER_BOT_TOKEN, "getManagedBotToken", user_id=bot_id)
        if not token_result:
            await send(session, MANAGER_BOT_TOKEN, user_id, "❌ Не получил токен бота."); return
        bot_token = token_result
        log.info(f"🔑 Токен: {bot_token[:25]}...")

        state = states.get(user_id, {})
        tpl_key = state.get("template", "photographer")
        tpl = TEMPLATES[tpl_key]
        answers = state.get("answers", [])

        bot_data = {
            "name": answers[0] if answers else "Без имени",
            "answers": answers, "template": tpl_key,
            "welcome_photo": None
        }

        fields = {"name": bot_data["name"]}
        for i, a in enumerate(answers[1:], 1): fields[f"q{i}"] = a
        bot_data["system_prompt"] = tpl["system_tpl"].format(
            **{k: fields.get(k,"") for k in ["name","q1","q2","q3","q4","q5","q6"]})
        bot_data["welcome"] = f"{tpl['emoji']} Привет! Я — бот-визитка: {bot_data['name']}\n\nВыбери что интересует 👇"

        db_save_bot(bot_id, bot_uname, bot_token, user_id, tpl_key, bot_data)
        await send(session, MANAGER_BOT_TOKEN, user_id, f"✅ Бот @{bot_uname} создан!\n⏳ Генерирую аватарку...")

        # Аватарка
        avatar_prompt = await or_chat(session, [
            {"role": "system", "content": "You create short image prompts for icons. Output ONLY the prompt. Max 30 words. Rules: NO people, NO faces, NO names, NO text. Only objects and symbols."},
            {"role": "user", "content": f"Icon with objects symbolizing the profession: {tpl['name']}. Only tools and symbols of this job. Flat vector style, bright colors, white background."}
        ])
        if not avatar_prompt:
            avatar_prompt = f"Flat vector icon of tools for {tpl['name']}, bright colors, white background, no text, no people"
        log.info(f"🎨 Avatar prompt: {avatar_prompt[:80]}...")
        avatar = await generate_image(session, avatar_prompt, aspect="1:1", size="1K")
        if avatar:
            await tg_upload(session, bot_token, "setMyProfilePhoto", "photo", avatar)
            await send(session, MANAGER_BOT_TOKEN, user_id, "🖼️ Аватарка установлена!")
        else:
            await send(session, MANAGER_BOT_TOKEN, user_id, "⚠️ Аватарка не сгенерировалась, продолжаем.")

        # Фото-визитка: спрашиваем
        states[user_id] = {"step": "photo_choice", "bot_id": bot_id, "bot_uname": bot_uname,
                           "template": tpl_key, "answers": answers}
        await send(session, MANAGER_BOT_TOKEN, user_id,
            "📸 Фото-визитка\n\n"
            "Картинка, которую клиенты увидят при открытии бота.\n\n"
            "Два варианта:",
            reply_markup={"inline_keyboard": [
                [{"text": "🎨 Сгенерировать по описанию", "callback_data": "photo:gen"}],
                [{"text": "📷 Загрузить своё фото", "callback_data": "photo:own"}],
                [{"text": "⏭ Пропустить", "callback_data": "photo:skip"}]
            ]})
        return

    # ─── Сообщение ───
    msg = update.get("message")
    if not msg:
        cb = update.get("callback_query")
        if cb: await handle_manager_cb(session, cb)
        return

    chat_id = msg["chat"]["id"]
    user_id = msg["from"]["id"]
    text = msg.get("text", "")
    photo = msg.get("photo")
    state = states.get(user_id, {})

    # Фото от пользователя
    if photo and state.get("step") in ("photo_wait", "edit_photo_wait", "edit_avatar_wait"):
        file_id = photo[-1]["file_id"]
        await send(session, MANAGER_BOT_TOKEN, chat_id, "⏳ Обрабатываю фото...")
        photo_bytes = await download_tg_photo(session, MANAGER_BOT_TOKEN, file_id)
        if not photo_bytes:
            await send(session, MANAGER_BOT_TOKEN, chat_id, "❌ Не скачалось. Попробуй ещё раз."); return

        photo_b64 = base64.b64encode(photo_bytes).decode()
        bot_id = state["bot_id"]
        bot_info = db_get_bot(bot_id)
        if not bot_info: return
        data = bot_info["data"]; tpl = TEMPLATES[bot_info["template"]]

        if state["step"] == "edit_avatar_wait":
            prompt = f"Transform this photo into a professional, modern avatar icon for a {tpl['name']}. Vibrant, clean, round composition. Keep recognizable but stylized. No text."
            img = await generate_image(session, prompt, aspect="1:1", size="1K", ref_photo_b64=photo_b64)
            if img:
                await tg_upload(session, bot_info["bot_token"], "setMyProfilePhoto", "photo", img)
                await send(session, MANAGER_BOT_TOKEN, chat_id, "✅ Аватарка обновлена!")
            else:
                await send(session, MANAGER_BOT_TOKEN, chat_id, "❌ Не получилось. Попробуй другое фото.")
        else:
            prompt = f"Transform this photo into a professional, stylish business card image for a {tpl['name']}. Business: {data['name']}. Premium, modern feel. Keep recognizable. No text."
            img = await generate_image(session, prompt, aspect="4:3", size="2K", ref_photo_b64=photo_b64)
            if img:
                data["welcome_photo"] = base64.b64encode(img).decode()
                db_update_bot_data(bot_id, data)
                await send_photo(session, MANAGER_BOT_TOKEN, chat_id, img, caption="✅ Фото-визитка установлена!")
            else:
                await send(session, MANAGER_BOT_TOKEN, chat_id, "❌ Не получилось. Попробуй другое фото.")

        if state["step"] == "photo_wait":
            await finalize_bot(session, user_id, bot_id)
        states.pop(user_id, None)
        return

    # Команды
    if text == "/start":
        states.pop(user_id, None)
        buttons = [[{"text": f"{t['emoji']} {t['name']}", "callback_data": f"tpl:{k}"}] for k,t in TEMPLATES.items()]
        await send(session, MANAGER_BOT_TOKEN, chat_id,
            "🪪 Bot-Vizitka Factory\n\n"
            "Создам тебе бота-визитку за 2 минуты.\n"
            "Аватарка, кнопки, запись клиентов, QR-код, AI-ответы.\n\n"
            "Выбери профессию 👇",
            reply_markup={"inline_keyboard": buttons})
        return

    if text == "/mybots":
        bots = db_get_bots_by_owner(user_id)
        if not bots: await send(session, MANAGER_BOT_TOKEN, chat_id, "Нет ботов. Жми /start!"); return
        lines = ["📋 Твои боты:\n"]
        for b in bots:
            t = TEMPLATES.get(b[2], {}); lines.append(f"🟢 {t.get('emoji','')} @{b[1]} — {t.get('name','')}")
        await send(session, MANAGER_BOT_TOKEN, chat_id, "\n".join(lines))
        return

    if text == "/leads":
        bots = db_get_bots_by_owner(user_id)
        if not bots: await send(session, MANAGER_BOT_TOKEN, chat_id, "Нет ботов."); return
        all_l = []
        for b in bots:
            for l in db_get_leads(b[0]): all_l.append(f"📩 @{b[1]}: {l[0]} — {l[1]} ({l[2][:10]})")
        await send(session, MANAGER_BOT_TOKEN, chat_id,
            ("📋 Заявки:\n\n" + "\n".join(all_l[:20])) if all_l else "Заявок пока нет.")
        return

    if text == "/stats":
        bots = db_get_bots_by_owner(user_id)
        if not bots: await send(session, MANAGER_BOT_TOKEN, chat_id, "Нет ботов."); return
        lines = ["📊 Статистика:\n"]
        for b in bots:
            total, td = db_count_visitors(b[0]); leads = db_count_leads(b[0])
            lines.append(f"{TEMPLATES.get(b[2],{}).get('emoji','')} @{b[1]}: 👥 {total} (+{td}) | 📩 {leads}")
        await send(session, MANAGER_BOT_TOKEN, chat_id, "\n".join(lines))
        return

    if text == "/qr":
        bots = db_get_bots_by_owner(user_id)
        if not bots: await send(session, MANAGER_BOT_TOKEN, chat_id, "Нет ботов."); return
        for b in bots:
            await tg_upload(session, MANAGER_BOT_TOKEN, "sendPhoto", "photo", make_qr(b[1]),
                fname="qr.png", chat_id=chat_id, caption=f"📱 @{b[1]}")
        return

    if text == "/edit":
        bots = db_get_bots_by_owner(user_id)
        if not bots: await send(session, MANAGER_BOT_TOKEN, chat_id, "Нет ботов."); return
        buttons = [[{"text": f"{TEMPLATES.get(b[2],{}).get('emoji','')} @{b[1]}",
            "callback_data": f"edit:{b[0]}"}] for b in bots]
        await send(session, MANAGER_BOT_TOKEN, chat_id, "Какого бота редактировать?",
            reply_markup={"inline_keyboard": buttons})
        return

    # Анкета
    if state.get("step") == "answering":
        answers = state["answers"]; answers.append(text)
        tpl = TEMPLATES[state["template"]]
        idx = len(answers)
        if idx < len(tpl["questions"]):
            await send(session, MANAGER_BOT_TOKEN, chat_id,
                f"✅ Записал!\n\nВопрос {idx+1}/{len(tpl['questions'])}:\n{tpl['questions'][idx]}")
        else:
            state["step"] = "ready"
            summary = "📝 Всё заполнено!\n\n"
            for i,q in enumerate(tpl["questions"]): summary += f"{i+1}. {answers[i]}\n"
            summary += "\nЕсли всё верно — жми кнопку ниже 👇"
            suggested = answers[0].lower().replace(" ","_").replace(".","")[:20] + "_vizitka_bot"
            await send(session, MANAGER_BOT_TOKEN, chat_id, summary)
            await tg(session, MANAGER_BOT_TOKEN, "sendMessage",
                chat_id=chat_id, text="Нажми кнопку чтобы создать бота 👇",
                reply_markup=json.dumps({"keyboard": [
                    [{"text": "🤖 Создать бота-визитку!", "request_managed_bot": {
                        "request_id": 1, "suggested_username": suggested,
                        "suggested_name": f"{tpl['emoji']} {answers[0]}"}}]
                ], "resize_keyboard": True, "one_time_keyboard": True}))
        return

    # Редактирование текста
    if state.get("step") == "editing":
        bot_id = state["edit_bot_id"]; fidx = state["edit_field"]
        bot_info = db_get_bot(bot_id)
        if bot_info:
            data = bot_info["data"]; data["answers"][fidx] = text
            tpl = TEMPLATES[bot_info["template"]]
            fields = {"name": data["answers"][0]}
            for i,a in enumerate(data["answers"][1:],1): fields[f"q{i}"] = a
            data["system_prompt"] = tpl["system_tpl"].format(
                **{k: fields.get(k,"") for k in ["name","q1","q2","q3","q4","q5","q6"]})
            db_update_bot_data(bot_id, data)
            await send(session, MANAGER_BOT_TOKEN, chat_id, f"✅ Обновлено! @{bot_info['bot_username']} уже использует новые данные.")
        states.pop(user_id, None); return

    await send(session, MANAGER_BOT_TOKEN, chat_id,
        "/start — создать бота\n/mybots — мои боты\n/edit — редактировать\n/leads — заявки\n/stats — статистика\n/qr — QR-код")

async def finalize_bot(session, user_id, bot_id):
    bot_info = db_get_bot(bot_id)
    if not bot_info: return
    uname = bot_info["bot_username"]
    qr = make_qr(uname)
    await tg_upload(session, MANAGER_BOT_TOKEN, "sendPhoto", "photo", qr,
        fname="qr.png", chat_id=user_id, caption=f"📱 QR-код: @{uname}\nРаспечатай на визитке!")
    await send(session, MANAGER_BOT_TOKEN, user_id,
        f"🎉 Готово!\n\n"
        f"🔗 https://t.me/{uname}\n\n"
        f"Админка:\n"
        f"/mybots — список ботов\n"
        f"/edit — изменить тексты, фото, аватарку\n"
        f"/leads — заявки клиентов\n"
        f"/stats — статистика\n"
        f"/qr — QR-код повторно")
    asyncio.create_task(run_child(session, bot_id))
    states.pop(user_id, None)

async def handle_manager_cb(session, cb):
    user_id = cb["from"]["id"]; chat_id = cb["message"]["chat"]["id"]; data = cb.get("data","")
    await tg(session, MANAGER_BOT_TOKEN, "answerCallbackQuery", callback_query_id=cb["id"])

    if data.startswith("tpl:"):
        key = data[4:]
        if key not in TEMPLATES: return
        tpl = TEMPLATES[key]
        states[user_id] = {"step": "answering", "template": key, "answers": []}
        await send(session, MANAGER_BOT_TOKEN, chat_id,
            f"{tpl['emoji']} Делаем визитку: {tpl['name']}\n\n"
            f"Отвечай на {len(tpl['questions'])} вопросов.\n\n"
            f"Вопрос 1/{len(tpl['questions'])}:\n{tpl['questions'][0]}")
        return

    if data == "photo:gen":
        state = states.get(user_id, {})
        bot_id = state.get("bot_id")
        if not bot_id: return
        bot_info = db_get_bot(bot_id)
        if not bot_info: return
        d = bot_info["data"]; tpl = TEMPLATES[bot_info["template"]]
        await send(session, MANAGER_BOT_TOKEN, chat_id, "⏳ Генерирую фото-визитку (10-15 сек)...")
        prompt = await or_chat(session, [
            {"role": "system", "content": "You create short image prompts for business hero images. Output ONLY the prompt. Max 50 words. Style: professional, warm lighting, premium feel. NO text on image, NO people, NO faces."},
            {"role": "user", "content": f"Business: {tpl['name']} — {d['name']}\nDetails: {'; '.join(d['answers'][:4])}\n\nCreate a hero image with objects and atmosphere of this business."}
        ])
        if not prompt: prompt = f"Professional business image for {tpl['name']}, tools and workspace, warm lighting, no text, no people"
        img = await generate_image(session, prompt, aspect="4:3", size="2K")
        if img:
            d["welcome_photo"] = base64.b64encode(img).decode()
            db_update_bot_data(bot_id, d)
            await send_photo(session, MANAGER_BOT_TOKEN, chat_id, img, caption="✅ Фото-визитка готова!")
        else:
            await send(session, MANAGER_BOT_TOKEN, chat_id, "⚠️ Не удалось сгенерировать, продолжаем без фото.")
        await finalize_bot(session, user_id, bot_id)
        return

    if data == "photo:own":
        state = states.get(user_id, {})
        if not state.get("bot_id"): return
        states[user_id] = {**state, "step": "photo_wait"}
        await send(session, MANAGER_BOT_TOKEN, chat_id, "📷 Отправь фото. Я стилизую его под визитку.")
        return

    if data == "photo:skip":
        state = states.get(user_id, {})
        bot_id = state.get("bot_id")
        if bot_id: await finalize_bot(session, user_id, bot_id)
        return

    # Редактирование
    if data.startswith("edit:"):
        bot_id = int(data[5:]); bot_info = db_get_bot(bot_id)
        if not bot_info: return
        tpl = TEMPLATES.get(bot_info["template"], {})
        buttons = []
        for i,q in enumerate(tpl.get("questions",[])):
            short = q[:35] + "..." if len(q) > 35 else q
            buttons.append([{"text": f"✏️ {i+1}. {short}", "callback_data": f"ef:{bot_id}:{i}"}])
        buttons.append([{"text": "🎨 Заменить аватарку", "callback_data": f"ea:{bot_id}"}])
        buttons.append([{"text": "📸 Заменить фото-визитку", "callback_data": f"ep:{bot_id}"}])
        await send(session, MANAGER_BOT_TOKEN, chat_id,
            f"🛠 Редактирование @{bot_info['bot_username']}\n\nЧто изменить?",
            reply_markup={"inline_keyboard": buttons})
        return

    if data.startswith("ef:"):
        parts = data.split(":"); bot_id = int(parts[1]); fidx = int(parts[2])
        bot_info = db_get_bot(bot_id)
        if not bot_info: return
        old = bot_info["data"]["answers"][fidx] if fidx < len(bot_info["data"]["answers"]) else ""
        states[user_id] = {"step": "editing", "edit_bot_id": bot_id, "edit_field": fidx}
        await send(session, MANAGER_BOT_TOKEN, chat_id, f"Сейчас:\n{old}\n\nВведи новое значение:")
        return

    if data.startswith("ea:"):
        bot_id = int(data[3:])
        buttons = [
            [{"text": "🎨 Сгенерировать новую", "callback_data": f"eag:{bot_id}"}],
            [{"text": "📷 Загрузить своё фото", "callback_data": f"eao:{bot_id}"}]
        ]
        await send(session, MANAGER_BOT_TOKEN, chat_id, "Как обновить аватарку?",
            reply_markup={"inline_keyboard": buttons})
        return

    if data.startswith("eag:"):
        bot_id = int(data[4:]); bot_info = db_get_bot(bot_id)
        if not bot_info: return
        d = bot_info["data"]; tpl = TEMPLATES[bot_info["template"]]
        await send(session, MANAGER_BOT_TOKEN, chat_id, "⏳ Генерирую новую аватарку...")
        prompt = await or_chat(session, [
            {"role": "system", "content": "You create short image prompts for icons. Output ONLY the prompt. Max 30 words. NO people, NO faces, NO names, NO text. Only objects and symbols."},
            {"role": "user", "content": f"NEW creative icon for {tpl['name']}. Tools and symbols. Flat vector, bright colors, white background."}
        ])
        if not prompt: prompt = f"Creative icon for {tpl['name']}, flat vector, bright colors"
        img = await generate_image(session, prompt, aspect="1:1", size="1K")
        if img:
            await tg_upload(session, bot_info["bot_token"], "setMyProfilePhoto", "photo", img)
            await send(session, MANAGER_BOT_TOKEN, chat_id, "✅ Аватарка обновлена!")
        else:
            await send(session, MANAGER_BOT_TOKEN, chat_id, "❌ Не получилось, попробуй ещё раз.")
        return

    if data.startswith("eao:"):
        bot_id = int(data[4:])
        states[user_id] = {"step": "edit_avatar_wait", "bot_id": bot_id}
        await send(session, MANAGER_BOT_TOKEN, chat_id, "📷 Отправь фото для аватарки.")
        return

    if data.startswith("ep:"):
        bot_id = int(data[3:])
        buttons = [
            [{"text": "🎨 Сгенерировать новую", "callback_data": f"epg:{bot_id}"}],
            [{"text": "📷 Загрузить своё фото", "callback_data": f"epo:{bot_id}"}],
            [{"text": "🗑 Удалить фото", "callback_data": f"epd:{bot_id}"}]
        ]
        await send(session, MANAGER_BOT_TOKEN, chat_id, "Как обновить фото-визитку?",
            reply_markup={"inline_keyboard": buttons})
        return

    if data.startswith("epg:"):
        bot_id = int(data[4:]); bot_info = db_get_bot(bot_id)
        if not bot_info: return
        d = bot_info["data"]; tpl = TEMPLATES[bot_info["template"]]
        await send(session, MANAGER_BOT_TOKEN, chat_id, "⏳ Генерирую новую фото-визитку...")
        prompt = await or_chat(session, [
            {"role": "system", "content": "You create image prompts for business hero images. ONLY the prompt. Max 50 words. Professional, warm lighting, no text, no people."},
            {"role": "user", "content": f"Business: {tpl['name']} — {d['name']}\nDetails: {'; '.join(d['answers'][:4])}\n\nCreate a NEW different hero image."}
        ])
        if not prompt: prompt = f"Professional hero image for {tpl['name']}, no text, no people"
        img = await generate_image(session, prompt, aspect="4:3", size="2K")
        if img:
            d["welcome_photo"] = base64.b64encode(img).decode()
            db_update_bot_data(bot_id, d)
            await send_photo(session, MANAGER_BOT_TOKEN, chat_id, img, caption="✅ Фото-визитка обновлена!")
        else:
            await send(session, MANAGER_BOT_TOKEN, chat_id, "❌ Не получилось.")
        return

    if data.startswith("epo:"):
        bot_id = int(data[4:])
        states[user_id] = {"step": "edit_photo_wait", "bot_id": bot_id}
        await send(session, MANAGER_BOT_TOKEN, chat_id, "📷 Отправь фото для визитки.")
        return

    if data.startswith("epd:"):
        bot_id = int(data[4:]); bot_info = db_get_bot(bot_id)
        if not bot_info: return
        d = bot_info["data"]; d["welcome_photo"] = None
        db_update_bot_data(bot_id, d)
        await send(session, MANAGER_BOT_TOKEN, chat_id, "✅ Фото удалено.")
        return

# ═══════════════════════════════════════════
#  ДОЧЕРНИЙ БОТ
# ═══════════════════════════════════════════
child_data = {}

def get_child(bot_id, chat_id):
    key = (bot_id, chat_id)
    if key not in child_data: child_data[key] = {"history": [], "booking": False}
    return child_data[key]

async def run_child(session, bot_id):
    bot_info = db_get_bot(bot_id)
    if not bot_info: log.error(f"Бот {bot_id} не найден"); return
    token = bot_info["bot_token"]; uname = bot_info["bot_username"]
    log.info(f"🟢 Запуск @{uname}...")
    offset = 0
    while True:
        try:
            updates = await tg(session, token, "getUpdates", offset=offset, timeout=30,
                allowed_updates=json.dumps(["message","callback_query"]))
            if not updates: continue
            for upd in updates:
                offset = upd["update_id"] + 1
                bot_info = db_get_bot(bot_id)
                if not bot_info: continue
                if "callback_query" in upd:
                    await handle_child_cb(session, bot_info, upd["callback_query"])
                elif "message" in upd:
                    await handle_child_msg(session, bot_info, upd["message"])
        except asyncio.CancelledError: break
        except Exception as e:
            log.error(f"[@{uname}] {e}"); await asyncio.sleep(3)

async def handle_child_msg(session, bot_info, msg):
    bot_id = bot_info["bot_id"]; token = bot_info["bot_token"]; uname = bot_info["bot_username"]
    chat_id = msg["chat"]["id"]; user_id = msg["from"]["id"]
    text = msg.get("text",""); user_name = msg["from"].get("first_name","Клиент")
    lang = msg["from"].get("language_code","ru")
    data = bot_info["data"]
    db_track_visitor(bot_id, user_id)
    cd = get_child(bot_id, chat_id)

    if text == "/start":
        cd["history"] = []; cd["booking"] = False
        buttons = [
            [{"text": "💰 Услуги и цены", "callback_data": "v:prices"}],
            [{"text": "👤 Обо мне", "callback_data": "v:about"}],
            [{"text": "🖼 Портфолио", "callback_data": "v:portfolio"}],
            [{"text": "📅 Записаться", "callback_data": "v:book"}],
            [{"text": "🕐 График работы", "callback_data": "v:schedule"}],
            [{"text": "📞 Контакты", "callback_data": "v:contacts"}]
        ]
        welcome_photo = data.get("welcome_photo")
        if welcome_photo:
            try:
                photo_bytes = base64.b64decode(welcome_photo)
                await send_photo(session, token, chat_id, photo_bytes,
                    caption=data.get("welcome","Привет!"), reply_markup={"inline_keyboard": buttons})
            except:
                await send(session, token, chat_id, data.get("welcome","Привет!"),
                    reply_markup={"inline_keyboard": buttons})
        else:
            await send(session, token, chat_id, data.get("welcome","Привет!"),
                reply_markup={"inline_keyboard": buttons})
        return

    if cd["booking"]:
        db_save_lead(bot_id, user_id, user_name, text)
        await send(session, MANAGER_BOT_TOKEN, bot_info["owner_id"],
            f"📩 Новая заявка!\n\nБот: @{uname}\nКлиент: {user_name}\nСообщение: {text}")
        await send(session, token, chat_id, f"✅ Заявка отправлена! {data.get('name','Мастер')} свяжется с тобой.")
        cd["booking"] = False
        log.info(f"📩 [@{uname}] {user_name}: {text[:50]}")
        return

    cd["history"].append({"role": "user", "content": text})
    if len(cd["history"]) > 10: cd["history"] = cd["history"][-10:]
    lang_note = f" Answer in {lang}." if lang != "ru" else ""
    messages = [{"role":"system","content": data.get("system_prompt","Ты бот-визитка.") + lang_note}] + cd["history"]
    await tg(session, token, "sendChatAction", chat_id=chat_id, action="typing")
    answer = await or_chat(session, messages)
    if answer:
        cd["history"].append({"role":"assistant","content": answer})
        await send(session, token, chat_id, answer)
        log.info(f"[@{uname}] {user_name}: {text[:40]} → {answer[:40]}")
    else:
        await send(session, token, chat_id, "Извините, попробуйте позже.")

async def handle_child_cb(session, bot_info, cb):
    bot_id = bot_info["bot_id"]; token = bot_info["bot_token"]
    chat_id = cb["message"]["chat"]["id"]; data_str = cb.get("data","")
    await tg(session, token, "answerCallbackQuery", callback_query_id=cb["id"])
    data = bot_info["data"]; answers = data.get("answers",[])
    cd = get_child(bot_id, chat_id)

    if data_str == "v:prices":
        await send(session, token, chat_id, f"💰 Услуги и цены\n\n{answers[2] if len(answers)>2 else 'Не указано'}")
    elif data_str == "v:about":
        await send(session, token, chat_id, f"👤 О мастере\n\n{answers[3] if len(answers)>3 else 'Не указано'}")
    elif data_str == "v:portfolio":
        await send(session, token, chat_id, f"🖼 Портфолио\n\n{answers[6] if len(answers)>6 else 'Не указано'}")
    elif data_str == "v:book":
        cd["booking"] = True
        await send(session, token, chat_id, "📅 Запись\n\nНапиши имя, какая услуга нужна и удобное время — передам мастеру!")
    elif data_str == "v:schedule":
        await send(session, token, chat_id, f"🕐 График\n\n{answers[4] if len(answers)>4 else 'Не указан'}")
    elif data_str == "v:contacts":
        await send(session, token, chat_id, f"📞 Контакты\n\n{answers[5] if len(answers)>5 else 'Не указаны'}")

# ═══════════════════════════════════════════
#  MAIN
# ═══════════════════════════════════════════
async def main():
    init_db()
    async with aiohttp.ClientSession() as session:
        me = await tg(session, MANAGER_BOT_TOKEN, "getMe")
        if not me: log.error("❌ Проверь токен!"); return
        cmb = me.get("can_manage_bots", False)
        log.info(f"🤖 Менеджер: @{me.get('username')} | can_manage_bots: {cmb}")
        if not cmb: log.error("❌ Включи Managed Bots в @BotFather!"); return

        for b in db_get_all_bots():
            log.info(f"🔄 Восстанавливаю @{b['bot_username']}...")
            asyncio.create_task(run_child(session, b["bot_id"]))

        log.info("🟢 Polling запущен. Напиши /start боту.")
        offset = 0
        while True:
            try:
                updates = await tg(session, MANAGER_BOT_TOKEN, "getUpdates",
                    offset=offset, timeout=30,
                    allowed_updates=json.dumps(["message","callback_query","managed_bot"]))
                if not updates: continue
                for upd in updates:
                    offset = upd["update_id"] + 1
                    await handle_manager(session, upd)
            except KeyboardInterrupt: break
            except Exception as e: log.error(f"Manager: {e}"); await asyncio.sleep(3)

if __name__ == "__main__":
    print("""
    ╔════════════════════════════════════════╗
    ║  🪪 BOT-VIZITKA FACTORY v3           ║
    ║  Nano Banana 2 + Managed Bots 9.6    ║
    ║  Клуб Вайбкодеров | 2026             ║
    ╚════════════════════════════════════════╝
    """)
    asyncio.run(main())
ENDOFFILE
💡
Важно! Что происходит после вставки:
После того как вы вставите команду в терминал, prompt исчезнет (не будет (venv) root@...). Это нормально!

Терминал перешёл в режим ввода кода и ждёт завершающего маркера ENDOFFILE.

Что делать:
1️⃣ Просто нажмите Enter после вставки всей команды
2️⃣ Терминал завершит создание файла
3️⃣ Prompt вернётся: (venv) root@...:~/vizitka-factory#

Если prompt не вернулся: нажмите Enter ещё раз.

Проверка создания файла:

Проверка
ls -lh bot.py

Должны увидеть: -rw-r--r-- 1 root root ~22K ... bot.py

🔴
Частые проблемы:

1. Prompt не вернулся после вставки
→ Нажмите Enter ещё раз. Терминал ждёт завершения ввода.

2. Видите > или >>> в начале строки
→ Терминал ждёт продолжения. Нажмите Ctrl+C для отмены и вставьте команду заново целиком.

3. Файл не создался (ls: cannot access 'bot.py')
→ Проверьте, что скопировали команду полностью, включая ENDOFFILE.

4. Терминал завис
→ Ctrl+C → rm bot.py → вставьте заново
6

Вставь токены

🔒
Безопасность:
Ваши токены не отправляются на сервер. Команды формируются прямо в браузере (JavaScript). Мы не видим и не сохраняем ваши ключи.
💡
После выполнения команд проверьте:
Проверка токенов
grep "MANAGER_BOT_TOKEN\|OPENROUTER_API_KEY" bot.py | head -2

Должны быть ваши реальные токены без слова ВСТАВЬ.

7

Запусти

Terminal
source venv/bin/activate python3 bot.py
Ожидаемый вывод
    ╔════════════════════════════════════════╗
    ║  🪪 BOT-VIZITKA FACTORY v3           ║
    ╚════════════════════════════════════════╝

📦 БД готова
🤖 Менеджер: @ваш_бот | can_manage_bots: True
🟢 Polling запущен. Напиши /start боту.
🔴
Важно!
Если видите can_manage_bots: False — вернитесь в @BotFather и включите Managed Bots (шаг 1, пункт 6).
8

Тестируй

Откройте менеджер-бота в Telegram и следуйте инструкциям:

  1. /start → выберите шаблон профессии
  2. Ответьте на 7 вопросов (по одному сообщению)
  3. Нажмите «🤖 Создать бота-визитку!»
  4. Выберите username для нового бота
  5. Дождитесь генерации аватарки
  6. Выберите вариант фото-визитки (сгенерировать / своё / пропустить)
  7. Получите QR-код и ссылку на бота
  8. Откройте бота-визитку → протестируйте кнопки и AI-ответы
📋
Команды админки:
/mybots — список ботов
/edit — редактировать тексты, фото, аватарку
/leads — заявки клиентов
/stats — статистика посещений
/qr — QR-код повторно

Ниже в секции «Тестируем» — готовые тексты для каждого из 6 шаблонов. Просто копируйте и вставляйте!

9

Запуск как сервис (чтобы работал после закрытия SSH)

Создаём systemd сервис:

Команда 1: Создать файл сервиса
cat > /etc/systemd/system/vizitka-factory.service << 'ENDOFFILE'
[Unit]
Description=Bot-Vizitka Factory
After=network.target

[Service]
Type=simple
WorkingDirectory=/root/vizitka-factory
ExecStart=/root/vizitka-factory/venv/bin/python3 bot.py
Restart=always
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
ENDOFFILE

Запускаем сервис:

Команда 2: Запустить
systemctl daemon-reload && systemctl enable vizitka-factory && systemctl start vizitka-factory

Полезные команды:

Просмотр логов
journalctl -u vizitka-factory -f
Остановить / Перезапустить
systemctl restart vizitka-factory
🟡
Учтённая ошибка:
Обязательно сначала создайте файл сервиса (команда 1), и только потом запускайте его (команда 2). Если выполнить команду 2 без команды 1 — получите ошибку.

Тестируем — 6 шаблонов, 12 примеров

Откройте менеджер-бота в Telegram, нажмите /start и пройдите все 6 шаблонов.
Для каждого — два готовых набора ответов. Отправляйте по одному сообщению.

📸 Фотограф — пример 1

Жмите 📸 Фотограф, отправляйте по одному:

1. Имя:

Анна Светлова

2. Жанры:

Свадебная съёмка, love-story, семейные фотосессии, портретная съёмка в студии и на улице, предметная фотография для маркетплейсов, репортажная съёмка мероприятий

3. Услуги и цены:

Свадебная съёмка полный день (10 часов, 500+ фото, обработка) — 45000₽. Love-story 2 часа (100+ фото, обработка) — 8000₽. Семейная фотосессия 1.5 часа (80+ фото) — 7000₽. Портретная съёмка в студии 1 час (30+ фото) — 5000₽. Предметная съёмка для маркетплейсов от 300₽/карточка (минимум 10 штук). Репортаж мероприятия от 3 часов — 15000₽. Срочная обработка +30%. Выезд за МКАД +1000₽/час дороги

4. О себе:

Профессиональный фотограф, 8 лет опыта. Работаю в стиле fine art и репортаж. Снимала для журналов Elle и Cosmopolitan. Выпускница Школы Родченко. Призёр фотоконкурса «Золотая Камера 2024». Работаю на Canon R5 и Sony A7IV. Имею собственную студию 80м² в центре Москвы с 3 фонами и профессиональным светом

5. График:

Пн-Сб 09:00-21:00, воскресенье — только свадьбы по предварительной записи. Студийная съёмка — в любой день по записи

6. Контакты:

Телефон: +7-926-555-12-34, Instagram: @anna_svetlova_photo, Telegram: @anna_photo, сайт: annasvetlova.ru

7. Портфолио:

https://annasvetlova.ru/portfolio

🧪 Тест бота:

Сколько стоит свадебная съёмка? И можно на выходных?

📸 Фотограф — пример 2

Тот же шаблон, другой профиль:

1. Имя:

Дмитрий Волков

2. Жанры:

Коммерческая фотография, съёмка интерьеров и архитектуры, фуд-фотография для ресторанов и кафе, каталожная съёмка одежды, аэросъёмка с дрона

3. Услуги и цены:

Съёмка интерьера квартиры до 100м² (30+ фото, обработка, виртуальный тур) — 25000₽. Фуд-фотография для меню ресторана от 500₽/блюдо (минимум 20 позиций). Каталожная съёмка одежды на модели — 2000₽/образ. Аэросъёмка с дрона DJI Mavic 3 — 15000₽/час. Съёмка коммерческой недвижимости от 35000₽. Пакет для маркетплейсов: 50 карточек с инфографикой — 25000₽. Срочность 24 часа +50%

4. О себе:

Коммерческий фотограф, 12 лет в индустрии. Постоянный фотограф сети ресторанов White Rabbit Family. Снимал интерьеры для AD Russia и Elle Decoration. Сертифицированный пилот дрона. Работаю с крупными брендами: IKEA, Leroy Merlin, Lamoda. Оборудование: Phase One IQ4, DJI Mavic 3 Pro, полный комплект студийного света Profoto

5. График:

Пн-Пт 08:00-20:00, Сб 10:00-18:00, воскресенье — выходной. Выезд на объект в любой день по согласованию

6. Контакты:

Телефон: +7-903-777-88-99, Instagram: @volkov_commercial, Telegram: @dvolkov_photo, email: [email protected]

7. Портфолио:

https://behance.net/volkov_photo

🧪 Тест бота:

Нужна съёмка меню для ресторана, 30 блюд. Сколько по времени и цене?

📚 Репетитор — пример 1

Жмите 📚 Репетитор:

1. Имя:

Елена Маркова

2. Предметы:

Математика и физика для школьников 7-11 класс, подготовка к ОГЭ и ЕГЭ, олимпиадная математика, высшая математика для студентов 1-2 курса (матанализ, линейная алгебра, теория вероятностей)

3. Услуги и цены:

Индивидуальное занятие 60 мин (7-9 класс) — 2000₽. Индивидуальное занятие 90 мин (10-11 класс) — 3000₽. Подготовка к ЕГЭ (90 мин, индивидуально) — 3500₽. Подготовка к ОГЭ (60 мин) — 2500₽. Олимпиадная математика (90 мин) — 4000₽. Высшая математика для студентов (90 мин) — 3500₽. Мини-группа 2-3 человека — скидка 30% каждому. Пробное занятие (30 мин) — бесплатно. Пакет 8 занятий — скидка 10%

4. О себе:

Кандидат физико-математических наук, доцент МГУ. 15 лет преподавательского опыта. Средний балл учеников на ЕГЭ — 86 из 100. 12 учеников стали призёрами Всероссийской олимпиады. Автор учебного пособия «Математика без страха». Член жюри городских олимпиад. Использую интерактивные доски и собственные тренажёры

5. График:

Пн-Пт 15:00-21:00, Сб 10:00-18:00, воскресенье — выходной. Онлайн (Zoom) и офлайн (м. Университет, 5 мин пешком)

6. Контакты:

Телефон: +7-916-333-44-55, Telegram: @elena_math, email: [email protected]

7. Отзывы:

https://profi.ru/profile/MarkovaEV

🧪 Тест:

Дочка в 10 классе, нужна подготовка к ЕГЭ по математике. Можно онлайн по субботам?

📚 Репетитор — пример 2

1. Имя:

Александр Петренко

2. Предметы:

Английский язык: разговорный, бизнес-английский, подготовка к IELTS/TOEFL/Cambridge, английский для IT-специалистов, корпоративное обучение для компаний

3. Услуги и цены:

Индивидуальное занятие 60 мин (общий курс) — 2500₽. Бизнес-английский 60 мин — 3000₽. Подготовка к IELTS/TOEFL (90 мин) — 4000₽. Английский для IT (60 мин) — 3000₽. Разговорный клуб мини-группа (90 мин, 4-6 чел) — 1000₽/чел. Корпоративное обучение для компаний — от 5000₽/час. Интенсив «С нуля до B1 за 3 месяца» — 80000₽ (48 занятий). Пробное занятие 30 мин — бесплатно

4. О себе:

Сертификат CELTA (Cambridge), IELTS 8.5. Жил и работал в Лондоне 4 года. 10 лет опыта преподавания. Средний результат учеников IELTS — 7.5. Обучал сотрудников Яндекса, Сбера и Тинькофф. Использую коммуникативную методику, без зубрёжки — только живой язык. Все материалы предоставляю бесплатно

5. График:

Пн-Сб 09:00-22:00, воскресенье 12:00-18:00. Только онлайн (Zoom, Google Meet). Часовой пояс UTC+3 (Москва)

6. Контакты:

Телефон: +7-905-111-22-33, Telegram: @alex_english_pro, Instagram: @petrenko_english, сайт: alexenglish.ru

7. Отзывы:

https://alexenglish.ru/reviews

🧪 Тест:

Мне нужен IELTS 7.0 через 3 месяца. Реально? Сколько занятий в неделю?

🎨 Дизайнер — пример 1

Жмите 🎨 Дизайнер:

1. Имя:

Мария Козлова

2. Направления:

UX/UI дизайн мобильных приложений и сайтов, веб-дизайн, брендинг и айдентика, дизайн презентаций для бизнеса, дизайн-системы для продуктовых команд

3. Услуги и цены:

Дизайн лендинга (до 10 экранов, адаптив) — 40000₽. Дизайн многостраничного сайта — от 80000₽. UX/UI мобильного приложения — от 120000₽. Логотип + брендбук (3 концепции) — 35000₽. Дизайн презентации до 20 слайдов — 15000₽. Дизайн-система (UI kit, компоненты, гайдлайны) — от 60000₽. Баннеры для соцсетей — 2000₽/шт. Срочность 48 часов +40%. Правки — 2 раунда включены

4. О себе:

Senior UX/UI дизайнер, 9 лет опыта. Ex-дизайнер Яндекс.Музыки и СберМаркета. Выпускница Британской Высшей Школы Дизайна. Работаю в Figma, использую дизайн-мышление и CJM. Постоянный спикер UX-конференций. Портфолио — более 60 реализованных проектов. Средний рейтинг на Behance — 95%

5. График:

Пн-Пт 10:00-19:00, Сб — по согласованию. Сроки: лендинг 5-7 дней, приложение 3-6 недель, логотип 7-10 дней

6. Контакты:

Telegram: @masha_design, Behance: behance.net/kozlova_ux, email: [email protected]

7. Портфолио:

https://behance.net/kozlova_ux

🧪 Тест:

Нужен дизайн мобильного приложения для доставки еды. Сколько по времени и стоимости?

🎨 Дизайнер — пример 2

1. Имя:

Артём Ильин

2. Направления:

Дизайн интерьеров жилых и коммерческих помещений, 3D-визуализация, авторский надзор, подбор мебели и материалов, комплектация объектов

3. Услуги и цены:

Дизайн-проект квартиры до 60м² (планировка, 3D-визуализация, чертежи, спецификация) — 120000₽. Квартира 60-120м² — 180000₽. Коммерческое помещение (офис, ресторан, магазин) — от 3000₽/м². 3D-визуализация одного ракурса — 5000₽. Авторский надзор за ремонтом — 30000₽/мес. Экспресс-консультация 1 час (выезд на объект) — 7000₽. Подбор мебели и комплектация — 10% от стоимости закупки

4. О себе:

Дизайнер интерьеров, 11 лет практики. Реализовал 80+ объектов в Москве и Подмосковье. Публикации в AD Russia, Salon Interior. Работаю в стилях: современная классика, минимализм, лофт, скандинавский. Использую 3ds Max, Corona Renderer, AutoCAD. Сотрудничаю с проверенными бригадами и поставщиками

5. График:

Пн-Пт 10:00-19:00, Сб 11:00-16:00. Выезд на объект — Москва и до 50 км от МКАД. Сроки: проект квартиры 4-6 недель

6. Контакты:

Телефон: +7-915-888-77-66, Telegram: @artem_interior, Instagram: @ilyin_design, сайт: ilyindesign.ru

7. Портфолио:

https://ilyindesign.ru/projects

🧪 Тест:

У меня двушка 55м² в новостройке. Хочу современную классику. Сколько стоит и что входит?

💅 Мастер маникюра — пример 1

Жмите 💅 Мастер маникюра:

1. Имя:

Кристина Белова

2. Услуги:

Маникюр классический, аппаратный и комбинированный, покрытие гель-лаком, укрепление ногтей, наращивание гелем и полигелем, дизайн ногтей любой сложности (стемпинг, слайдеры, аэрография, лепка), педикюр аппаратный, SPA-уход для рук

3. Цены:

Маникюр аппаратный + покрытие гель-лак — 2500₽. Маникюр + укрепление + покрытие — 3000₽. Наращивание гелем (до 3 см) — 4500₽. Наращивание полигелем — 4000₽. Коррекция наращивания — 3500₽. Дизайн простой (стемпинг, втирка) — 50₽/ноготь. Дизайн сложный (ручная роспись, лепка) — от 150₽/ноготь. Педикюр аппаратный + покрытие — 3000₽. SPA-уход для рук (пилинг, маска, массаж) — 1500₽. Комплекс маникюр + педикюр — 5000₽ (экономия 500₽). Снятие чужого покрытия — 500₽

4. О себе:

Мастер маникюра и nail-дизайнер, 6 лет опыта. Сертификаты: EMI School, ParisNail Academy. Призёр конкурса NailPro 2024 в номинации «Дизайн». Работаю на аппарате Marathon Champion, использую только премиум-материалы: Luxio, Komilfo, E.Mi. Строгая стерилизация: автоклав, одноразовые пилки и бафы. Мастер категории «Топ» на YouDo (4.9/5, 200+ отзывов)

5. График и адрес:

Пн-Сб 10:00-21:00, воскресенье — выходной. Студия у метро Чистые пруды, 2 мин пешком, ул. Покровка 12, подъезд 2, домофон 12. Бесплатный чай, кофе и сладости. Есть зарядки для телефона и Netflix на экране

6. Контакты:

Телефон: +7-977-444-55-66, Instagram: @kristina_nails_msk, Telegram: @kris_nails, WhatsApp: +7-977-444-55-66

7. Работы:

https://instagram.com/kristina_nails_msk

🧪 Тест:

Хочу наращивание с дизайном на свадьбу через 2 недели. Можно записаться?

💅 Мастер маникюра — пример 2

1. Имя:

Алина Хасанова

2. Услуги:

Маникюр и педикюр на выезд (еду к вам домой или в офис), аппаратный маникюр, покрытие гель-лаком, экспресс-маникюр за 40 минут, свадебный маникюр, мужской маникюр

3. Цены:

Маникюр аппаратный + покрытие — 2800₽ (включая выезд по Москве в пределах МКАД). Выезд за МКАД до 10 км — +500₽. Экспресс-маникюр 40 мин (без замачивания) — 2000₽. Педикюр + покрытие — 3200₽. Свадебный маникюр (2 пробных + день свадьбы) — 8000₽. Мужской маникюр (аппаратный, без покрытия) — 1500₽. Комплекс маникюр + педикюр — 5500₽. Дизайн: французский, лунный — +300₽, стразы и камни — от 100₽/ноготь. Привожу с собой все материалы и одноразовые инструменты

4. О себе:

Мобильный мастер маникюра, 4 года опыта. Обучалась в академии ParisNail и у мастера международного класса Екатерины Мирошниченко. Все инструменты стерилизуются в автоклаве, каждый клиент получает новый набор пилок. Привожу портативный аппарат Strong 210, LED-лампу 48W, полный набор гель-лаков (150+ цветов Luxio и CND). Работаю чисто и аккуратно, не тороплюсь

5. График и адрес:

Пн-Вс 09:00-22:00 (без выходных, по записи за день). Выезд по всей Москве и до 15 км от МКАД. Минимальное время бронирования — за 24 часа

6. Контакты:

Телефон: +7-964-222-33-44, Instagram: @alina_nails_home, Telegram: @alina_mobile_nails, WhatsApp: +7-964-222-33-44

7. Работы:

https://instagram.com/alina_nails_home

🧪 Тест:

Вы приезжаете в Химки? Сколько будет маникюр + педикюр?

🧠 Психолог / Коуч — пример 1

Жмите 🧠 Психолог / Коуч:

1. Имя:

Ирина Волошина

2. Запросы:

Тревожные расстройства, панические атаки, депрессия, проблемы в отношениях и семейные конфликты, выгорание и стресс на работе, потеря смысла жизни, низкая самооценка, горевание и утрата, психосоматика

3. Услуги и цены:

Индивидуальная консультация 50 мин (онлайн) — 4000₽. Индивидуальная консультация 50 мин (офлайн) — 5000₽. Семейная/парная терапия 80 мин — 7000₽. Пакет 4 индивидуальные сессии — 14000₽ (экономия 2000₽). Пакет 8 сессий — 26000₽ (экономия 6000₽). Экстренная сессия (в тот же день) — 6000₽. Первая ознакомительная сессия 30 мин — 2000₽. Групповая терапия (6-8 чел, 2 часа) — 2500₽/чел

4. О себе:

Клинический психолог, 13 лет практики. Образование: МГУ, факультет психологии + магистратура по клинической психологии. Сертифицированный КПТ-терапевт (Beck Institute). Обучение по схема-терапии и EMDR. Член Российского Психологического Общества. Постоянно повышаю квалификацию: 200+ часов супервизии ежегодно. Публикации в журнале «Психология сегодня». Работаю на русском и английском языках

5. График:

Пн-Пт 10:00-20:00, Сб 11:00-16:00. Онлайн (Zoom, Telegram видео) и офлайн (кабинет у м. Третьяковская, Москва)

6. Контакты:

Telegram: @irina_psy, email: [email protected], сайт: voloshina-psy.ru

7. Публикации:

https://voloshina-psy.ru/articles

🧪 Тест:

У меня сильная тревожность и панические атаки. Какой подход вы используете?

🧠 Психолог / Коуч — пример 2

1. Имя:

Максим Дорохов

2. Запросы:

Карьерный и бизнес-коучинг, выход из профессионального кризиса, развитие лидерских качеств, управление командой, work-life balance, постановка и достижение целей, подготовка к переговорам и публичным выступлениям

3. Услуги и цены:

Индивидуальная коуч-сессия 60 мин — 5000₽. Стратегическая сессия 2 часа (глубокий разбор ситуации, план действий) — 12000₽. Пакет «Трансформация» 10 сессий за 2.5 месяца — 40000₽. Экспресс-коучинг 30 мин (один конкретный вопрос) — 3000₽. Групповой коучинг для команды (до 10 чел, 3 часа) — 30000₽. VIP-пакет: 10 сессий + доступ в Telegram-чат для вопросов между сессиями + 2 экстренных звонка — 70000₽. Бесплатный ознакомительный звонок 15 мин

4. О себе:

Сертифицированный коуч ICF (PCC level). MBA Сколково. 8 лет опыта коучинга, до этого 10 лет в топ-менеджменте (директор по развитию в Lamoda, руководитель направления в McKinsey). Провёл 2000+ сессий. Клиенты: CEO и топ-менеджеры из Forbes 500 Russia. Автор книги «Лидер без выгорания». Спикер конференций TEDx, Сколково Startup Village

5. График:

Пн-Пт 08:00-20:00, Сб 10:00-14:00. Только онлайн (Zoom). Часовой пояс любой — работаю с клиентами из России, Европы, ОАЭ

6. Контакты:

Telegram: @dorokhov_coach, LinkedIn: linkedin.com/in/dorokhov, email: [email protected], сайт: dorokhov-coaching.com

7. Отзывы:

https://dorokhov-coaching.com/testimonials

🧪 Тест:

Хочу уволиться из найма и запустить свой бизнес, но страшно. Можете помочь?

💻 Разработчик — пример 1

Жмите 💻 Разработчик:

1. Имя:

Игорь Сотников

2. Технологии:

Telegram-боты и Mini Apps, AI-интеграции (OpenAI, Claude, OpenRouter), автоматизация бизнес-процессов, парсинг и мониторинг данных, разработка CRM и внутренних инструментов на Python

3. Услуги и цены:

Telegram-бот простой (до 10 команд, без AI) — 15000₽. Telegram-бот с AI (OpenRouter/GPT) — от 30000₽. Telegram Mini App — от 50000₽. Бот-магазин с каталогом и оплатой — 45000₽. Автоматизация (парсер, уведомления, интеграция API) — от 20000₽. Интеграция AI в существующий проект — от 25000₽. CRM/панель управления на FastAPI + React — от 80000₽. Часовая консультация по архитектуре — 5000₽. Поддержка и обновления — от 10000₽/мес. Срочность (дедлайн до 3 дней) — +50%

4. О себе:

Full-stack Python разработчик, 7 лет опыта. Стек: Python, FastAPI, aiogram, aiohttp, PostgreSQL, Redis, Docker, React. Реализовал 100+ проектов на фрилансе. Постоянный разработчик для 3 digital-агентств. Опыт с нагрузкой: боты обслуживающие 50K+ пользователей. Открытые проекты на GitHub (2K+ звёзд). Участник хакатонов: победитель Telegram Bot Hackathon 2024

5. График:

Пн-Пт 10:00-22:00, Сб 12:00-18:00. Сроки: простой бот 3-5 дней, сложный проект 2-4 недели. Связь в Telegram — отвечаю в течение часа

6. Контакты:

Telegram: @igor_dev, GitHub: github.com/sotnikov-dev, email: [email protected], сайт: sotnikov.dev

7. Портфолио:

https://github.com/sotnikov-dev

🧪 Тест:

Нужен Telegram-бот для ресторана: меню, заказ, оплата через Stars. Сколько и как быстро?

💻 Разработчик — пример 2

1. Имя:

Вера Антонова

2. Технологии:

Мобильные приложения на Flutter (iOS + Android), разработка сайтов и веб-приложений на Next.js, интернет-магазины, интеграция с платёжными системами (ЮKassa, Stripe, Telegram Stars), UI/UX реализация по макетам из Figma

3. Услуги и цены:

Мобильное приложение на Flutter (обе платформы) — от 150000₽. Лендинг на Next.js — от 25000₽. Многостраничный сайт с CMS — от 60000₽. Интернет-магазин (каталог, корзина, оплата) — от 120000₽. Вёрстка по макету Figma (1 страница) — 8000₽. Интеграция платёжной системы — от 15000₽. Публикация приложения в App Store и Google Play — 10000₽ (включая скриншоты и описания). Техподдержка и обновления — от 15000₽/мес. Пробная консультация 30 мин — бесплатно

4. О себе:

Mobile & Web разработчик, 6 лет опыта. Стек: Flutter/Dart, Next.js, TypeScript, Node.js, Firebase, Supabase, Figma. Реализовала 40+ мобильных приложений, 8 из них в топ-100 App Store в своих категориях. Ex-разработчик в Тинькофф (мобильная команда). Выпускница МФТИ. Пишу чистый, тестируемый код с CI/CD. Все проекты с документацией и передачей

5. График:

Пн-Пт 10:00-20:00. Сроки: лендинг 5-7 дней, мобильное приложение 1-3 месяца. Работаю удалённо, часовой пояс UTC+3

6. Контакты:

Telegram: @vera_dev, GitHub: github.com/vera-antonova, email: [email protected], LinkedIn: linkedin.com/in/vera-antonova

7. Портфолио:

https://github.com/vera-antonova

🧪 Тест:

Нужно мобильное приложение для фитнес-клуба с расписанием и оплатой. За сколько сделаете?

Идеи что делать дальше

💰

Монетизация через Stars

Продавайте визитки за Telegram Stars. Бесплатный бот с ограничениями, премиум — за 50 Stars.

🌍

Мультиязычность

AI уже отвечает на языке клиента. Добавьте перевод кнопок и приветствия.

📊

Расширенная аналитика

Добавьте графики посещений, конверсию в заявки, воронку продаж для каждого бота.

🗓️

Онлайн-запись

Интеграция с календарём: клиент выбирает свободный слот, мастер получает уведомление.

🤖

Новые шаблоны

Добавьте ещё профессий: адвокат, стоматолог, фитнес-тренер, свадебный организатор.

🔗

Партнёрская программа

Каждый созданный бот рекомендует вашу фабрику. Вирусный рост.