Sniketor для разработчиков

Sniketor Bot API · версия 1

Боты для Sniketor

Всё как в Telegram: @botfather выдаёт токен, а бота ты запускаешь сам — на своём компьютере, сервере или хостинге. Sniketor передаёт боту сообщения и команды, бот отвечает текстом и кнопками.

1Напиши @botfather и нажми «Создать бота». Имя бота начинается с bot: bot_weather
2Получи токен вида 4fed…418a:Kxy…937
3Запусти свой код, который ходит в https://chat.qwgames.ru/api/bot/…
4Найди бота в каталоге 🤖 и добавь в сообщество или канал
Где работает бот? Там, где ты его запустил. Sniketor не хранит твой код и не запускает его — он только принимает запросы с токеном. Токен хранится у нас в виде хэша, поэтому восстановить его нельзя: если потерял — выпусти новый через /token.

Быстрый старт: эхо-бот за 5 минут

Нужен Node.js 18+. Никаких библиотек ставить не нужно.

  1. В Sniketor открой каталог ботов 🤖 → «Создать через @botfather» → /newbot → придумай имя, которое начинается с bot (например, bot_echo).
  2. Скопируй токен и сохрани файл bot.mjs:
// bot.mjs — запусти: SNIKETOR_TOKEN="твой_токен" node bot.mjs
const API = 'https://chat.qwgames.ru/api/bot/';
const headers = { Authorization: 'Bot ' + process.env.SNIKETOR_TOKEN, 'Content-Type': 'application/json' };
const call = async (method, body) => {
  const res = await fetch(API + method, { method: body ? 'POST' : 'GET', headers, body: body && JSON.stringify(body) });
  const data = await res.json();
  if (!res.ok) throw new Error(`${res.status}: ${data.error}`);
  return data;
};

const me = await call('me');
console.log('Запущен бот @' + me.username);
let offset = 0;
while (true) {
  let updates = [];
  try { updates = await call(`updates?timeout=25&offset=${offset}`); }
  catch (e) { console.error(e.message); await new Promise(r => setTimeout(r, 3000)); continue; }
  for (const u of updates) {
    offset = u.update_id + 1;
    try {
    if (u.type === 'message') {
      const m = u.message;
      const text = m.text === '/start' ? 'Привет! Я эхо-бот. Напиши что-нибудь 🙂' : 'Ты написал: ' + m.text;
      await call('send', { chat: m.chat.id, text, request_id: 'reply-' + u.update_id,
        buttons: [[{ text: '👍 Нравится', data: 'like' }]] });
    }
    if (u.type === 'callback') {
      await call('answer', { callback: u.callback.id, text: 'Спасибо, ' + u.callback.from.name + '!' });
    }
    } catch (e) { console.error('Не удалось обработать событие', u.update_id, e.message); }
  }
}
  1. Запусти: SNIKETOR_TOKEN="…" node bot.mjs (Windows PowerShell: $env:SNIKETOR_TOKEN="…"; node bot.mjs).
  2. Открой каталог ботов 🤖, найди своего бота → «Написать» → «Начать». Бот ответит.

Токен и авторизация

Каждый запрос — на адрес https://chat.qwgames.ru/api/bot/<метод> с заголовком:

Authorization: Bot 4fed8ce370f8be3f80fc5b9a3d4c418a:KxyJbFw3Qcws9YW3C4yIhtbkwApZ8937

Тело запросов — JSON (Content-Type: application/json), ответы — тоже JSON. В случае ошибки приходит {"error":"текст"} и HTTP-код (см. ошибки).

Управление токеном — в чате с @botfather: /token выпускает новый (старый сразу перестаёт работать), /mybots → «Выключить» временно блокирует бота, «Удалить» — навсегда.

Получение событий (long polling)

GET /api/bot/updates?timeout=25&offset=0

Сервер держит запрос открытым до timeout секунд (максимум 25), пока не появятся события, и возвращает массив Update (до 100 штук).

  • offset — передавай update_id последнего обработанного события + 1. Всё, что меньше, сервер удалит: так он понимает, что ты уже обработал события.
  • Если бот не забирал события, они хранятся 24 часа (не больше 1000 штук).
  • Делай следующий запрос сразу после ответа — это нормальная схема работы, а не нагрузка.

