Bot API

RU EN

Bot API позволяет создавать ботов для мессенджера WWChat.

Начало работы

Базовый URL

https://api.wwchat.org/bot/v1/{token}/ 

Аутентификация

Бот аутентифицируется по токену в URL. Токен выдается при создании бота.

Формат токена: {user_id}:{random_string}

Пример: 550e8400-e29b-41d4-a716-446655440000:aB3dE5fG7hJ9kL1mN3pQ5rS7tU9vW1xY3zA5bC7dE9fG1

Создание бота

Через @BotMama:

  1. Откройте чат с @BotMama
  2. Отправьте команду /newbot
  3. Введите имя бота
  4. Введите username (должен заканчиваться на bot)
  5. Получите токен

Формат ответов

Все методы возвращают JSON:

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

При ошибке:

{
  "ok": false,
  "error_code": 400,
  "description": "Error message"
}

Методы API

getMe

Получить информацию о боте.

Запрос: GET /bot/v1/{token}/getMe

{
  "ok": true,
  "result": {
    "id": "uuid",
    "username": "MyBot",
    "description": "Bot description",
    "is_bot": true,
    "can_join_groups": true,
    "can_read_group_messages": true,
    "can_join_channels": false
  }
}

sendMessage

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

Запрос: POST /bot/v1/{token}/sendMessage

ПараметрТипОбязательныйОписание
chat_idStringДаUUID чата или @username
textStringДаТекст сообщения
parse_modeStringНетФорматирование: HTML или Markdown
reply_to_message_idStringНетUUID сообщения для ответа
disable_notificationBooleanНетОтключить уведомление
reply_markupInlineKeyboardMarkupНетInline-клавиатура

Пример с inline-клавиатурой:

{
  "chat_id": "550e8400-e29b-41d4-a716-446655440000",
  "text": "Выберите действие:",
  "reply_markup": {
    "inline_keyboard": [
      [
        {"text": "Кнопка 1", "callback_data": "btn1"},
        {"text": "Кнопка 2", "callback_data": "btn2"}
      ],
      [
        {"text": "Открыть сайт", "url": "https://example.com"}
      ]
    ]
  }
}

getUpdates

Получить обновления (long polling).

Запрос: GET /bot/v1/{token}/getUpdates

ПараметрТипОписание
offsetIntegerID первого update для возврата
limitIntegerМакс. количество (1-100)
timeoutIntegerТаймаут long polling (макс. 60 сек)
allowed_updatesArrayФильтр типов updates

Важно: После получения updates, отправьте следующий запрос с offset = last_update_id + 1 чтобы подтвердить получение.


editMessageText

Редактировать текст сообщения.

Запрос: POST /bot/v1/{token}/editMessageText

ПараметрТипОбязательныйОписание
chat_idStringДаUUID чата
message_idStringДаUUID сообщения
textStringДаНовый текст
reply_markupInlineKeyboardMarkupНетНовая клавиатура

Важно: Можно редактировать только сообщения, отправленные ботом.


answerCallbackQuery

Ответить на нажатие inline-кнопки.

Запрос: POST /bot/v1/{token}/answerCallbackQuery

ПараметрТипОписание
callback_query_idStringID callback query (обязательно)
textStringТекст уведомления (до 200 символов)
show_alertBooleanПоказать alert вместо toast
cache_timeIntegerВремя кэширования ответа (секунды)

Важно: Вызывайте этот метод даже если не нужно показывать уведомление.


getChat

Получить информацию о чате.

Запрос: GET/POST /bot/v1/{token}/getChat

ПараметрТипОбязательныйОписание
chat_idStringДаUUID чата

Ответ (приватный чат):

{
  "ok": true,
  "result": {
    "id": "uuid",
    "type": "private",
    "username": "john_doe",
    "first_name": "john_doe",
    "bio": "Hello!",
    "language": "ru",
    "is_bot": false
  }
}

Ответ (группа):

{
  "ok": true,
  "result": {
    "id": "uuid",
    "type": "group",
    "title": "My Group",
    "username": "mygroup",
    "description": "Group description",
    "members_count": 42,
    "is_public": true
  }
}

Поле language возвращает язык пользователя — полезно для локализации сообщений бота.


Файлы

uploadFile

Загрузить файл для последующей отправки.

Запрос: POST /bot/v1/{token}/uploadFile (multipart/form-data)

