Bot API позволяет создавать ботов для мессенджера WWChat.
Начало работы
Базовый URL
https://api.wwchat.org/bot/v1/{token}/ Аутентификация
Бот аутентифицируется по токену в URL. Токен выдается при создании бота.
Формат токена: {user_id}:{random_string}
Пример: 550e8400-e29b-41d4-a716-446655440000:aB3dE5fG7hJ9kL1mN3pQ5rS7tU9vW1xY3zA5bC7dE9fG1
Создание бота
Через @BotMama:
- Откройте чат с @BotMama
- Отправьте команду
/newbot - Введите имя бота
- Введите username (должен заканчиваться на
bot) - Получите токен
Формат ответов
Все методы возвращают 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_id | String | Да | UUID чата или @username |
| text | String | Да | Текст сообщения |
| parse_mode | String | Нет | Форматирование: HTML или Markdown |
| reply_to_message_id | String | Нет | UUID сообщения для ответа |
| disable_notification | Boolean | Нет | Отключить уведомление |
| reply_markup | InlineKeyboardMarkup | Нет | 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
| Параметр | Тип | Описание |
|---|---|---|
| offset | Integer | ID первого update для возврата |
| limit | Integer | Макс. количество (1-100) |
| timeout | Integer | Таймаут long polling (макс. 60 сек) |
| allowed_updates | Array | Фильтр типов updates |
Важно: После получения updates, отправьте следующий запрос с offset = last_update_id + 1 чтобы подтвердить получение.
editMessageText
Редактировать текст сообщения.
Запрос: POST /bot/v1/{token}/editMessageText
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| chat_id | String | Да | UUID чата |
| message_id | String | Да | UUID сообщения |
| text | String | Да | Новый текст |
| reply_markup | InlineKeyboardMarkup | Нет | Новая клавиатура |
Важно: Можно редактировать только сообщения, отправленные ботом.
answerCallbackQuery
Ответить на нажатие inline-кнопки.
Запрос: POST /bot/v1/{token}/answerCallbackQuery
| Параметр | Тип | Описание |
|---|---|---|
| callback_query_id | String | ID callback query (обязательно) |
| text | String | Текст уведомления (до 200 символов) |
| show_alert | Boolean | Показать alert вместо toast |
| cache_time | Integer | Время кэширования ответа (секунды) |
Важно: Вызывайте этот метод даже если не нужно показывать уведомление.
getChat
Получить информацию о чате.
Запрос: GET/POST /bot/v1/{token}/getChat
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| chat_id | String | Да | 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)
| Параметр | Тип | Описание |
|---|---|---|
| file | File | Файл для загрузки (макс. 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_id | String | Да | UUID чата |
| document | String | Да | file_id загруженного файла |
| caption | String | Нет | Подпись к документу |
| parse_mode | String | Нет | Форматирование подписи |
| reply_markup | InlineKeyboardMarkup | Нет | Inline-клавиатура |
sendPhoto
Отправить фото.
Запрос: POST /bot/v1/{token}/sendPhoto
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| chat_id | String | Да | UUID чата |
| photo | String | Да | file_id загруженного изображения |
| caption | String | Нет | Подпись к фото |
| parse_mode | String | Нет | Форматирование подписи |
| reply_markup | InlineKeyboardMarkup | Нет | Inline-клавиатура |
sendVoice
Отправить голосовое сообщение.
Запрос: POST /bot/v1/{token}/sendVoice
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| chat_id | String | Да | UUID чата |
| voice | String | Да | file_id аудиофайла |
| caption | String | Нет | Подпись |
| duration | Integer | Нет | Длительность в секундах |
| reply_markup | InlineKeyboardMarkup | Нет | Inline-клавиатура |
Оплата звёздами (Star Payments)
Боты могут принимать оплату звёздами.
Флоу оплаты
- Бот вызывает
sendInvoice→ пользователь видит сообщение с кнопкой «Pay» - Пользователь нажимает «Pay» → клиент показывает модальное окно с деталями
- Пользователь подтверждает → сервер отправляет боту update
pre_checkout_query - Бот вызывает
answerPreCheckoutQuery(ok: true/false) — в течение 10 секунд - При ok=true → звёзды списываются у пользователя и зачисляются боту
- Бот получает update с
successful_paymentв message
sendInvoice
Отправить счёт на оплату звёздами.
Запрос: POST /bot/v1/{token}/sendInvoice
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| chat_id | String | Да | UUID чата или @username |
| title | String | Да | Название товара/услуги (до 255 символов) |
| description | String | Да | Описание (до 1000 символов) |
| amount | Float | Да | Сумма в звёздах (> 0) |
| photo_url | String | Нет | URL изображения товара |
| payload | String | Да | Данные для бота (до 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_id | String | Да | UUID из pre_checkout_query update |
| ok | Boolean | Да | true — подтвердить, false — отклонить |
| error_message | String | Нет | Сообщение об ошибке (при 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
| Параметр | Тип | Описание |
|---|---|---|
| url | String | HTTPS URL для webhook (обязательно) |
| secret_token | String | Секрет для header X-WWChat-Bot-Api-Secret-Token |
| max_connections | Integer | Макс. подключений (1-100) |
| allowed_updates | Array | Фильтр типов 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
| Поле | Тип | Описание |
|---|---|---|
| text | String | Текст на кнопке (обязательно) |
| callback_data | String | Данные для callback (до 64 байт) |
| url | String | URL для открытия |
| pay | Boolean | true для кнопки оплаты (только в invoice) |
User
| Поле | Тип | Описание |
|---|---|---|
| id | String | UUID пользователя |
| username | String | Username |
| is_bot | Boolean | true если это бот |
| is_deleted | Boolean | true если удалён |
Chat
| Поле | Тип | Описание |
|---|---|---|
| id | String | UUID чата |
| type | String | private, group, channel |
| title | String | Название (для групп/каналов) |
| username | String | Username группы/канала |
Боты в группах и каналах
Боты могут быть добавлены в группы и каналы. При добавлении бот получает 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
- Сообщения в группе →
message(chat.type = "group") - Посты в канале →
channel_post(chat.type = "channel") - Добавление/удаление →
my_chat_member
Примеры
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_data | 64 байт |
Сообщения
| Лимит | Значение |
|---|---|
| Текст сообщения | 4096 символов |
| Invoice title | 255 символов |
| Invoice description | 1000 символов |
| Invoice payload | 512 символов |
Прочее
- Long polling timeout: максимум 60 секунд
- Updates limit: максимум 100 за запрос
- Webhook URL: только HTTPS
- Webhook max_connections: 1-100
- Загрузка файлов: максимум 50 МБ
- Максимум 20 ботов на пользователя (включая удалённых)
- Username бота занимается навсегда (даже после удаления)
Системные боты
@BotMama
Системный бот для создания и управления ботами:
/newbot— создать нового бота/mybots— список моих ботов/editbot— редактировать бота (имя, описание, аватарка)/deletebot— удалить бота/token— получить новый токен