Bot API — боты для разработчиков

Как сделать бота для MRIYU: токен, сообщения, кнопки, фото и файлы, боты в группах и каналах, вебхуки, лимиты и готовые примеры.

Бот — это страница-программа. Люди переписываются с ней в мессенджере, а отвечает ваш код: он получает сообщения через Bot API и отправляет ответы. Если вы делали ботов для Telegram, всё будет знакомо.

Что умеет бот:

  • отвечать на сообщения и команды (/start, /help и свои);
  • присылать сообщения с кнопками: ответ боту, ссылка на сайт или открытие игры / Mini App в MRIYU;
  • менять и удалять свои сообщения;
  • присылать фото и файлы;
  • рассылать уведомления тем, кто на него подписан;
  • работать в групповых чатах и каналах: отвечать на команды, приветствовать новичков, модерировать, публиковать посты (автопостинг);
  • показывать кнопку меню, которая открывает вашу игру или Mini App.

Бот может писать только тем, кто сам нажал «Начать» или написал ему. Первым незнакомому человеку бот не напишет — так в MRIYU нет спама от ботов.

Быстрый старт за 5 минут

  1. Откройте «Разработчикам» в меню слева (пункт скрыт — включите его в «Настройке пунктов меню», см. Настройки и меню, или откройте сразу mriyu.com/developers?bots) → вкладка «Боты».
  2. Нажмите «Новый бот»: укажите имя, адрес (латиница, цифры, _, в конце обязательно bot, например weather_bot) и пару слов о боте.
  3. Скопируйте токен — он показывается один раз. Токен — это пароль бота.
  4. Во вкладке «Подключение» скопируйте готовый код (там уже подставлен ваш токен) и запустите его.
  5. Откройте mriyu.com/<адрес бота>, нажмите «Открыть бота» → «Начать» и напишите что-нибудь — бот ответит.

Как устроены запросы

Адрес каждого метода:

https://mriyu.com/api/bot/v1/<метод>
  • Запрос — POST с телом в JSON (Content-Type: application/json). Методы без параметров можно вызывать и GET.
  • Токен — в заголовке Authorization: Bearer <токен>. Можно также заголовком X-Bot-Token или параметром ?token= (не советуем: адрес с токеном может попасть в журналы).
  • Ответ всегда в JSON.

Успех:

{"ok": true, "result": { ... }}

Ошибка:

{"ok": false, "error_code": 403, "description": "Пользователь не нажимал «Начать» или остановил бота."}
Код Когда
400 Неверные параметры (в description — что именно).
401 Неверный токен или бот заблокирован администрацией.
403 Нельзя писать этому человеку: он не нажимал «Начать», остановил бота или заблокировал его.
404 Нет такого метода, пользователя или сообщения.
409 getUpdates при включённом вебхуке.
422 Неверные кнопки (подробности — в description).
429 Слишком много запросов — подождите и повторите.

Как бот получает сообщения

Всё, что происходит с ботом, приходит обновлениями (update). Есть два способа их получать — выберите один.

Способ 1. getUpdates — без своего сайта

Ваш код раз в 1–3 секунды спрашивает: «есть что-то новое?». Подходит любой компьютер, VPS или хостинг — открытый адрес не нужен.

  • Обновления хранятся 24 часа.
  • Передавайте offset = update_id последнего обработанного обновления + 1 — всё, что раньше, считается полученным и больше не придёт.

Способ 2. Вебхук — сообщения приходят сразу

MRIYU сам отправляет каждое обновление POST-запросом на ваш адрес. Нужен сайт с https.

  • Включить: метод setWebhook или кабинет → бот → «Настройки» → «Вебхук». Кнопка «Проверить вебхук» пришлёт пробное обновление {"update_id": 0, "type": "ping"}.
  • Если указали секрет, он приходит в заголовке X-Mriyu-Bot-Secret — проверяйте его, чтобы чужие не слали вам поддельные обновления.
  • Ваш сервер должен ответить кодом 2xx за 5 секунд. Отвечайте сразу, а долгую работу делайте после ответа.
  • Не ответил или ошибка — доставка остановится и повторится через минуту, порядок обновлений сохранится. Ошибка видна в кабинете и в getWebhookInfo.
  • Пока вебхук включён, getUpdates недоступен (ошибка 409).