ПараметрТипОписание
fileFileФайл для загрузки (макс. 50 МБ)
{
  "ok": true,
  "result": {
    "id": "uuid",
    "file_name": "document.pdf",
    "file_size": 102400,
    "mime_type": "application/pdf",
    "media_type": "document"
  }
}

sendDocument

Отправить документ.

Запрос: POST /bot/v1/{token}/sendDocument

ПараметрТипОбязательныйОписание
chat_idStringДаUUID чата
documentStringДаfile_id загруженного файла
captionStringНетПодпись к документу
parse_modeStringНетФорматирование подписи
reply_markupInlineKeyboardMarkupНетInline-клавиатура

sendPhoto

Отправить фото.

Запрос: POST /bot/v1/{token}/sendPhoto

ПараметрТипОбязательныйОписание
chat_idStringДаUUID чата
photoStringДаfile_id загруженного изображения
captionStringНетПодпись к фото
parse_modeStringНетФорматирование подписи
reply_markupInlineKeyboardMarkupНетInline-клавиатура

sendVoice

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

Запрос: POST /bot/v1/{token}/sendVoice

ПараметрТипОбязательныйОписание
chat_idStringДаUUID чата
voiceStringДаfile_id аудиофайла
captionStringНетПодпись
durationIntegerНетДлительность в секундах
reply_markupInlineKeyboardMarkupНетInline-клавиатура

Оплата звёздами (Star Payments)

Боты могут принимать оплату звёздами.

Флоу оплаты

  1. Бот вызывает sendInvoice → пользователь видит сообщение с кнопкой «Pay»
  2. Пользователь нажимает «Pay» → клиент показывает модальное окно с деталями
  3. Пользователь подтверждает → сервер отправляет боту update pre_checkout_query
  4. Бот вызывает answerPreCheckoutQuery (ok: true/false) — в течение 10 секунд
  5. При ok=true → звёзды списываются у пользователя и зачисляются боту
  6. Бот получает update с successful_payment в message

sendInvoice

Отправить счёт на оплату звёздами.

Запрос: POST /bot/v1/{token}/sendInvoice

ПараметрТипОбязательныйОписание
chat_idStringДаUUID чата или @username
titleStringДаНазвание товара/услуги (до 255 символов)
descriptionStringДаОписание (до 1000 символов)
amountFloatДаСумма в звёздах (> 0)
photo_urlStringНетURL изображения товара
payloadStringДаДанные для бота (до 512 символов, не видны пользователю)

Пример:

{
  "chat_id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Premium подписка",
  "description": "Доступ к premium функциям на 30 дней",
  "amount": 10.0,
  "photo_url": "https://example.com/premium.png",
  "payload": "premium_30d_user123"
}

answerPreCheckoutQuery

Ответить на pre-checkout запрос. Бот должен ответить в течение 10 секунд, иначе оплата будет отменена.

Запрос: POST /bot/v1/{token}/answerPreCheckoutQuery

ПараметрТипОбязательныйОписание
pre_checkout_query_idStringДаUUID из pre_checkout_query update
okBooleanДаtrue — подтвердить, false — отклонить
error_messageStringНетСообщение об ошибке (при ok=false)

Пример (подтверждение):

{
  "pre_checkout_query_id": "550e8400-e29b-41d4-a716-446655440000",
  "ok": true
}

Пример (отклонение):

{
  "pre_checkout_query_id": "550e8400-e29b-41d4-a716-446655440000",
  "ok": false,
  "error_message": "Товар закончился"
}

getStarBalance

Получить баланс звёзд бота.

Запрос: GET/POST /bot/v1/{token}/getStarBalance

{
  "ok": true,
  "result": {
    "balance": 150.5,
    "pending": 0
  }
}

Webhooks

setWebhook

Установить webhook для получения updates.

Запрос: POST /bot/v1/{token}/setWebhook

ПараметрТипОписание
urlStringHTTPS URL для webhook (обязательно)
secret_tokenStringСекрет для header X-WWChat-Bot-Api-Secret-Token
max_connectionsIntegerМакс. подключений (1-100)
allowed_updatesArrayФильтр типов updates

deleteWebhook

Запрос: POST /bot/v1/{token}/deleteWebhook

getWebhookInfo

Запрос: GET /bot/v1/{token}/getWebhookInfo

