Как стать автором
Обновить

Telegram‑бот на Supabase

Уровень сложностиСредний
Время на прочтение4 мин
Количество просмотров367

Немного предыстории

Месяц назад я баловался с сервисом Lovable — мне показался прикольным инструментом, а главное для нашей истории это то, что он предложил мне настроить БД на Supabase с помощью пары кликов: база Postgres уже крутится, таблицы создаются, в админке всё прозрачно и, главное, что все это бесплатно в пределах небольших квот.

Пока щёлкал UI заметил раздел Functions. «Что‑то похожее на AWS Lambda, только прямо рядом с базой» - подумал я. И так в "долгом ящике" появилась идея потыкать в эти функции.

И тут несколько дней назад релиз OpenAI Images API. Мы как-то обсуждали со знакомым разработчиком, что прикольно было бы сделать бота в телеге для ру пользователей, так как их доступ до ChatGPT-plus ограничен. Вспомнил про Supabase → идеальный шанс пощупать edge‑функции подняв простого бота.

Как обычно для себя, накидал мысли по тому, как должен работать бот и на чем, и попросил ChatGPT написать первые черновики ТЗ. Несколько правок и плюс-минус полное описание задачи было на руках. Затем пошёл в Claude через OpenRouter — он выплюнул скелет кода под Supabase edge functions. Имея на руках написанный код я пошел его править локально и запускать постепенно погружаясь в написание функций.

Бот можно потыкать здесь, а если вам интересно, чем занимается аналитик данных в свободное время, то можно еще и на мой канал подписаться

Архитектура решения

Почему именно Supabase

  1. Edge Functions на Deno — супер легкий деплой supabase functions deploy bot и готово.

  2. Знакомый мне Postgres — из коробки policies + row‑level security.

  3. Есть встроенный Storage S3— если решу кешировать картинки.

  4. Бесплатный план тянет 500 тыс. Edge‑инвокаций / 10 ГБ‑трафика — на хобби‑бот за глаза хватает.

Логическая модель

logic model
logic model

Таблица

Назначение

users

учёт TG‑пользователей

channel_subscribers

подписка на промо‑канал

sessions

FSM (finite state machine — конечный автомат)‑состояние бота

generations

лог картинок

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

Edge Function bot

  • URL https://xyz.supabase.co/functions/v1/bot/<WEBHOOK_SECRET> — Именно туда Telegram «постучится» методом POST, передавая JSON-объект TelegramUpdate с сообщениями, фото, callback-кнопками и т.д.

  • Проверяем req.method === 'POST' и секрет в path.

  • Парсим TelegramUpdate, роутим в states.ts.

Состояния бота

states.ts

WAITING_PHOTO ─▶ WAITING_PROMPT ─▶ GENERATING ─▶ WAITING_PHOTO
            └────────── SUBSCRIPTION_OFFER

FSM (finite state machine) хранится в sessions — одна строка на пользователя.

Глубже в код

1. database.ts

Создаём Supabase‑клиент:

const supabase = createClient(supabaseUrl, supabaseKey);

и дальше поехали обрабатывать запросы

  • getUser / createUser / updateUserInteraction .

  • RPC (Remote Procedure Call) через supabase.rpc('decrease_generation_count') — дёргает за функцию DB, чтобы декрементировать счётчик.

  • Проверка подписки: сначала смотрим локальную таблицу, если же пользователя там нет, то стоит еще посмотреть подписку пользователя прямо в телеге через методgetChatMember. Если пользователь подписчик моего замечательного канала, то он на этом шаге автоматически прорастет в таблицу с подписками.

2. states.ts — бизнес‑логика

  • /start приветствует и создаёт пользователя.

  • handlePhoto — сохраняет photo_url и переводит в WAITING_PROMPT.

  • handleCallbackQuery — меню стилей + проверка подписки.

  • handlePromptWithPhoto — главное блюдо: скачиваем фото, вызываем generateImageBytes, шлём sendPhoto, минусуем фрипасс.

3. openai.ts - Интеграция с OpenAI

// 1. скачиваем картинку
const res = await fetch(srcUrl);
if (!res.ok) throw new Error("Failed to fetch source image");
const blob = await res.blob();

// 2. определяем расширение и MIME
const ext  = srcUrl.match(/\.(png|jpe?g|webp)$/i)?.[1]?.toLowerCase() ?? "png";
const mime = ext.startsWith("jp") ? "image/jpeg" :
             ext === "webp"      ? "image/webp"  : "image/png";

// 3. превращаем Blob в File (openai ждёт File | Buffer)
const file = new File([blob], `source.${ext}`, { type: mime });

// 4. вызываем Images API
const { data } = await openai.images.edit({
  model  : 'gpt-image-1',
  image  : file,
  prompt,
  n      : 1,
  size   : '1024x1024',
  quality: 'low',
});

// 5. декодируем base64
const bytes = Uint8Array.from(atob(data[0].b64_json!), c => c.charCodeAt(0));

Что происходит выше

  1. fetch → blob — превращаем URL в бинарные данные, чтобы можно было переслать файл целиком (Images API не принимает внешние URL, ну или я слепой и не нашел).

  2. ext / mime — по имени файла решаем, какой Content‑Type передать OpenAI. Изображения допускаются только четырёх форматов: PNG, JPEG/JPG, WEBP.

  3. File() — OpenAI SDK в Deno/Node ждёт File или Buffer; обычный Blob не подойдёт.

  4. openai.images.edit() — ключевые параметры:

    • model: сейчас единственная модель — gpt-image-1.

    • image: исходный File.

    • prompt: текст‑инструкция.

    • n: количество вариантов (1‑10).

    • size: '256x256' | '512x512' | '1024x1024'.

    • quality: 'low' | 'standard' | 'hd' — влияет на цену и скорость.

  5. base64 → Uint8Array — Telegram API принимает файл как FormData, поэтому нужно превратить строку в сырые байты.

На выходе получаем b64_json, декодируем в Uint8Array, кидаем обратно в Telegram.

4. Деплой

Тут все просто

supabase functions serve bot --env-file .env --no-verify-jwt  # локально (отключаем JWT)
supabase functions deploy bot --no-verify-jwt                       # прод  (бот авторизуется по секрету в URL)

И не забываем зарегистрировать hook:

curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook?url=https://xyz.supabase.co/functions/v1/bot/<WEBHOOK_SECRET>"

Подводные камни

JWT‑проверка

По умолчанию Edge Functions требуют валидный JWT в заголовке Authorization и отвечают 401 Unauthorized, если его нет. Для веб‑хуков (Stripe, Telegram, Viber) Supabase предлагает опцию отключить проверку:

# локально
supabase functions serve bot --no-verify-jwt
# деплой
supabase functions deploy bot --no-verify-jwt

Используйте эту «дырку» только вместе с собственным секретом в URL (как /<WEBHOOK_SECRET>), иначе функцию сможет дернуть кто угодно.

Итоги

За пару часов на Supabase я собрал полностью безсерверный Telegram‑бот на Supabase function, и всё заработало без танцев с VPS и Docker.

Качество генерации установил на "low", так как с medium особой разницы не заметил, а вот жрет токенов в 4 раза больше, по этой причине качество генерации очень ОЧЕНЬ страдает

Вот примеры

Low
Low



medium
medium
hight
hight

Можно конечно еще и платные генерации сделать за деньги, но нужны ли они кому нибудь будут я не уверен.

Если кому нужен код, то там ничего интересного, но посмотреть можно здесь

Теги:
Хабы:
0
Комментарии0

Публикации

Работа

Ближайшие события