Telegram Bot API 9.6 + OpenRouter + один Python-файл =
неограниченное количество персональных ботов-визиток
с AI-аватарками, CRM, QR-кодами и автоответчиком
Бот-визитка заменяет лендинг
Один менеджер-бот, через которого любой пользователь за 2 минуты создаёт персонального бота-визитку. Бот заменяет лендинг.
AI-аватарка, фото-визитка, кнопки «Услуги», «Цены», «Записаться», мини-CRM с заявками, QR-код, статистика, автоперевод, админка для редактирования.
Telegram Bot API 9.6 (Managed Bots)
+ OpenRouter (Gemini 2.5 Flash)
+ SQLite
+ Один файл, один VPS
Любой VPS с Ubuntu 22.04+. Подойдёт самый дешёвый тариф.
Рекомендуем: Beget VPS от 210₽/мес — быстрый, русскоязычная
поддержка, оплата из РФ.
Для создания бота-менеджера и включения функции Managed Bots.
Единый 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₽ на первый месяц.
Каждая команда копируется одним кликом. Вставляй в терминал и работает с первого раза.
/newbot7123456789:AAF.../mybots → выберите бота → Bot Settings →
Managed Bots → EnableManaged Bots: OnЗапишите токен — он понадобится в шаге 6.
sk-or-v1-...)Запишите ключ — он понадобится в шаге 6.
Подключитесь через PowerShell:
ssh root@твой_IP
Выполните по одной команде:
apt update && apt upgrade -y
apt install python3.12-venv python3-pip -y
python3 -m venv упадёт с ошибкой
ensurepip is not available.
mkdir ~/vizitka-factory && cd ~/vizitka-factory
python3 -m venv venv
source venv/bin/activate
Должно появиться (venv) в начале строки. Далее:
pip install aiohttp qrcode Pillow
Дождитесь Successfully installed...
cat.
Это исключает ошибки при редактировании и проблемы с сохранением.
Просто скопируйте всё целиком от cat до ENDOFFILE
и вставьте в терминал.
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
(venv) root@...). Это нормально!ENDOFFILE.(venv) root@...:~/vizitka-factory#Проверка создания файла:
ls -lh bot.py
Должны увидеть: -rw-r--r-- 1 root root ~22K ... bot.py
Enter ещё раз. Терминал ждёт завершения ввода.> или >>> в начале строкиCtrl+C для отмены
и вставьте команду заново целиком.ls: cannot access 'bot.py')ENDOFFILE.Ctrl+C → rm bot.py → вставьте заново
grep "MANAGER_BOT_TOKEN\|OPENROUTER_API_KEY" bot.py | head -2
Должны быть ваши реальные токены без слова ВСТАВЬ.
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).
Откройте менеджер-бота в Telegram и следуйте инструкциям:
/mybots — список ботов/edit — редактировать тексты, фото, аватарку/leads — заявки клиентов/stats — статистика посещений/qr — QR-код повторно
Ниже в секции «Тестируем» — готовые тексты для каждого из 6 шаблонов. Просто копируйте и вставляйте!
Создаём systemd сервис:
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
Запускаем сервис:
systemctl daemon-reload && systemctl enable vizitka-factory && systemctl start vizitka-factory
Полезные команды:
journalctl -u vizitka-factory -f
systemctl restart vizitka-factory
Откройте менеджер-бота в Telegram, нажмите /start и пройдите все 6 шаблонов.
Для каждого — два готовых набора ответов. Отправляйте по одному сообщению.
Жмите 📸 Фотограф, отправляйте по одному:
Анна Светлова
Свадебная съёмка, love-story, семейные фотосессии, портретная съёмка в студии и на улице, предметная фотография для маркетплейсов, репортажная съёмка мероприятий
Свадебная съёмка полный день (10 часов, 500+ фото, обработка) — 45000₽. Love-story 2 часа (100+ фото, обработка) — 8000₽. Семейная фотосессия 1.5 часа (80+ фото) — 7000₽. Портретная съёмка в студии 1 час (30+ фото) — 5000₽. Предметная съёмка для маркетплейсов от 300₽/карточка (минимум 10 штук). Репортаж мероприятия от 3 часов — 15000₽. Срочная обработка +30%. Выезд за МКАД +1000₽/час дороги
Профессиональный фотограф, 8 лет опыта. Работаю в стиле fine art и репортаж. Снимала для журналов Elle и Cosmopolitan. Выпускница Школы Родченко. Призёр фотоконкурса «Золотая Камера 2024». Работаю на Canon R5 и Sony A7IV. Имею собственную студию 80м² в центре Москвы с 3 фонами и профессиональным светом
Пн-Сб 09:00-21:00, воскресенье — только свадьбы по предварительной записи. Студийная съёмка — в любой день по записи
Телефон: +7-926-555-12-34, Instagram: @anna_svetlova_photo, Telegram: @anna_photo, сайт: annasvetlova.ru
https://annasvetlova.ru/portfolio
Сколько стоит свадебная съёмка? И можно на выходных?
Тот же шаблон, другой профиль:
Дмитрий Волков
Коммерческая фотография, съёмка интерьеров и архитектуры, фуд-фотография для ресторанов и кафе, каталожная съёмка одежды, аэросъёмка с дрона
Съёмка интерьера квартиры до 100м² (30+ фото, обработка, виртуальный тур) — 25000₽. Фуд-фотография для меню ресторана от 500₽/блюдо (минимум 20 позиций). Каталожная съёмка одежды на модели — 2000₽/образ. Аэросъёмка с дрона DJI Mavic 3 — 15000₽/час. Съёмка коммерческой недвижимости от 35000₽. Пакет для маркетплейсов: 50 карточек с инфографикой — 25000₽. Срочность 24 часа +50%
Коммерческий фотограф, 12 лет в индустрии. Постоянный фотограф сети ресторанов White Rabbit Family. Снимал интерьеры для AD Russia и Elle Decoration. Сертифицированный пилот дрона. Работаю с крупными брендами: IKEA, Leroy Merlin, Lamoda. Оборудование: Phase One IQ4, DJI Mavic 3 Pro, полный комплект студийного света Profoto
Пн-Пт 08:00-20:00, Сб 10:00-18:00, воскресенье — выходной. Выезд на объект в любой день по согласованию
Телефон: +7-903-777-88-99, Instagram: @volkov_commercial, Telegram: @dvolkov_photo, email: [email protected]
https://behance.net/volkov_photo
Нужна съёмка меню для ресторана, 30 блюд. Сколько по времени и цене?
Жмите 📚 Репетитор:
Елена Маркова
Математика и физика для школьников 7-11 класс, подготовка к ОГЭ и ЕГЭ, олимпиадная математика, высшая математика для студентов 1-2 курса (матанализ, линейная алгебра, теория вероятностей)
Индивидуальное занятие 60 мин (7-9 класс) — 2000₽. Индивидуальное занятие 90 мин (10-11 класс) — 3000₽. Подготовка к ЕГЭ (90 мин, индивидуально) — 3500₽. Подготовка к ОГЭ (60 мин) — 2500₽. Олимпиадная математика (90 мин) — 4000₽. Высшая математика для студентов (90 мин) — 3500₽. Мини-группа 2-3 человека — скидка 30% каждому. Пробное занятие (30 мин) — бесплатно. Пакет 8 занятий — скидка 10%
Кандидат физико-математических наук, доцент МГУ. 15 лет преподавательского опыта. Средний балл учеников на ЕГЭ — 86 из 100. 12 учеников стали призёрами Всероссийской олимпиады. Автор учебного пособия «Математика без страха». Член жюри городских олимпиад. Использую интерактивные доски и собственные тренажёры
Пн-Пт 15:00-21:00, Сб 10:00-18:00, воскресенье — выходной. Онлайн (Zoom) и офлайн (м. Университет, 5 мин пешком)
Телефон: +7-916-333-44-55, Telegram: @elena_math, email: [email protected]
https://profi.ru/profile/MarkovaEV
Дочка в 10 классе, нужна подготовка к ЕГЭ по математике. Можно онлайн по субботам?
Александр Петренко
Английский язык: разговорный, бизнес-английский, подготовка к IELTS/TOEFL/Cambridge, английский для IT-специалистов, корпоративное обучение для компаний
Индивидуальное занятие 60 мин (общий курс) — 2500₽. Бизнес-английский 60 мин — 3000₽. Подготовка к IELTS/TOEFL (90 мин) — 4000₽. Английский для IT (60 мин) — 3000₽. Разговорный клуб мини-группа (90 мин, 4-6 чел) — 1000₽/чел. Корпоративное обучение для компаний — от 5000₽/час. Интенсив «С нуля до B1 за 3 месяца» — 80000₽ (48 занятий). Пробное занятие 30 мин — бесплатно
Сертификат CELTA (Cambridge), IELTS 8.5. Жил и работал в Лондоне 4 года. 10 лет опыта преподавания. Средний результат учеников IELTS — 7.5. Обучал сотрудников Яндекса, Сбера и Тинькофф. Использую коммуникативную методику, без зубрёжки — только живой язык. Все материалы предоставляю бесплатно
Пн-Сб 09:00-22:00, воскресенье 12:00-18:00. Только онлайн (Zoom, Google Meet). Часовой пояс UTC+3 (Москва)
Телефон: +7-905-111-22-33, Telegram: @alex_english_pro, Instagram: @petrenko_english, сайт: alexenglish.ru
https://alexenglish.ru/reviews
Мне нужен IELTS 7.0 через 3 месяца. Реально? Сколько занятий в неделю?
Жмите 🎨 Дизайнер:
Мария Козлова
UX/UI дизайн мобильных приложений и сайтов, веб-дизайн, брендинг и айдентика, дизайн презентаций для бизнеса, дизайн-системы для продуктовых команд
Дизайн лендинга (до 10 экранов, адаптив) — 40000₽. Дизайн многостраничного сайта — от 80000₽. UX/UI мобильного приложения — от 120000₽. Логотип + брендбук (3 концепции) — 35000₽. Дизайн презентации до 20 слайдов — 15000₽. Дизайн-система (UI kit, компоненты, гайдлайны) — от 60000₽. Баннеры для соцсетей — 2000₽/шт. Срочность 48 часов +40%. Правки — 2 раунда включены
Senior UX/UI дизайнер, 9 лет опыта. Ex-дизайнер Яндекс.Музыки и СберМаркета. Выпускница Британской Высшей Школы Дизайна. Работаю в Figma, использую дизайн-мышление и CJM. Постоянный спикер UX-конференций. Портфолио — более 60 реализованных проектов. Средний рейтинг на Behance — 95%
Пн-Пт 10:00-19:00, Сб — по согласованию. Сроки: лендинг 5-7 дней, приложение 3-6 недель, логотип 7-10 дней
Telegram: @masha_design, Behance: behance.net/kozlova_ux, email: [email protected]
https://behance.net/kozlova_ux
Нужен дизайн мобильного приложения для доставки еды. Сколько по времени и стоимости?
Артём Ильин
Дизайн интерьеров жилых и коммерческих помещений, 3D-визуализация, авторский надзор, подбор мебели и материалов, комплектация объектов
Дизайн-проект квартиры до 60м² (планировка, 3D-визуализация, чертежи, спецификация) — 120000₽. Квартира 60-120м² — 180000₽. Коммерческое помещение (офис, ресторан, магазин) — от 3000₽/м². 3D-визуализация одного ракурса — 5000₽. Авторский надзор за ремонтом — 30000₽/мес. Экспресс-консультация 1 час (выезд на объект) — 7000₽. Подбор мебели и комплектация — 10% от стоимости закупки
Дизайнер интерьеров, 11 лет практики. Реализовал 80+ объектов в Москве и Подмосковье. Публикации в AD Russia, Salon Interior. Работаю в стилях: современная классика, минимализм, лофт, скандинавский. Использую 3ds Max, Corona Renderer, AutoCAD. Сотрудничаю с проверенными бригадами и поставщиками
Пн-Пт 10:00-19:00, Сб 11:00-16:00. Выезд на объект — Москва и до 50 км от МКАД. Сроки: проект квартиры 4-6 недель
Телефон: +7-915-888-77-66, Telegram: @artem_interior, Instagram: @ilyin_design, сайт: ilyindesign.ru
https://ilyindesign.ru/projects
У меня двушка 55м² в новостройке. Хочу современную классику. Сколько стоит и что входит?
Жмите 💅 Мастер маникюра:
Кристина Белова
Маникюр классический, аппаратный и комбинированный, покрытие гель-лаком, укрепление ногтей, наращивание гелем и полигелем, дизайн ногтей любой сложности (стемпинг, слайдеры, аэрография, лепка), педикюр аппаратный, SPA-уход для рук
Маникюр аппаратный + покрытие гель-лак — 2500₽. Маникюр + укрепление + покрытие — 3000₽. Наращивание гелем (до 3 см) — 4500₽. Наращивание полигелем — 4000₽. Коррекция наращивания — 3500₽. Дизайн простой (стемпинг, втирка) — 50₽/ноготь. Дизайн сложный (ручная роспись, лепка) — от 150₽/ноготь. Педикюр аппаратный + покрытие — 3000₽. SPA-уход для рук (пилинг, маска, массаж) — 1500₽. Комплекс маникюр + педикюр — 5000₽ (экономия 500₽). Снятие чужого покрытия — 500₽
Мастер маникюра и nail-дизайнер, 6 лет опыта. Сертификаты: EMI School, ParisNail Academy. Призёр конкурса NailPro 2024 в номинации «Дизайн». Работаю на аппарате Marathon Champion, использую только премиум-материалы: Luxio, Komilfo, E.Mi. Строгая стерилизация: автоклав, одноразовые пилки и бафы. Мастер категории «Топ» на YouDo (4.9/5, 200+ отзывов)
Пн-Сб 10:00-21:00, воскресенье — выходной. Студия у метро Чистые пруды, 2 мин пешком, ул. Покровка 12, подъезд 2, домофон 12. Бесплатный чай, кофе и сладости. Есть зарядки для телефона и Netflix на экране
Телефон: +7-977-444-55-66, Instagram: @kristina_nails_msk, Telegram: @kris_nails, WhatsApp: +7-977-444-55-66
https://instagram.com/kristina_nails_msk
Хочу наращивание с дизайном на свадьбу через 2 недели. Можно записаться?
Алина Хасанова
Маникюр и педикюр на выезд (еду к вам домой или в офис), аппаратный маникюр, покрытие гель-лаком, экспресс-маникюр за 40 минут, свадебный маникюр, мужской маникюр
Маникюр аппаратный + покрытие — 2800₽ (включая выезд по Москве в пределах МКАД). Выезд за МКАД до 10 км — +500₽. Экспресс-маникюр 40 мин (без замачивания) — 2000₽. Педикюр + покрытие — 3200₽. Свадебный маникюр (2 пробных + день свадьбы) — 8000₽. Мужской маникюр (аппаратный, без покрытия) — 1500₽. Комплекс маникюр + педикюр — 5500₽. Дизайн: французский, лунный — +300₽, стразы и камни — от 100₽/ноготь. Привожу с собой все материалы и одноразовые инструменты
Мобильный мастер маникюра, 4 года опыта. Обучалась в академии ParisNail и у мастера международного класса Екатерины Мирошниченко. Все инструменты стерилизуются в автоклаве, каждый клиент получает новый набор пилок. Привожу портативный аппарат Strong 210, LED-лампу 48W, полный набор гель-лаков (150+ цветов Luxio и CND). Работаю чисто и аккуратно, не тороплюсь
Пн-Вс 09:00-22:00 (без выходных, по записи за день). Выезд по всей Москве и до 15 км от МКАД. Минимальное время бронирования — за 24 часа
Телефон: +7-964-222-33-44, Instagram: @alina_nails_home, Telegram: @alina_mobile_nails, WhatsApp: +7-964-222-33-44
https://instagram.com/alina_nails_home
Вы приезжаете в Химки? Сколько будет маникюр + педикюр?
Жмите 🧠 Психолог / Коуч:
Ирина Волошина
Тревожные расстройства, панические атаки, депрессия, проблемы в отношениях и семейные конфликты, выгорание и стресс на работе, потеря смысла жизни, низкая самооценка, горевание и утрата, психосоматика
Индивидуальная консультация 50 мин (онлайн) — 4000₽. Индивидуальная консультация 50 мин (офлайн) — 5000₽. Семейная/парная терапия 80 мин — 7000₽. Пакет 4 индивидуальные сессии — 14000₽ (экономия 2000₽). Пакет 8 сессий — 26000₽ (экономия 6000₽). Экстренная сессия (в тот же день) — 6000₽. Первая ознакомительная сессия 30 мин — 2000₽. Групповая терапия (6-8 чел, 2 часа) — 2500₽/чел
Клинический психолог, 13 лет практики. Образование: МГУ, факультет психологии + магистратура по клинической психологии. Сертифицированный КПТ-терапевт (Beck Institute). Обучение по схема-терапии и EMDR. Член Российского Психологического Общества. Постоянно повышаю квалификацию: 200+ часов супервизии ежегодно. Публикации в журнале «Психология сегодня». Работаю на русском и английском языках
Пн-Пт 10:00-20:00, Сб 11:00-16:00. Онлайн (Zoom, Telegram видео) и офлайн (кабинет у м. Третьяковская, Москва)
Telegram: @irina_psy, email: [email protected], сайт: voloshina-psy.ru
https://voloshina-psy.ru/articles
У меня сильная тревожность и панические атаки. Какой подход вы используете?
Максим Дорохов
Карьерный и бизнес-коучинг, выход из профессионального кризиса, развитие лидерских качеств, управление командой, work-life balance, постановка и достижение целей, подготовка к переговорам и публичным выступлениям
Индивидуальная коуч-сессия 60 мин — 5000₽. Стратегическая сессия 2 часа (глубокий разбор ситуации, план действий) — 12000₽. Пакет «Трансформация» 10 сессий за 2.5 месяца — 40000₽. Экспресс-коучинг 30 мин (один конкретный вопрос) — 3000₽. Групповой коучинг для команды (до 10 чел, 3 часа) — 30000₽. VIP-пакет: 10 сессий + доступ в Telegram-чат для вопросов между сессиями + 2 экстренных звонка — 70000₽. Бесплатный ознакомительный звонок 15 мин
Сертифицированный коуч ICF (PCC level). MBA Сколково. 8 лет опыта коучинга, до этого 10 лет в топ-менеджменте (директор по развитию в Lamoda, руководитель направления в McKinsey). Провёл 2000+ сессий. Клиенты: CEO и топ-менеджеры из Forbes 500 Russia. Автор книги «Лидер без выгорания». Спикер конференций TEDx, Сколково Startup Village
Пн-Пт 08:00-20:00, Сб 10:00-14:00. Только онлайн (Zoom). Часовой пояс любой — работаю с клиентами из России, Европы, ОАЭ
Telegram: @dorokhov_coach, LinkedIn: linkedin.com/in/dorokhov, email: [email protected], сайт: dorokhov-coaching.com
https://dorokhov-coaching.com/testimonials
Хочу уволиться из найма и запустить свой бизнес, но страшно. Можете помочь?
Жмите 💻 Разработчик:
Игорь Сотников
Telegram-боты и Mini Apps, AI-интеграции (OpenAI, Claude, OpenRouter), автоматизация бизнес-процессов, парсинг и мониторинг данных, разработка CRM и внутренних инструментов на Python
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%
Full-stack Python разработчик, 7 лет опыта. Стек: Python, FastAPI, aiogram, aiohttp, PostgreSQL, Redis, Docker, React. Реализовал 100+ проектов на фрилансе. Постоянный разработчик для 3 digital-агентств. Опыт с нагрузкой: боты обслуживающие 50K+ пользователей. Открытые проекты на GitHub (2K+ звёзд). Участник хакатонов: победитель Telegram Bot Hackathon 2024
Пн-Пт 10:00-22:00, Сб 12:00-18:00. Сроки: простой бот 3-5 дней, сложный проект 2-4 недели. Связь в Telegram — отвечаю в течение часа
Telegram: @igor_dev, GitHub: github.com/sotnikov-dev, email: [email protected], сайт: sotnikov.dev
https://github.com/sotnikov-dev
Нужен Telegram-бот для ресторана: меню, заказ, оплата через Stars. Сколько и как быстро?
Вера Антонова
Мобильные приложения на Flutter (iOS + Android), разработка сайтов и веб-приложений на Next.js, интернет-магазины, интеграция с платёжными системами (ЮKassa, Stripe, Telegram Stars), UI/UX реализация по макетам из Figma
Мобильное приложение на Flutter (обе платформы) — от 150000₽. Лендинг на Next.js — от 25000₽. Многостраничный сайт с CMS — от 60000₽. Интернет-магазин (каталог, корзина, оплата) — от 120000₽. Вёрстка по макету Figma (1 страница) — 8000₽. Интеграция платёжной системы — от 15000₽. Публикация приложения в App Store и Google Play — 10000₽ (включая скриншоты и описания). Техподдержка и обновления — от 15000₽/мес. Пробная консультация 30 мин — бесплатно
Mobile & Web разработчик, 6 лет опыта. Стек: Flutter/Dart, Next.js, TypeScript, Node.js, Firebase, Supabase, Figma. Реализовала 40+ мобильных приложений, 8 из них в топ-100 App Store в своих категориях. Ex-разработчик в Тинькофф (мобильная команда). Выпускница МФТИ. Пишу чистый, тестируемый код с CI/CD. Все проекты с документацией и передачей
Пн-Пт 10:00-20:00. Сроки: лендинг 5-7 дней, мобильное приложение 1-3 месяца. Работаю удалённо, часовой пояс UTC+3
Telegram: @vera_dev, GitHub: github.com/vera-antonova, email: [email protected], LinkedIn: linkedin.com/in/vera-antonova
https://github.com/vera-antonova
Нужно мобильное приложение для фитнес-клуба с расписанием и оплатой. За сколько сделаете?
Продавайте визитки за Telegram Stars. Бесплатный бот с ограничениями, премиум — за 50 Stars.
AI уже отвечает на языке клиента. Добавьте перевод кнопок и приветствия.
Добавьте графики посещений, конверсию в заявки, воронку продаж для каждого бота.
Интеграция с календарём: клиент выбирает свободный слот, мастер получает уведомление.
Добавьте ещё профессий: адвокат, стоматолог, фитнес-тренер, свадебный организатор.
Каждый созданный бот рекомендует вашу фабрику. Вирусный рост.