Свой Telegram-бот на Claude или GPT: минимальный пример
Собираю минимального Telegram-бота на aiogram, который дергает Claude или GPT через шлюз. Полный код, плюс что делать с ошибками баланса и лимитов прямо в чате.
3 мин · · Обновлено · разработка
Ниже — минимальный, но рабочий телеграм-бот на aiogram, который пересылает сообщение пользователя в Claude или GPT через шлюз и отвечает текстом. Свой бот в Telegram — это отдельная история от бонуса за подписку на канал @zukkopro: там вы привязываете свой Telegram к аккаунту в кабинете zukko.pro и один раз получаете $0.50, а этого бота вы пишете сами и он использует ваш API-ключ для генерации ответов.
Установка и токен бота
pip install aiogram httpxТокен самого Telegram-бота получаете у @BotFather в Telegram — это отдельная сущность от fetch_... ключа шлюза, их нельзя путать: один авторизует бота в Telegram, второй — запрос к модели.
Код бота на Claude
import asyncio
import httpx
from aiogram import Bot, Dispatcher
from aiogram.types import Message
TELEGRAM_TOKEN = "ваш_токен_из_botfather"
ZUKKO_API_KEY = "fetch_ваш_ключ"
bot = Bot(token=TELEGRAM_TOKEN)
dp = Dispatcher()
async def ask_claude(text: str) -> str:
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(Тот же бот, но на GPT/GLM
Меняется только функция запроса — эндпоинт и путь до текста ответа другие:
async def ask_openai_compatible(text: str, model: str = "gpt-5.6-sol") -> str:
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(
"https://api.zukko.pro/v1/chat/completions",
headers={
"Authorization": f"Bearer {ZUKKO_API_KEY}",
"Content-Type": "application/json",
},
json={
"model": model,
"messages": [{"role": "user", "content": text}],
},
)
data = resp.json()
if resp.status_code != 200:
return f"Ошибка: {data.get('error', {}).get('message', 'unknown')}"
return data["choices"][0]["message"]["content"]Вызывайте с нужным model: gpt-5.6-sol, gpt-5.6-luna, glm-5.3, glm-5.3-flash (Claude тоже можно звать этим путём — шлюз конвертирует формат) — вся линейка идёт через один и тот же путь /v1/chat/completions; актуальный список — GET https://api.zukko.pro/v1/models.
Обработка лимитов прямо в диалоге
Если бот публичный и им пользуется больше одного человека, разумно поймать 429 отдельно и явно сказать об этом в чате, а не молчать:
if resp.status_code == 429:
code = data.get("error", {}).get("code", "")
if code == "key_daily_limit_reached":
return "Дневной лимит ключа исчерпан, попробуйте после 00:00 UTC"
if code == "key_monthly_limit_reached":
return "Месячный лимит ключа исчерпан"
# прочие 429 приходят от провайдера модели — у самого шлюза rate limit нет
return "Слишком много запросов, подождите немного"Дневной и месячный лимит расхода на ключ настраивается в кабинете на странице ключа (/app/api-keys) — для публичного бота стоит поставить разумный потолок, чтобы один настойчивый пользователь не выжег весь баланс за вечер.
Идемпотентность на случай дублей от Telegram
Telegram иногда присылает одно и то же обновление дважды при нестабильном соединении с вашим сервером — если это критично (например, бот тратит деньги на длинную генерацию), добавляйте в заголовки конкретного запроса Idempotency-Key, построенный из message.message_id и chat.id, — он уникален для каждого сообщения и одинаков при его дубле, поэтому дубль не спишется повторно:
headers["Idempotency-Key"] = f"{message.chat.id}-{message.message_id}"Генерация изображений из бота
Генерация изображений через Zukko временно недоступна — раздел оставлен для справки. Актуальный список моделей —
GET https://api.zukko.pro/v1/models.
Для команды вида /image кот в скафандре дергается отдельный эндпоинт с отдельным биллингом — за штуку, не за токены:
async def generate_image(prompt: str) -> bytes:
async with httpx.AsyncClient(timeout=120) as client:
resp = await client.post(
"https://api.zukko.pro/v1/images/generations",
headers={"Authorization": f"Bearer {ZUKKO_API_KEY}"},
json={
"model": "nano-banana-2-text-to-image",
"prompt": prompt,
},
)
return resp.json()Проверка баланса перед сессией
Полезная привычка для бота, который крутится без присмотра — раз в час проверять баланс и слать себе алерт при низком остатке:
curl https://api.zukko.pro/v1/balance -H "Authorization: Bearer fetch_ваш_ключ"Ключ создаётся на https://zukko.pro/app/api-keys и показывается один раз — храните в переменных окружения сервера, где крутится бот, не в коде. Стартового баланса нет; если это тестовый проект, $0.50 на первые вызовы один раз даёт подписка на @zukkopro с привязкой Telegram в кабинете.
Команда /stats для админа бота
Удобно завести скрытую команду, которая отдаёт вам историю трат — эндпоинт /v1/usage отдаёт историю по всему аккаунту (все ключи вместе, фильтра по ключу нет) и поддерживает курсорную пагинацию, так что для дайджеста за день достаточно последних записей:
@dp.message(Command("stats"))
async def stats(message: Message):
if message.from_user.id != ADMIN_ID:
return
async with httpx.AsyncClient() as client:
resp = await client.get(
"https://api.zukko.pro/v1/usage",
headers={"Authorization": f"Bearer {ZUKKO_API_KEY}"},
params={"limit": 20},
)
await message.answer(str(resp.json()))Здоровье сервиса перед стартом polling
Если бот падает с непонятными таймаутами, а ключ и баланс в порядке, стоит сначала проверить сам сервис, а не свой код:
curl https://api.zukko.pro/api/statusЭто публичный JSON со статусом моделей, без ключа — быстрый способ отличить временную недоступность шлюза (тогда модели будут отвечать 503 с provider_unavailable) от бага в самом боте.
Реферальная ссылка для читателей бота
Если бот публичный и им пользуются подписчики канала, есть смысл рассказать им, что за подписку на @zukkopro начисляется $0.50, а по реферальной программе с их будущих пополнений капает 10% приглашавшему — это две разные механики, и в боте их стоит объяснять отдельно, чтобы не путать бонус за подписку с реферальным процентом.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Промпт-инъекция: что это и как защитить бота и агентаОбъясняем, как работает промпт-инъекция через пользовательский ввод и чужие документы, и какие меры снижают ущерб: изоляция данных, права инструментов, лимиты ключа.
- Свой прокси к нейросети на Node.js: не показывать API-ключ во фронтендеНебольшой сервер на Express между вашим фронтендом и API нейросети: ключ остаётся на сервере, а модели, длина запроса и частота ограничены.
- DeepSeek API на Python: запросы, стриминг, JSON и обработка ошибокГотовый код для DeepSeek V4.1 Flash и V4 Pro на OpenAI SDK: запрос, стриминг, разбор JSON и аккуратная обработка ошибок.
- Промпт-инжиниринг: примеры и 12 готовых шаблонов для работыДвенадцать шаблонов промптов для рабочих задач: что в них написано, почему именно так и сколько стоит один вызов по API.