Виды обновлений

Каждое обновление — JSON с полями update_id (растёт по порядку) и type. Поле from — кто это сделал: {"id": 15, "name": "Оля Петренко", "username": "olya"}.

type Когда приходит Поля
start Человек нажал «Начать» from, payload — параметр из ссылки ?start= или null
message Человек написал боту from, message
button Нажал кнопку с data под сообщением бота from, button: {message_id, data}
stop Нажал «Остановить бота» from
chat_message Сообщение в групповом чате или канале, где есть бот (см. «Боты в группах») chat, from, message
chat_added Бота добавили в чат или канал chat, by — кто добавил
chat_removed Бота удалили из чата chat, by
chat_join В чат пришёл новый участник chat, user, by — кто пригласил или null
chat_leave Участник вышел или его удалили chat, user, by

Поле chat — {"id": 7, "title": "Двор на Шевченко", "type": "chat"} (type: chat — групповой чат, channel — канал). Нажатие кнопки под сообщением бота в группе приходит обновлением button с полем chat.

Поля message:

Поле Что это
id Номер сообщения
text Текст
kind Вид: text (бот получает текстовые сообщения)
command Если текст начинается с /: {"name": "forecast", "args": "Киев"} для /forecast Киев, иначе null
reply_to Номер сообщения, на которое ответили, или null
date Время, unix-секунды

Пример обновления:

{
  "update_id": 1042,
  "type": "message",
  "from": {"id": 15, "name": "Оля Петренко", "username": "olya"},
  "message": {"id": 98761, "text": "/forecast Киев", "kind": "text",
              "command": {"name": "forecast", "args": "Киев"}, "reply_to": null, "date": 1760524800}
}

Методы

getMe

Проверить токен и узнать о боте. Параметров нет. Ответ: id, name, username, about, verified (галочка «Официальный»), webhook (включён ли), subscribers (сколько людей нажали «Начать»).

sendMessage

Отправить сообщение человеку.

