CloudGram API
Один модуль: боты, свой аккаунт и доступ к аккаунтам пользователей вашего сайта.
Адреса, заголовки, коды ошибок и опрос обновлений настроены внутри модуля. Снаружи — только функции из этого справочника.
Первый бот за минуту
Бот создаётся не в коде, а в переписке: у CloudGram есть служебный бот Bot Father, он всё и оформит.
Найдите Bot Father
В поиске чатов наберите @fatherbot и откройте его.
Нажмите «Меню»
Слева от поля ввода появится кнопка «Меню» — она открывает мини-приложение, где живёт всё управление ботами. Печатать команды не нужно.
«Создать бота»
Придумайте имя (его увидят люди в чатах) и юзернейм — латиница, цифры и подчёркивания, обязательно заканчивается на bot. Например: my_helper_bot.
Заберите токен
Он появится в карточке бота, скрытый точками. Нажмите «Показать», затем «Скопировать».
Вставьте токен в код
Скачайте модуль, подставьте токен — и бот отвечает. Настраивать больше нечего.
const cloudgram = require("./cloudgram.js");
const bot = cloudgram.bot("ВАШ_ТОКЕН");
bot.onMessage((msg) => msg.reply("Привет, " + msg.from.first_name + "!"));
bot.start();
В том же мини-приложении задаются аватарка, описание, команды-подсказки и мини-приложение самого бота.
Что такое токен и почему его берегут
Токен — это и логин, и пароль бота в одной строке вида 5:c229bf39…. Сервер не спрашивает больше ничего: пришёл верный токен — значит, пришёл владелец бота.
| Что можно с токеном | Последствие |
|---|---|
| Читать входящие сообщения | чужой человек увидит переписку ваших пользователей с ботом |
| Писать от имени бота | можно рассылать что угодно от вашего имени |
| Менять кнопки и мини-приложение | кнопку можно увести на чужой сайт |
Как хранить правильно:
- держите токен в переменной окружения, а не в тексте программы;
- не коммитьте файл с токеном в репозиторий — добавьте его в .gitignore;
- если токен где-то засветился, сразу нажмите «Сбросить» в карточке бота: старый перестанет работать в ту же секунду.
const bot = cloudgram.bot(process.env.CLOUDGRAM_TOKEN);
Установка
Модуль без зависимостей: один файл, ставить ничего не нужно.
Node.js 12+
Скачайте cloudgram.js и положите рядом со своим скриптом:
const cloudgram = require("./cloudgram.js");
Python 3.8+
Скачайте cloudgram.py и положите рядом со своим скриптом:
import cloudgram
Быстрый старт
Бот отвечает на сообщения
const cloudgram = require("./cloudgram.js");
const bot = cloudgram.bot("ТОКЕН_БОТА");
bot.onMessage((msg) => {
msg.reply("Привет, " + msg.from.first_name + "!");
});
bot.start();
import cloudgram
bot = cloudgram.bot("ТОКЕН_БОТА")
@bot.on_message
def handle(msg):
msg.reply("Привет, " + msg.sender_name + "!")
bot.start()
Свой аккаунт
const account = cloudgram.account("CLIENT_ID", "CLIENT_SECRET");
const chats = await account.chats();
await account.send(chats[0].id, "Сообщение из скрипта");
account = cloudgram.account("CLIENT_ID", "CLIENT_SECRET")
chats = account.chats()
account.send(chats[0]["id"], "Сообщение из скрипта")
Сайт подключает аккаунт пользователя
Шаг 1 — отправьте пользователя на страницу согласия. Шаг 2 — обменяйте код из адреса возврата на подключение.
const site = cloudgram.site({ name: "Моя CRM", redirectUri: "https://ваш-сайт/callback" });
// 1. куда отправить пользователя
const url = site.link(myRandomState);
// 2. на адресе возврата пришёл code
const session = await site.connect(code);
const chats = await session.chats();
site = cloudgram.site("https://ваш-сайт/callback", name="Моя CRM")
url = site.link(my_random_state)
session = site.connect(code)
chats = session.chats()
Ключи
| Что нужно | Где взять |
|---|---|
| Токен бота | команда /newbot в чате с ботом @newbot, там же /token для сброса |
| client_id и client_secret | страница создания доступа, нужна корона |
| Разрешение адреса возврата | адрес добавляет владелец CloudGram; проверить — site.checkRedirect() |
Функции: бот
Создание: cloudgram.bot(token) — в Python так же.
Приём сообщений
| Node.js | Python | Что делает |
|---|---|---|
| bot.onMessage(fn) | @bot.on_message | вызывает fn на каждое входящее сообщение |
| bot.onCommand(cmd, fn) | @bot.on_command(cmd) | вызывает fn только на указанную команду, например "/start" |
| bot.onError(fn) | @bot.on_error | вызывает fn при ошибке опроса; без него ошибки печатаются в консоль |
| bot.start() | bot.start() | начинает получать сообщения; в Node работает в фоне, в Python блокирует поток |
| bot.stop() | bot.stop() | прекращает получать сообщения |
Ссылка на бота с меткой
Ссылка вида cloudgram.ru/имя_бота?start=метка открывает чат с ботом и показывает кнопку «Начать». Когда человек её нажимает, боту приходит команда /start метка — по метке видно, откуда он пришёл: из рекламы, по приглашению друга, с конкретной страницы сайта.
/start пришла метка — значит человек перешёл именно по вашей ссылке.bot.onCommand("/start", function (msg) {
var метка = msg.text.split(" ")[1] || "";
if (метка) {
msg.reply("Вы пришли по приглашению: " + метка);
} else {
msg.reply("Здравствуйте!");
}
});| bot.onCallback(fn) | @bot.on_callback | вызывает fn при нажатии callback-кнопки; у нажатия есть data, id, from и answer() |
| bot.getUpdates(opts) | bot.get_updates() | забирает обновления вручную; нужно, только если не используете start |
Нажатия кнопок
Кнопка с полем callback не отправляет ничего в чат — вместо этого бот получает нажатие и отвечает всплывающей подсказкой.
bot.send(chatId, "Продолжить?", { buttons: [
[{ text: "Да", callback: "yes" }, { text: "Нет", callback: "no" }]
]});
bot.onCallback((press) => {
press.answer(press.data === "yes" ? "Поехали!" : "Отменено");
});
| Node.js | Python | Что делает |
|---|---|---|
| press.data | press.data | значение callback нажатой кнопки |
| press.from | press.sender | кто нажал |
| press.answer(text) | press.answer(text) | подсказка пользователю, до 200 символов |
| press.answer(text, {alert:true}) | press.answer(text, alert=True) | то же, но окном, которое нужно закрыть |
Отправка
| Node.js | Python | Что делает |
|---|---|---|
| bot.send(chatId, text) | bot.send(chat_id, text) | отправляет текст, до 4096 символов; возвращает сообщение |
| bot.send(chatId, text, {buttons}) | bot.send(chat_id, text, buttons=...) | то же, но с кнопками под сообщением |
| bot.send(id, text, {format}) | bot.send(id, text, format=...) | то же с разметкой: "html" или "markdown". См. разметку |
| bot.photo(chatId, файл) | bot.photo(chat_id, файл) | отправляет картинку: jpg, png, gif, webp |
| bot.video(chatId, файл) | bot.video(chat_id, файл) | отправляет видео mp4 |
| bot.file(chatId, файл) | bot.file(chat_id, файл) | отправляет любой файл |
| bot.photo(id, файл, {caption}) | bot.photo(id, файл, caption=...) | то же с подписью; подпись понимает format и buttons |
| bot.edit(chatId, msgId, text) | bot.edit(chat_id, msg_id, text) | меняет текст своего отправленного сообщения; принимает format и buttons |
| bot.delete(chatId, msgId) | bot.delete(chat_id, msg_id) | удаляет своё сообщение |
| bot.typing(chatId) | bot.typing(chat_id) | показывает пользователю «печатает…»; bot.typing(id, false) убирает |
| bot.stream(chatId, streamId, text) | bot.stream(chat_id, stream_id, text) | показывает текст, который ещё генерируется; один streamId обновляет тот же черновик |
| bot.stream(id, sid, text, {done:true}) | bot.stream(id, sid, text, done=True) | помечает генерацию законченной |
| bot.getMe() | bot.get_me() | данные бота; заодно проверяет токен |
| bot.call(method, params) | bot.call(method, params) | вызывает метод API напрямую, если он новее модуля |
| msg.reply(text) | msg.reply(text) | отвечает в тот же чат, откуда пришло сообщение |
Файлы
Файл можно передать четырьмя способами, модуль сам разберётся: путь на диске, ссылка http/https, содержимое (Buffer или bytes) либо строка base64.
bot.photo(chatId, "./cat.jpg", { caption: "Котик" });
bot.video(chatId, "https://example.com/clip.mp4");
bot.file(chatId, "./отчёт.pdf", { name: "Отчёт за август.pdf" });
bot.photo(chat_id, "./cat.jpg", caption="Котик")
bot.video(chat_id, "https://example.com/clip.mp4")
bot.file(chat_id, "./отчёт.pdf", name="Отчёт за август.pdf")
| Ограничение | Значение |
|---|---|
| Размер файла | 25 МБ |
| Картинки | jpg, png, gif, webp |
| Видео | mp4 |
| Подпись | 1024 символа |
Команды
Заданные команды всплывают подсказкой, когда пользователь начинает вводить «/», и открываются кнопкой слева в поле ввода.
bot.setCommands([
{ command: "start", description: "Меню и справка" },
{ command: "check", description: "Проверить каталог сейчас" }
]);
bot.set_commands([
{"command": "start", "description": "Меню и справка"},
{"command": "check", "description": "Проверить каталог сейчас"},
])
Кто написал боту
По id из входящего сообщения можно получить данные пользователя — работает для тех, кто уже писал боту.
const who = await bot.getUser(msg.chat.id);
// { id, username, first_name, registered_at, registered_date, verified, avatar_url }
| Поле | Что содержит |
|---|---|
| id | числовой идентификатор |
| username | юзернейм без @ |
| first_name | имя |
| registered_at | дата регистрации, строка |
| registered_date | та же дата в Unix time — удобно считать «сколько дней с нами» |
| verified | подтверждённый аккаунт |
| avatar_url | ссылка на аватарку, если есть |
Мини-приложения
Ваш сайт открывается прямо в CloudGram: в поле ввода появляется кнопка, по ней всплывает окно с приложением. Сверху — название бота, крестик и меню с пунктами «На весь экран», «Перезагрузить», «Закрыть».
| Node.js | Python | Что делает |
|---|---|---|
| bot.setCommands(list) | bot.set_commands(list) | подсказки команд при вводе «/»: [{command, description}] |
| bot.getUser(chatId) | bot.get_user(chat_id) | данные пользователя: id, username, когда зарегистрировался |
| bot.setMenu(url, text) | bot.set_menu(url, text) | ставит кнопку мини-приложения; url обязательно https |
| bot.setMenu() | bot.set_menu() | убирает кнопку |
bot.setMenu("https://example.com", "Открыть карту");
То же самое можно сделать без кода — командой в чате с ботом @newbot:
/setmenu @вашбот https://example.com Открыть карту
Кто открыл приложение
Адрес приложения дополняется данными о пользователе — во фрагменте, после решётки. Фрагмент не уходит на ваш сервер в запросе и не оседает в логах: приложение читает его само.
var p = new URLSearchParams(location.hash.replace(/^#/, ""));
var data = JSON.parse(p.get("cgInitData")); // user, bot, auth_date
var signature = p.get("cgSignature");
| Поле | Что содержит |
|---|---|
| user | кто открыл: id, first_name, username |
| bot | ваш бот: id, username |
| auth_date | время открытия, Unix time |
| cgSignature | подпись данных |
const crypto = require("crypto");
const ok = crypto.createHmac("sha256", ТОКЕН_БОТА).update(cgInitData).digest("hex") === cgSignature;
Команды из приложения
Приложение может управлять окном через postMessage:
parent.postMessage({ type: "cloudgram:close" }, "*"); // закрыть
parent.postMessage({ type: "cloudgram:expand" }, "*"); // на весь экран
Разметка текста
Параметр format у send и edit. Значение "html" — привычные теги, "markdown" — короткие символы. Без параметра текст уходит как есть.
| HTML | Markdown | Результат |
|---|---|---|
| <b>, <strong> | **текст** | жирный |
| <i>, <em> | _текст_ | курсив |
| <u>, <ins> | __текст__ или ++текст++ | подчёркнутый |
| <s>, <del> | ~~текст~~ | зачёркнутый |
| <code>, <pre> | `текст` | моноширинный |
| — | ||текст|| | скрытый текст, открывается по клику |
| <h1>…<h6> | — | заголовок, выглядит как жирный |
| <a href="адрес">текст</a> | — | текст и адрес рядом: «текст (адрес)» |
| <br> | перенос строки | новая строка |
<, >, & раскрываются в обычные.bot.send(chatId, "<b>Готово</b>, файл: <code>отчёт.pdf</code>", { format: "html" });
Функции: аккаунт
Создание: cloudgram.account(clientId, clientSecret). Работает с вашим собственным аккаунтом.
| Node.js | Python | Что делает |
|---|---|---|
| account.me() | account.me() | данные вашего аккаунта |
| account.chats() | account.chats() | список ваших чатов |
| account.messages(chatId) | account.messages(chat_id) | последние сообщения чата, по умолчанию 50 |
| account.messages(id, {limit}) | account.messages(id, limit=...) | то же, сколько последних сообщений вернуть, до 200 |
| account.send(chatId, text) | account.send(chat_id, text) | отправляет текст в чат |
| account.photo(chatId, файл, подпись) | account.photo(chat_id, файл, caption) | отправляет картинку: путь, ссылка или base64 |
| account.video(chatId, файл, подпись) | account.video(chat_id, файл, caption) | отправляет видео (mp4) |
| account.file(chatId, файл, подпись) | account.file(chat_id, файл, caption) | отправляет документ без сжатия |
| account.edit(chatId, messageId, text) | account.edit(chat_id, message_id, text) | правит своё сообщение (текст уходит и в сам мессенджер) |
| account.delete(chatId, messageId) | account.delete(chat_id, message_id) | удаляет своё сообщение — у себя и на платформе (у всех) |
| account.read(chatId) | account.read(chat_id) | отмечает чат прочитанным (сбрасывает счётчик непрочитанных) |
| account.forward(toChatId, fromChatId, messageId) | account.forward(to_chat_id, from_chat_id, message_id) | пересылает сообщение в другой чат (пока только внутренние) |
| account.media(chatId, messageId) | account.media(chat_id, message_id) | скачивает вложение сообщения: Buffer в Node, bytes в Python |
| account.contacts() | account.contacts() | люди, с кем у аккаунта есть внутренний диалог |
Функции: сайт
Создание: cloudgram.site({ name, redirectUri }), в Python — cloudgram.site(redirect_uri, name). Пользователь один раз подтверждает доступ, пароль вам не передаётся.
| Node.js | Python | Что делает |
|---|---|---|
| site.link(state) | site.link(state) | возвращает адрес страницы согласия; state вернётся без изменений, сверьте его |
| site.connect(code) | site.connect(code) | меняет код из адреса возврата на подключение; код одноразовый и живёт 5 минут |
| site.checkRedirect() | site.check_redirect() | проверяет, что ваш адрес возврата разрешён |
При отказе пользователя на адрес возврата придёт error=denied вместо code.
Подключение
Возвращает site.connect(). Сохранённый токен восстанавливается через cloudgram.session(token).
| Node.js | Python | Что делает |
|---|---|---|
| session.token | session.token | токен подключения; храните на своём сервере |
| session.user | session.user | кто выдал доступ |
| session.chats() | session.chats() | диалоги пользователя из всех мессенджеров |
| session.messages(chatId) | session.messages(chat_id) | сообщения диалога |
| session.send(chatId, text) | session.send(chat_id, text) | отправляет сообщение в диалог |
| session.revoke() | session.revoke() | отключает доступ; после этого токен перестаёт работать |
Объекты
Сообщение
| Поле | Тип | Что содержит |
|---|---|---|
| message_id | String | идентификатор сообщения |
| from | Объект | отправитель: id, is_bot, first_name, username |
| chat | Объект | чат: id, type |
| date | Integer | время отправки, Unix time |
| text | String | текст сообщения |
| media_url | String | вложение, если есть; ссылка временная, скачивайте сразу |
В Python у сообщения есть короткие свойства: msg.text, msg.chat_id, msg.sender, msg.sender_name, msg.media_url. Само сообщение при этом остаётся обычным словарём.
Кнопка
Кнопки передаются рядами: массив рядов, в каждом до 3 кнопок, всего до 6 рядов — лишние отбрасываются. Один ряд можно передать без вложенного массива. В url принимаются только адреса http и https.
| Поле | Тип | Что делает |
|---|---|---|
| text | String | надпись на кнопке, до 200 символов |
| url | String | открывает адрес при нажатии |
| send | Boolean | отправляет надпись кнопки в чат как сообщение пользователя |
| copy | String | копирует указанный текст в буфер обмена |
| callback | String | бот получит нажатие с этим значением, до 64 символов; в чат ничего не отправляется |
| app | String | открывает мини-приложение по адресу https; в углу кнопки — значок окошка |
bot.send(chatId, "Выберите действие:", { buttons: [
[{ text: "Каталог", app: "https://example.com/shop" }],
[{ text: "Открыть сайт", url: "https://cloudgram.ru" }],
[{ text: "/start", send: true }, { text: "Скопировать код", copy: "ABC-123" }]
]});
Чат
| Поле | Тип | Что содержит |
|---|---|---|
| id | String | идентификатор чата; для личных совпадает с id пользователя |
| title | String | название чата |
| channel | String | мессенджер: Telegram, VK, MAX, CloudGram |
| preview | String | последнее сообщение |
| unread | Integer | сколько непрочитанных |
Ошибки
При ошибке функция выбрасывает исключение с понятным текстом на русском. В Node это CloudGramError, в Python — cloudgram.CloudGramError.
| Поле | Что содержит |
|---|---|
| message | текст ошибки от сервера |
| code | числовой код: 401 — неверный ключ, 404 — нет доступа или неверный id, 429 — превышен лимит |
| error | машинный код, например chat_not_found |
try {
await account.send(chatId, "текст");
} catch (e) {
console.log(e.code, e.message);
}
try:
account.send(chat_id, "текст")
except cloudgram.CloudGramError as e:
print(e.code, e.message)
Лимиты
| Лимит | Значение |
|---|---|
| Запросы бота | 60 в минуту |
| Отправка сообщений ботом | 20 в минуту |
| Стриминг | 1200 в минуту |
| Запросы аккаунта | 60 в минуту на client_id |
| Длина сообщения | 4096 символов |
| Очередь обновлений | 200 на бота, хранится в памяти сервера |
| Ботов на пользователя | 10 |
При превышении функция выбрасывает ошибку с кодом 429 — повторите позже.