GET me
Кто я. Удобно проверить токен.
{ "id": "4fed8ce3…", "username": "bot_raid", "description": "…",
"commands": [{ "command": "raid", "description": "Собрать рейд" }], "disabled": false }Sniketor Bot API · версия 1
Всё как в Telegram: @botfather выдаёт токен, а бота ты запускаешь сам — на своём компьютере, сервере или хостинге. Sniketor передаёт боту сообщения и команды, бот отвечает текстом и кнопками.
bot: bot_weather4fed…418a:Kxy…937https://chat.qwgames.ru/api/bot/…/token.Нужен Node.js 18+. Никаких библиотек ставить не нужно.
/newbot → придумай имя, которое начинается с bot (например, bot_echo).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); }
}
}
SNIKETOR_TOKEN="…" node bot.mjs (Windows PowerShell: $env:SNIKETOR_TOKEN="…"; node bot.mjs).Каждый запрос — на адрес https://chat.qwgames.ru/api/bot/<метод> с заголовком:
Authorization: Bot 4fed8ce370f8be3f80fc5b9a3d4c418a:KxyJbFw3Qcws9YW3C4yIhtbkwApZ8937
Тело запросов — JSON (Content-Type: application/json), ответы — тоже JSON. В случае ошибки приходит {"error":"текст"} и HTTP-код (см. ошибки).
Управление токеном — в чате с @botfather: /token выпускает новый (старый сразу перестаёт работать), /mybots → «Выключить» временно блокирует бота, «Удалить» — навсегда.
GET /api/bot/updates?timeout=25&offset=0
Сервер держит запрос открытым до timeout секунд (максимум 25), пока не появятся события, и возвращает массив Update (до 100 штук).
update_id последнего обработанного события + 1. Всё, что меньше, сервер удалит: так он понимает, что ты уже обработал события.{
"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_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) твоему боту не придёт. Сообщения других ботов и вебхуков боту не приходят.
Кто я. Удобно проверить токен.
{ "id": "4fed8ce3…", "username": "bot_raid", "description": "…",
"commands": [{ "command": "raid", "description": "Собрать рейд" }], "disabled": false }События. Параметры: offset, timeout (0–25). Ответ — массив Update.
Отправить сообщение.
| Поле | Тип | |
|---|---|---|
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 }Изменить своё сообщение: {"message":1226,"text":"Рейд в 21:00 — уже 5 человек","buttons":[…]}. Можно передать только text или только buttons; "buttons":null убирает кнопки.
Удалить своё сообщение: {"message":1226}.
Ответить на нажатие кнопки: {"callback":"9b1f3c0a7d2e4f11","text":"Записал ✓"}. text необязателен (до 200 символов), его увидит только нажавший.
Список команд для подсказок: когда человек набирает «/» в чате с ботом, он видит этот список.
{ "commands": [ { "command": "start", "description": "Начать" },
{ "command": "raid", "description": "Собрать рейд" } ] }
То же можно сделать в @botfather: /setcommands.
**жирный**, *курсив*, __подчёркнутый__, ~~зачёркнутый~~, ||спойлер||, `код`, ```блок кода```, строка с > — цитата. Ссылки и @упоминания подсвечиваются сами.
Сеть иногда обрывается, и непонятно, дошло ли сообщение. Передавай 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 |
Права бота в сообществе настраиваются так же, как у участника: роли и исключения в настройках канала.
Каждого нового бота проверяют модераторы 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 запрещают передавать их музыку ботам — такие боты будут заблокированы.
Пока ты тестируешь, бот может работать прямо на твоём ПК. Чтобы он отвечал всегда, запусти его на сервере (подойдёт самый дешёвый VPS с Linux).
# /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 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).
/token в @botfather: старый перестанет работать мгновенно.Да, на любом устройстве с Node.js или Python и интернетом. Бесплатно — на своём ПК, пока он включён. Для работы 24/7 нужен сервер.
В каналах бот получает только команды, @упоминания и ответы себе — это защищает переписку людей. В личке он видит всё.
Ты не передаёшь offset = последний update_id + 1. Без него сервер считает, что события ещё не обработаны.
Передавай request_id, построенный из update_id (например, reply-42), и сохраняй offset в файл.
Пока нет: long polling работает без открытых портов и белого IP, поэтому подходит для запуска дома. Исходящие вебхуки появятся позже.
В приложении: «Помощь» → «Сообщить о проблеме». Или спроси @bot_sniketor.