Параметр Обязательно Что это
user_id да Кому — from.id из обновления
text да Текст до 4000 символов. Разметка как в мессенджере: **жирный**, __курсив__, ссылки [текст](https://…), моноширинный текст — в обратных кавычках
buttons нет Кнопки под сообщением (см. ниже)
reply_to нет Номер сообщения, на которое бот отвечает
silent нет true — без звука уведомления

Ответ — отправленное сообщение: {"id", "user_id", "text", "buttons", "date", "edited"}. Запомните id, чтобы потом изменить сообщение.

editMessage

Изменить своё сообщение: message_id и новые text и/или buttons. Передайте "buttons": null, чтобы убрать кнопки. Удобно после нажатия кнопки: «Подписаться?» → «Подписка оформлена ✅».

deleteMessage

Удалить своё сообщение: message_id.

getUpdates

Забрать обновления (только без вебхука). Параметры: offset — с какого update_id (всё раньше считается полученным), limit — сколько, до 100 (по умолчанию 100). Ответ — массив обновлений.

setWebhook / deleteWebhook / getWebhookInfo

  • setWebhook: url (https) и необязательный secret (8–128 символов: латиница, цифры, _, -).
  • deleteWebhook: выключить вебхук и вернуться к getUpdates.
  • getWebhookInfo: url, has_secret, pending (сколько ждут доставки), last_error, last_error_at.

setCommands / getCommands

Список команд — его видят в чате кнопкой «Команды». До 30 команд: {"command": "forecast", "description": "Прогноз на завтра"}. Имя команды — латиница, цифры и _, до 32 символов, без /. То же можно настроить в кабинете.

getUser

Сведения о человеке, который писал боту: user_id → id, name, username, avatar, started (подписан ли сейчас).

sendPhoto / sendDocument

Фото или файл — человеку (user_id) или в чат (chat_id).

Параметр Обязательно Что это
user_id или chat_id да Кому или куда
file или url да Сам файл (запрос multipart/form-data) или ссылка https — MRIYU скачает файл сам
caption нет Подпись до 1000 символов
buttons нет Кнопки; в multipart/form-data — строкой JSON
reply_to, silent нет Как у sendMessage

sendPhoto принимает только картинки (jpg, png, webp, gif). Размер — до лимита загрузок сайта. Программы, скрипты и HTML отправить нельзя. В группах и каналах действует фильтр откровенных картинок.

curl -X POST https://mriyu.com/api/bot/v1/sendPhoto \
  -H "Authorization: Bearer ВАШ_ТОКЕН" \
  -F user_id=15 -F caption="Погода на завтра" -F file=@forecast.png

getSubscribers

Кто подписан на бота — для рассылок. По 1000 номеров: {"user_ids": [...], "next": 5012}. Если next не null, запросите следующую страницу с after = next.

Боты в группах и каналах

Бота можно добавить в групповой чат или канал: владелец или администратор открывает «Участники» → «Добавить бота» и вводит адрес бота. Разработчик может это запретить: кабинет → бот → «Настройки» → снять «Можно добавлять в группы и каналы».

  • В групповом чате бот становится обычным участником. Владелец может сделать его администратором («Назначить администратором») — тогда бот сможет модерировать.
  • В канал бот сразу добавляется администратором — он может публиковать посты (автопостинг).

Что бот видит в группе

По умолчанию — как режим приватности в Telegram — бот получает только:

  • команды: /rules, /help Киев (команда /start@other_bot достанется только боту other_bot);
  • упоминания: сообщения, где есть @адрес_бота;
  • ответы на его собственные сообщения.

Если боту нужно видеть всё (например, чтобы удалять спам), включите в кабинете «Видит все сообщения в группах». Люди в чате об этом не узнают — включайте, только если это действительно нужно.

Методы для групп

Метод Параметры Что делает
getChats — Чаты и каналы, где состоит бот: [{id, title, type, role}]
getChat chat_id Чат: id, title, type, role бота, members
sendChatMessage chat_id, text, buttons, reply_to, silent Сообщение в чат; в канал — только если бот администратор
editChatMessage chat_id, message_id, text, buttons Изменить своё сообщение
deleteChatMessage chat_id, message_id Удалить своё сообщение; чужое — если бот администратор
kickChatMember chat_id, user_id Исключить участника (бот — администратор; владельца и администраторов исключить нельзя)
sendPhoto, sendDocument chat_id вместо user_id Фото или файл в чат

Ответ sendChatMessage: {"id", "chat_id", "kind", "text", "url", "buttons", "date"}.

Пример: страж чата (Node.js)

Бот-администратор с «Видит все сообщения»: приветствует новичков, удаляет ссылки и отвечает на /rules.

async function handle(update) {
  if (update.type === 'chat_join') {
    await call('sendChatMessage', {chat_id: update.chat.id, text: 'Привет, ' + update.user.name + '! Правила — /rules'});
  }
  if (update.type === 'chat_message') {
    const {chat, message} = update;
    if (/https?:\/\//i.test(message.text)) {
      await call('deleteChatMessage', {chat_id: chat.id, message_id: message.id});
      await call('sendChatMessage', {chat_id: chat.id, text: 'Ссылки в этом чате запрещены.'});
    } else if (message.command?.name === 'rules') {
      await call('sendChatMessage', {chat_id: chat.id, text: '1. Без спама\n2. Без оскорблений', reply_to: message.id});
    }
  }
}

Пример: автопостинг в канал (Python)

import requests

HEADERS = {'Authorization': 'Bearer ВАШ_ТОКЕН'}
API = 'https://mriyu.com/api/bot/v1/'

chats = requests.post(API + 'getChats', headers=HEADERS).json()['result']
channel = next(c for c in chats if c['type'] == 'channel')
requests.post(API + 'sendPhoto', headers=HEADERS, data={
    'chat_id': channel['id'], 'caption': 'Новости дня', 'url': 'https://example.com/today.jpg',
})

Кнопки

buttons — массив рядов, в ряду — массив кнопок. До 8 рядов, в ряду от 1 до 4 кнопок, текст кнопки — до 40 символов. Каждая кнопка — одно из трёх:

Кнопка Что происходит при нажатии
{"text": "Да", "data": "sub:yes"} Боту приходит обновление button с этим data (до 64 байт). Ответьте новым сообщением или измените это (editMessage).
{"text": "Сайт", "url": "https://example.com"} Открывается ссылка (только https) в новой вкладке.
{"text": "Играть", "app": "my-game"} Открывается игра или Mini App MRIYU по её адресу.
"buttons": [
  [{"text": "✅ Да", "data": "sub:yes"}, {"text": "❌ Нет", "data": "sub:no"}],
  [{"text": "🎮 Открыть игру", "app": "my-game"}]
]

Публичная страница бота

У бота есть своя страница — mriyu.com/<адрес бота>. Её настраивают в кабинете → бот → «Настройки» → «Публичная страница»:

  • обложка и аватар;
  • статус — строка под именем (до 140 символов);
  • категория — «Новости», «Погода», «Игры», «Инструменты», «Покупки», «Обучение», «Развлечения», «Поддержка», «Сообщества» или «Другое»;
  • сайт — ссылка на ваш сайт;
  • «Что умеет бот» — описание, оно видно на странице и в пустом чате.

На странице также видны команды, сколько людей пользуются ботом, разработчик и кнопки «Открыть бота» и кнопка меню.

Команды, кнопка меню и ссылка с параметром

  • Команды. Над полем ввода в чате с ботом есть кнопка «Команды» — список из setCommands. Нажатие отправляет команду, вам приходит message с заполненным command.
  • Кнопка меню. В кабинете → «Настройки» укажите адрес игры или Mini App (например my-game) и текст кнопки — рядом с «Командами» появится кнопка, открывающая её.
  • Ссылка с параметром. mriyu.com/weather_bot?start=promo42 — после «Начать» вы получите обновление start с "payload": "promo42". Параметр — до 64 символов: латиница, цифры, _, -. Удобно для реферальных ссылок и рекламы.

Лимиты

Что Сколько
Сообщений от бота 30 в секунду всего, 20 в минуту одному человеку, 20 в минуту в один чат
Запросов к API с одного IP 600 в минуту
Хранение обновлений 24 часа
Ответ вебхука 5 секунд
Ботов у одного разработчика 20

Превысили — ответ 429: подождите и повторите. Для рассылки многим людям отправляйте по 25–30 сообщений в секунду.

Готовые примеры

Node.js (18+), без сервера — getUpdates

const TOKEN = 'ВАШ_ТОКЕН';
const call = (method, data = {}) => fetch('https://mriyu.com/api/bot/v1/' + method, {
  method: 'POST',
  headers: {'Authorization': 'Bearer ' + TOKEN, 'Content-Type': 'application/json'},
  body: JSON.stringify(data),
}).then((r) => r.json());

let offset = 0;
async function loop() {
  const {ok, result} = await call('getUpdates', {offset});
  if (ok) {
    for (const update of result) {
      offset = update.update_id + 1;
      if (update.type === 'start') {
        await call('sendMessage', {
          user_id: update.from.id,
          text: 'Привет, **' + update.from.name + '**! Подписаться на прогноз?',
          buttons: [[{text: 'Да', data: 'yes'}, {text: 'Нет', data: 'no'}]],
        });
      } else if (update.type === 'button') {
        await call('editMessage', {
          message_id: update.button.message_id,
          text: update.button.data === 'yes' ? 'Подписка оформлена ✅' : 'Хорошо, не будем.',
          buttons: null,
        });
      } else if (update.type === 'message') {
        await call('sendMessage', {user_id: update.from.id, text: 'Вы написали: ' + update.message.text});
      }
    }
  }
  setTimeout(loop, 1500);
}
loop();

PHP — вебхук

<?php
// Файл https://example.com/mriyu-bot.php — укажите его адрес в setWebhook.
const TOKEN = 'ВАШ_ТОКЕН';
const SECRET = 'ВАШ_СЕКРЕТ_ВЕБХУКА';

if (($_SERVER['HTTP_X_MRIYU_BOT_SECRET'] ?? '') !== SECRET) { http_response_code(403); exit; }
$update = json_decode(file_get_contents('php://input'), true);
http_response_code(200);

function call(string $method, array $data): array {
    $ch = curl_init('https://mriyu.com/api/bot/v1/' . $method);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . TOKEN, 'Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode($data),
    ]);
    return json_decode(curl_exec($ch), true);
}