Объекты

Update — сообщение

{
  "update_id": 42,
  "type": "message",
  "message": {
    "id": 1225,
    "chat": { "id": "6136cffc…", "type": "channel", "name": "рейды",
              "community": { "id": "d8d0aca2…", "name": "ХАБИБ" } },
    "from": { "id": "4685c1ad…", "name": "vovan_test" },
    "text": "/raid 21:00",
    "file": null,
    "reply_to": null,
    "thread": null,
    "created": "2026-10-10T18:00:00.000Z"
  }
}
  • chat.type: "private" — личка с ботом, "channel" — канал сообщества.
  • file — если прислали файл: {"name":"скрин.png"}, текст тогда пустой.
  • reply_to — id сообщения, на которое ответили; thread — id ветки.

Update — нажатие кнопки

{
  "update_id": 43,
  "type": "callback",
  "callback": {
    "id": "9b1f3c0a7d2e4f11",
    "from": { "id": "4685c1ad…", "name": "vovan_test" },
    "message": 1225,
    "chat": { "id": "6136cffc…", "type": "channel", "name": "рейды" },
    "data": "go"
  }
}

На нажатие нужно ответить методом answer в течение 5 минут — человек увидит всплывающее сообщение.

Что видит бот

ГдеЧто получает бот
Личка с ботомВсе сообщения
Канал сообществаТолько команды (/start, /raid@bot_raid), сообщения с @имя_бота и ответы на сообщения бота

Команда с чужим именем (/start@bot_other) твоему боту не придёт. Сообщения других ботов и вебхуков боту не приходят.

Методы

GET me

Кто я. Удобно проверить токен.

{ "id": "4fed8ce3…", "username": "bot_raid", "description": "…",
  "commands": [{ "command": "raid", "description": "Собрать рейд" }], "disabled": false }

GET updates

События. Параметры: offset, timeout (0–25). Ответ — массив Update.

POST send

Отправить сообщение.

ПолеТип
chatстрокаid чата из события (message.chat.id)
textстрока1–2000 символов. Поддерживается форматирование
buttonsмассивнеобязательно, см. кнопки
reply_toчислонеобязательно, id сообщения в этом же чате
request_idстроканеобязательно, 1–64 символа (латиница, цифры, - _ . :): защита от дублей
→ { "chat": "6136cffc…", "text": "**Рейд в 21:00!** Кто идёт?",
    "buttons": [[{ "text": "Я иду", "data": "go" }, { "text": "Не смогу", "data": "no" }]],
    "request_id": "raid-2026-10-10" }
← { "id": 1226, "chat": "6136cffc…", "duplicate": false }

POST edit

Изменить своё сообщение: {"message":1226,"text":"Рейд в 21:00 — уже 5 человек","buttons":[…]}. Можно передать только text или только buttons; "buttons":null убирает кнопки.

POST delete

Удалить своё сообщение: {"message":1226}.

POST answer

Ответить на нажатие кнопки: {"callback":"9b1f3c0a7d2e4f11","text":"Записал ✓"}. text необязателен (до 200 символов), его увидит только нажавший.

POST commands

Список команд для подсказок: когда человек набирает «/» в чате с ботом, он видит этот список.

{ "commands": [ { "command": "start", "description": "Начать" },
                { "command": "raid",  "description": "Собрать рейд" } ] }

То же можно сделать в @botfather: /setcommands.

Форматирование текста

**жирный**, *курсив*, __подчёркнутый__, ~~зачёркнутый~~, ||спойлер||, `код`, ```блок кода```, строка с > — цитата. Ссылки и @упоминания подсвечиваются сами.

Кнопки