Формат webhook-запроса

POST https://mybot.example.com/webhook
Content-Type: application/json
X-WWChat-Bot-Api-Secret-Token: my_secret_123

{
  "update_id": 123456789,
  "message": { ... }
}

Ваш сервер должен вернуть HTTP 200 OK. После 10 последовательных ошибок webhook автоматически отключается.

Важно: При активном webhook метод getUpdates недоступен (вернёт 409 Conflict).


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

Методы sendMessage, editMessageText, sendDocument, sendPhoto поддерживают параметр parse_mode.

HTML

{
  "text": "<b>жирный</b>, <i>курсив</i>, <u>подчёркнутый</u>, <s>зачёркнутый</s>, <code>код</code>, <pre>блок кода</pre>, <a href=\"url\">ссылка</a>",
  "parse_mode": "HTML"
}

Теги: <b>, <strong>, <i>, <em>, <u>, <s>, <code>, <pre>, <a href="url">

Markdown

{
  "text": "**жирный**, *курсив*, `код`, ~~зачёркнутый~~, [ссылка](https://example.com)",
  "parse_mode": "Markdown"
}

Типы объектов

Update

{
  "update_id": 123456789,
  "message": { ... },            // новое сообщение
  "callback_query": { ... },     // нажатие inline-кнопки
  "channel_post": { ... },       // новый пост в канале
  "my_chat_member": { ... },     // бот добавлен/удалён из чата
  "pre_checkout_query": { ... }  // запрос подтверждения оплаты
}

Update содержит один из типов событий.

Message

{
  "message_id": "uuid",
  "from": { "id": "uuid", "username": "john", "is_bot": false },
  "chat": { "id": "uuid", "type": "private" },
  "date": 1705123456,
  "text": "Hello",
  "entities": [ ... ],
  "reply_to_message": { ... },
  "reply_markup": { ... },
  "photo": [ ... ],
  "document": { ... },
  "voice": { ... },
  "successful_payment": { ... }
}

CallbackQuery

{
  "id": "unique_callback_id",
  "from": { "id": "uuid", "username": "john" },
  "message": { ... },
  "chat_instance": "uuid",
  "data": "btn1"
}

PreCheckoutQuery

{
  "id": "uuid",
  "from": { "id": "uuid", "username": "john", "is_bot": false },
  "currency": "XTR",
  "total_amount": 1000,
  "invoice_payload": "premium_30d_user123"
}

total_amount в units (100 units = 1 звезда). currency всегда "XTR".

SuccessfulPayment

Содержится в поле successful_payment объекта Message после завершения оплаты.

{
  "currency": "XTR",
  "total_amount": 1000,
  "invoice_payload": "premium_30d_user123",
  "provider_payment_charge_id": "uuid"
}

MyChatMember

Уведомление о добавлении или удалении бота из группы/канала.

{
  "chat": { "id": "uuid", "type": "group", "title": "Моя группа" },
  "from": { "id": "uuid", "username": "admin" },
  "old_chat_member": { "status": "left", "user": { ... } },
  "new_chat_member": { "status": "member", "user": { ... } }
}

Статусы: member, left, kicked.

InlineKeyboardMarkup

{
  "inline_keyboard": [
    [
      { "text": "Кнопка", "callback_data": "btn1" }
    ],
    [
      { "text": "Сайт", "url": "https://example.com" }
    ]
  ]
}

InlineKeyboardButton

ПолеТипОписание
textStringТекст на кнопке (обязательно)
callback_dataStringДанные для callback (до 64 байт)
urlStringURL для открытия
payBooleantrue для кнопки оплаты (только в invoice)

User

ПолеТипОписание
idStringUUID пользователя
usernameStringUsername
is_botBooleantrue если это бот
is_deletedBooleantrue если удалён

Chat

ПолеТипОписание
idStringUUID чата
typeStringprivate, group, channel
titleStringНазвание (для групп/каналов)
usernameStringUsername группы/канала

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

Боты могут быть добавлены в группы и каналы. При добавлении бот получает update my_chat_member.

Отправка в группы

POST /bot/v1/{token}/sendMessage
{ "chat_id": "@group_username", "text": "Hello group!" }

Поддерживается chat_id как UUID или @username.

Отправка в каналы

POST /bot/v1/{token}/sendMessage
{ "chat_id": "@channel_username", "text": "Новый пост!" }