if (($update['type'] ?? '') === 'message') {
    $command = $update['message']['command']['name'] ?? null;
    $text = $command === 'help' ? 'Я умею: /forecast <город>' : 'Напишите /help';
    call('sendMessage', ['user_id' => $update['from']['id'], 'text' => $text]);
}

Python 3 — getUpdates

import time, requests

TOKEN = 'ВАШ_ТОКЕН'
API = 'https://mriyu.com/api/bot/v1/'
HEADERS = {'Authorization': 'Bearer ' + TOKEN}

def call(method, **data):
    return requests.post(API + method, json=data, headers=HEADERS, timeout=10).json()

offset = 0
while True:
    answer = call('getUpdates', offset=offset)
    for update in answer.get('result', []):
        offset = update['update_id'] + 1
        if update['type'] == 'message':
            call('sendMessage', user_id=update['from']['id'], text='Эхо: ' + update['message']['text'])
    time.sleep(1.5)

curl — проверить токен и отправить сообщение

curl -X POST https://mriyu.com/api/bot/v1/getMe -H "Authorization: Bearer ВАШ_ТОКЕН"

curl -X POST https://mriyu.com/api/bot/v1/sendMessage \
  -H "Authorization: Bearer ВАШ_ТОКЕН" -H "Content-Type: application/json" \
  -d '{"user_id": 15, "text": "Привет!", "buttons": [[{"text": "Сайт", "url": "https://example.com"}]]}'