buttons — массив рядов, в ряду — массив кнопок. До 6 рядов и до 4 кнопок в ряду.

  • {"text":"Я иду","data":"go"} — кнопка-действие: при нажатии бот получит событие callback с этим data (1–64 символа).
  • {"text":"Сайт","url":"https://example.com"} — кнопка-ссылка, только https://.
  • Текст кнопки — 1–40 символов. Удобно класть в data короткие команды вида vote:42.

Защита от дублей

Сеть иногда обрывается, и непонятно, дошло ли сообщение. Передавай request_id: если запрос с тем же request_id придёт повторно (в течение 7 дней), сервер не создаст второе сообщение, а вернёт {"id":…, "duplicate":true}.

Хороший вариант — строить request_id из события, на которое отвечаешь: "reply-" + update_id. Тогда даже после перезапуска бота ответ не задвоится.

Ошибки и лимиты

КодЧто значитЧто делать
400Неверные данные (длина текста, кнопки, request_id)Исправить запрос, повтор не поможет
401Нет токена, он неверный или отозванПроверить токен, выпустить новый в @botfather
403Бот выключен владельцем или нет доступа к чатуВключить бота / добавить в сообщество или канал
404Чат, сообщение или нажатие не найденоПроверить id
429Слишком частоПодождать Retry-After секунд и повторить
5xxВременная ошибка сервераПовторить с паузой (1, 2, 4… секунды) и тем же request_id

Лимиты

  • До 600 запросов в минуту на бота и до 120 отправок в минуту.
  • Текст — до 2000 символов, описание бота — до 300, команд — до 30.
  • У одного человека — до 10 ботов; в одном сообществе — до 20 ботов.

Где бот может писать

  • В личку — тем, кто открыл чат с ботом (каталог 🤖 → «Написать»). Первым написать человеку бот не может.
  • В сообщество — во все каналы, где у бота есть право писать. Добавляет владелец: каталог 🤖 → «Добавить» или Настройки сообщества → Интеграции → Боты.
  • В отдельный канал — если бота добавили только в канал (Настройки канала → «Боты канала»), он видит и пишет только туда.

Права бота в сообществе настраиваются так же, как у участника: роли и исключения в настройках канала.

Проверка ботов

Каждого нового бота проверяют модераторы Sniketor. Пока проверка идёт, бот уже работает, но только со своим создателем: отвечает ему в личке и работает в сообществах, куда создатель добавил его сам. В каталоге такой бот не виден, а другие люди не могут его добавить.

Когда бота одобрят, @botfather пришлёт сообщение, и бот появится в каталоге. Если бот нарушает правила (спам, мошенничество, выдаёт себя за другой сервис), его блокируют: ключ перестаёт работать (ошибка 403), бот выходит из всех сообществ. Статус бота виден в @botfather → «Мои боты».

Голос и музыка

Бот может зайти в голосовую комнату и передавать туда звук — так работают музыкальные боты. Звук передаётся по WebRTC, как у обычного участника. Бот находится не больше чем в одной комнате одновременно и только передаёт звук (микрофоны людей он не записывает).

МетодЧто делает
POST voice/where {chat, user}В какой голосовой комнате сейчас сидит человек (только в сообществе этого чата). Ответ: {channel, name} или {channel:null}.
POST voice/join {channel}Войти в комнату. Ответ: peers — кто уже в комнате, и config — ICE-серверы для WebRTC. Боту нужно право «Подключаться».
POST voice/signal {to, signal}Передать участнику {description} (offer/answer) или {candidate}.
POST voice/heartbeatРаз в 20 секунд, иначе через 75 секунд бот выйдет из комнаты.
POST voice/leaveВыйти из комнаты.
GET file?message=IDСкачать аудиофайл из сообщения (mp3, ogg, m4a, wav, flac, opus, webm), если бот видит этот канал.

События в updates: voice_signal (voice.from, voice.channel, voice.signal), voice_left (voice.user вышел) и voice_kicked (бота отключили). Боту, который входит в комнату, нужно самому отправить offer каждому из peers; люди, которые зайдут позже, пришлют offer сами.