При отправке в канал создаётся полноценный пост. Поддерживаются sendDocument, sendPhoto, sendVoice.

Получение updates


Примеры

import requests
import time

TOKEN = "550e8400-...:aB3dE5fG7h..."
BASE_URL = f"https://api.wwchat.org/bot/v1/{TOKEN}"

def get_updates(offset=None):
    params = {"timeout": 30}
    if offset:
        params["offset"] = offset
    resp = requests.get(f"{BASE_URL}/getUpdates", params=params)
    return resp.json()

def send_message(chat_id, text):
    resp = requests.post(f"{BASE_URL}/sendMessage", json={
        "chat_id": chat_id,
        "text": text
    })
    return resp.json()

def main():
    offset = None
    print("Bot started!")

    while True:
        result = get_updates(offset)

        if not result.get("ok"):
            time.sleep(5)
            continue

        for update in result.get("result", []):
            offset = update["update_id"] + 1

            if "message" in update:
                msg = update["message"]
                chat_id = msg["chat"]["id"]
                text = msg.get("text", "")

                if text.startswith("/start"):
                    send_message(chat_id, "Привет! Я бот WWChat.")
                elif text.startswith("/help"):
                    send_message(chat_id, "Команды:\n/start\n/help")
                else:
                    send_message(chat_id, f"Вы написали: {text}")

if __name__ == "__main__":
    main()
const TOKEN = "550e8400-...:aB3dE5fG7h...";
const BASE_URL = `https://api.wwchat.org/bot/v1/${TOKEN}`;

async function getUpdates(offset) {
  const params = new URLSearchParams({ timeout: "30" });
  if (offset) params.set("offset", offset);

  const resp = await fetch(`${BASE_URL}/getUpdates?${params}`);
  return resp.json();
}

async function sendMessage(chatId, text) {
  const resp = await fetch(`${BASE_URL}/sendMessage`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ chat_id: chatId, text }),
  });
  return resp.json();
}

async function main() {
  let offset = null;
  console.log("Bot started!");

  while (true) {
    const result = await getUpdates(offset);

    if (!result.ok) {
      await new Promise((r) => setTimeout(r, 5000));
      continue;
    }

    for (const update of result.result) {
      offset = update.update_id + 1;

      if (update.message) {
        const chatId = update.message.chat.id;
        const text = update.message.text || "";

        if (text.startsWith("/start")) {
          await sendMessage(chatId, "Привет! Я бот WWChat.");
        } else if (text.startsWith("/help")) {
          await sendMessage(chatId, "Команды:\n/start\n/help");
        } else {
          await sendMessage(chatId, `Вы написали: ${text}`);
        }
      }
    }
  }
}

main();
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"
)

const token = "550e8400-...:aB3dE5fG7h..."
const baseURL = "https://api.wwchat.org/bot/v1/" + token

type Update struct {
	UpdateID int     `json:"update_id"`
	Message  *Message `json:"message"`
}

type Message struct {
	MessageID string `json:"message_id"`
	From      struct {
		ID       string `json:"id"`
		Username string `json:"username"`
	} `json:"from"`
	Chat struct {
		ID   string `json:"id"`
		Type string `json:"type"`
	} `json:"chat"`
	Text string `json:"text"`
}

type APIResponse struct {
	OK     bool     `json:"ok"`
	Result []Update `json:"result"`
}

func getUpdates(offset int) (*APIResponse, error) {
	url := fmt.Sprintf("%s/getUpdates?timeout=30", baseURL)
	if offset > 0 {
		url += fmt.Sprintf("&offset=%d", offset)
	}
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var result APIResponse
	json.NewDecoder(resp.Body).Decode(&result)
	return &result, nil
}

func sendMessage(chatID, text string) {
	body, _ := json.Marshal(map[string]string{
		"chat_id": chatID,
		"text":    text,
	})
	http.Post(baseURL+"/sendMessage", "application/json",
		bytes.NewReader(body))
}