Безопасность и правила

  • Токен — пароль бота. Не публикуйте его в открытом коде и на клиенте (в браузере, в приложении). Утёк — кабинет → бот → «Подключение» → «Новый токен»: старый перестанет работать сразу.
  • Проверяйте X-Mriyu-Bot-Secret в вебхуке.
  • Бот получает имя и адрес страницы тех, кто ему пишет. Не собирайте лишнего и не передавайте эти данные третьим лицам.
  • Запрещены спам и рассылки тем, кто не просил, мошенничество, сбор паролей и платёжных данных, контент 18+.
  • Пользователи могут пожаловаться на бота (в чате «⋯» → «Пожаловаться на бота»). За нарушения администрация блокирует бота — токен перестаёт работать, а причина видна в кабинете.
  • Проверенным ботам администрация ставит галочку «Официальный».

Чего пока нет

  • Голосовые и видео от бота.
  • Платежи через бота.

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

Бот не отвечает. Проверьте токен (getMe). Без вебхука — запущен ли ваш код с getUpdates. С вебхуком — getWebhookInfo: если там last_error, ваш сервер не ответил 2xx за 5 секунд.

403 при отправке. Человек не нажимал «Начать», остановил бота или заблокировал его. Ждите, пока он сам напишет.

Обновления приходят повторно. С getUpdates передавайте offset = последний update_id + 1. С вебхуком — отвечайте 2xx: иначе обновление придёт снова.

Как сделать рассылку? getSubscribers → для каждого sendMessage, не быстрее 30 в секунду. Для большой аудитории удобнее канал: добавьте бота администратором и публикуйте через sendChatMessage.

Бот в группе не реагирует на сообщения. Без «Видит все сообщения» он получает только команды, упоминания @адрес_бота и ответы на свои сообщения.

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

См. также: Боты для пользователей, Разработчикам игр.