Готовый пример — музыкальный бот Sniketor: он сделан на Node.js и маленькой программе sniketor-voice (WebRTC на Go), а звук готовит ffmpeg. Играть можно только то, что разрешено: интернет-радио, свои файлы и музыку со свободной лицензией. YouTube, VK Музыка и Spotify запрещают передавать их музыку ботам — такие боты будут заблокированы.

Запуск 24/7

Пока ты тестируешь, бот может работать прямо на твоём ПК. Чтобы он отвечал всегда, запусти его на сервере (подойдёт самый дешёвый VPS с Linux).

Linux + systemd

# /etc/systemd/system/mybot.service
[Unit]
Description=My Sniketor bot
After=network-online.target

[Service]
User=mybot
WorkingDirectory=/opt/mybot
Environment=SNIKETOR_TOKEN=твой_токен
ExecStart=/usr/bin/node /opt/mybot/bot.mjs
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now mybot
journalctl -u mybot -f

Входящие порты открывать не нужно: бот сам обращается к Sniketor.

Пример на Python

Только стандартная библиотека, Python 3.9+.

import json, os, time, urllib.request

API = "https://chat.qwgames.ru/api/bot/"
TOKEN = os.environ["SNIKETOR_TOKEN"]

def call(method, body=None, timeout=35):
    req = urllib.request.Request(API + method, method="POST" if body else "GET",
        data=json.dumps(body).encode() if body else None,
        headers={"Authorization": "Bot " + TOKEN, "Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return json.load(r)

print("Запущен @" + call("me")["username"])
offset = 0
while True:
    try:
        updates = call(f"updates?timeout=25&offset={offset}")
    except Exception as e:
        print("ошибка:", e); time.sleep(3); continue
    for u in updates:
        offset = u["update_id"] + 1
        try:
            if u["type"] == "message" and u["message"]["text"].startswith("/start"):
                call("send", {"chat": u["message"]["chat"]["id"], "text": "Привет из Python! 🐍",
                              "request_id": f"reply-{u['update_id']}"})
        except Exception as e:
            print("не удалось обработать", u["update_id"], e)

Вебхуки для уведомлений

Если нужно только присылать сообщения (новости, мониторинг, оповещения о сборке) и не нужно читать чат — бот не нужен. Создай вебхук: Настройки сообщества → Интеграции → Вебхуки, и отправляй:

curl -X POST "https://chat.qwgames.ru/api/hooks/<id>/<секрет>" \
  -H "Content-Type: application/json" \
  -d '{"content":"✅ Сборка 1.2.3 готова","username":"CI"}'

Формат как у Discord, поэтому подходят многие готовые интеграции (GitHub Actions, Zapier, IFTTT).

Безопасность

  • Токен — как пароль. Не публикуй его в GitHub, скриншотах и чатах. Храни в переменной окружения или файле с правами только для владельца.
  • Утёк токен — сразу /token в @botfather: старый перестанет работать мгновенно.
  • Бот не может войти в Sniketor как человек и не имеет доступа к базе — только к чатам, куда его пустили.
  • Не проси у людей пароли, коды входа и данные карт — за это бота заблокируют.
  • Боты подчиняются правилам Sniketor, как и все участники.

Частые вопросы

Можно ли запустить бота на телефоне или бесплатно?

Да, на любом устройстве с Node.js или Python и интернетом. Бесплатно — на своём ПК, пока он включён. Для работы 24/7 нужен сервер.

Почему бот не видит сообщения в канале?

В каналах бот получает только команды, @упоминания и ответы себе — это защищает переписку людей. В личке он видит всё.

Бот получает одно и то же событие повторно

Ты не передаёшь offset = последний update_id + 1. Без него сервер считает, что события ещё не обработаны.

Как сделать, чтобы бот не отвечал дважды после перезапуска?

Передавай request_id, построенный из update_id (например, reply-42), и сохраняй offset в файл.

Есть ли вебхуки вместо long polling?

Пока нет: long polling работает без открытых портов и белого IP, поэтому подходит для запуска дома. Исходящие вебхуки появятся позже.

Куда писать, если что-то не работает?

В приложении: «Помощь» → «Сообщить о проблеме». Или спроси @bot_sniketor.