func main() {
	offset := 0
	fmt.Println("Bot started!")

	for {
		result, err := getUpdates(offset)
		if err != nil || !result.OK {
			time.Sleep(5 * time.Second)
			continue
		}

		for _, update := range result.Result {
			offset = update.UpdateID + 1

			if update.Message == nil {
				continue
			}

			chatID := update.Message.Chat.ID
			text := update.Message.Text

			switch {
			case strings.HasPrefix(text, "/start"):
				sendMessage(chatID, "Привет! Я бот WWChat.")
			case strings.HasPrefix(text, "/help"):
				sendMessage(chatID, "Команды:\n/start\n/help")
			default:
				sendMessage(chatID, "Вы написали: "+text)
			}
		}
	}
}
using System.Net.Http.Json;
using System.Text.Json;

const string token = "550e8400-...:aB3dE5fG7h...";
const string baseUrl = $"https://api.wwchat.org/bot/v1/{token}";

using var http = new HttpClient();
int? offset = null;
Console.WriteLine("Bot started!");

while (true)
{
    var url = $"{baseUrl}/getUpdates?timeout=30";
    if (offset.HasValue) url += $"&offset={offset}";

    try
    {
        var resp = await http.GetFromJsonAsync<JsonElement>(url);
        if (!resp.GetProperty("ok").GetBoolean())
        {
            await Task.Delay(5000);
            continue;
        }

        foreach (var update in resp.GetProperty("result").EnumerateArray())
        {
            offset = update.GetProperty("update_id").GetInt32() + 1;

            if (!update.TryGetProperty("message", out var msg))
                continue;

            var chatId = msg.GetProperty("chat").GetProperty("id").GetString()!;
            var text = msg.TryGetProperty("text", out var t) ? t.GetString() ?? "" : "";

            var reply = text switch
            {
                _ when text.StartsWith("/start") => "Привет! Я бот WWChat.",
                _ when text.StartsWith("/help") => "Команды:\n/start\n/help",
                _ => $"Вы написали: {text}"
            };

            await http.PostAsJsonAsync($"{baseUrl}/sendMessage", new
            {
                chat_id = chatId,
                text = reply
            });
        }
    }
    catch
    {
        await Task.Delay(5000);
    }
}
<?php
$token = "550e8400-...:aB3dE5fG7h...";
$baseUrl = "https://api.wwchat.org/bot/v1/$token";

function getUpdates(string $baseUrl, ?int $offset = null): array {
    $url = "$baseUrl/getUpdates?timeout=30";
    if ($offset !== null) $url .= "&offset=$offset";

    $resp = file_get_contents($url);
    return json_decode($resp, true);
}

function sendMessage(string $baseUrl, string $chatId, string $text): void {
    $opts = [
        'http' => [
            'method'  => 'POST',
            'header'  => 'Content-Type: application/json',
            'content' => json_encode([
                'chat_id' => $chatId,
                'text'    => $text,
            ]),
        ],
    ];
    file_get_contents($baseUrl . "/sendMessage",
        false, stream_context_create($opts));
}

$offset = null;
echo "Bot started!\n";

while (true) {
    $result = getUpdates($baseUrl, $offset);

    if (!($result['ok'] ?? false)) {
        sleep(5);
        continue;
    }

    foreach ($result['result'] as $update) {
        $offset = $update['update_id'] + 1;

        if (!isset($update['message'])) continue;

        $chatId = $update['message']['chat']['id'];
        $text = $update['message']['text'] ?? '';

        if (str_starts_with($text, '/start')) {
            sendMessage($baseUrl, $chatId, "Привет! Я бот WWChat.");
        } elseif (str_starts_with($text, '/help')) {
            sendMessage($baseUrl, $chatId, "Команды:\n/start\n/help");
        } else {
            sendMessage($baseUrl, $chatId, "Вы написали: $text");
        }
    }
}

Ограничения

Rate limits

ЛимитЗначение
Запросов в секунду (все методы)30 на бота
Сообщений в секунду на чат1 на бота
Сообщений в минуту на чат20 на бота

При превышении лимита возвращается 429 Too Many Requests с заголовком Retry-After: 1.

Лимиты на сообщения (per-chat) применяются к методам: sendMessage, sendDocument, sendPhoto, sendVoice, sendInvoice.

Inline-клавиатура

ЛимитЗначение
Максимум рядов25
Максимум кнопок в ряду8
Максимум кнопок всего100
Текст кнопки256 байт
callback_data64 байт

Сообщения

ЛимитЗначение
Текст сообщения4096 символов
Invoice title255 символов
Invoice description1000 символов
Invoice payload512 символов

Прочее

Системные боты

@BotMama

Системный бот для создания и управления ботами: