Перейти к основному содержимому

Официальные SDK

Используйте официальные SDK для удобной интеграции Metriox в ваше приложение.

Доступные SDK

JavaScript/TypeScript

metriox-javascript

C#

metriox-csharp

Python

metriox-python


JavaScript: подключение Mini App

JS SDK предназначен для Telegram Mini App: он берёт initData, разделяет свойства по типам, копит события в очереди и сам разбирается с повторами.

<script src="https://cdn.jsdelivr.net/npm/metriox-javascript/dist/metriox-tg-webapp.min.js"></script>
<script>
const mx = window.MetrioxTG.init({
botId: "<YOUR_BOT_ID>",
auth: () => ({ initData: window.Telegram?.WebApp?.initData || "" }),
auto: true,
});

mx.track("purchase_completed", { price: 299 }, { type: "payment" });
</script>

projectId передавать не нужно: проект определяется по botId на сервере. До версии 0.2.0 SDK требовал его при инициализации, хотя ingest это поле никогда не использовал — теперь оно необязательное и ни на что не влияет.

Для React есть отдельная точка входа metriox-javascript/react с провайдером и хуками.

Категория события

Третий аргумент track() задаёт категорию $event.type: message, interaction, payment, membership, business, poll, reaction, boost, platform. Без неё событие записывается как platform.

Методы page() и interaction() проставляют категорию сами. До 0.2.0 SDK отправлял custom и page — таких категорий не существует, и каждое событие приезжало с предупреждением event_type_unknown.

Что собирается автоматически

К каждому событию добавляется контекст запуска и устройства: session_id, seq, tg_platform, tg_client_version, tg_color_scheme, tg_viewport_h, tg_is_expanded, tg_is_fullscreen, tg_is_active, path, referrer, language, timezone, screen_w/screen_h, viewport_w/viewport_h, dpr_x100. Отключается флагом context: false.

Данные из initData при этом не дублируются: пользователь, тип чата, chat_instance и start_param берутся из подписанной строки на сервере.

С auto: true дополнительно пишутся просмотры страниц, SPA-навигация, клики по элементам с data-mx, отправки форм, необработанные ошибки и события жизненного цикла Telegram — нажатия главной, дополнительной, системной кнопок и кнопки настроек, результаты popup и инвойса, смена темы, размера, полноэкранного режима и активности. Содержимое QR-сканирования и буфера обмена не передаётся: фиксируется только сам факт события. Можно включить часть: auto: { page: true, tg: true }.


Python: подключение одной строкой

Python SDK покрывает три популярные библиотеки для Telegram-ботов: aiogram 3.x, python-telegram-bot 20+ и pyTelegramBotAPI 4.x. Подключение оборачивает то, что у вас уже есть:

pip install "metriox[aiogram]"     # или metriox[ptb], metriox[telebot]
from metriox.integrations.aiogram import setup_metriox

bot = Bot(TOKEN)
dp = Dispatcher()
metriox = setup_metriox(bot, dp, api_key="...", platform_bot_id="my_bot")

Это вся интеграция. Дальше каждое входящее обновление и каждое отправленное ботом сообщение учитывается автоматически — отдельные вызовы не нужны. У python-telegram-bot и pyTelegramBotAPI та же функция setup_metriox(...), отличается только импорт.

У SDK нет обязательных зависимостей: транспорт написан на стандартном urllib, поэтому добавление Metriox не притянет второй HTTP-клиент и не создаст конфликт версий с тем, который у вас уже есть.

Обёртка сама отправляет ответы бота

Bot API никогда не присылает боту его собственные сообщения, поэтому без SDK половина диалога в аналитике отсутствует — ровно та же причина, что и у C#-обёртки выше. Python SDK перехватывает единую точку отправки, поэтому покрыты все методы send*/edit*/copy*, включая те, что появятся в библиотеке позже.

Свои события внутри обработчиков

Обогащение необязательно и пишется прямо в обработчиках, которые вы уже написали:

@dp.message(Command("buy"))
async def buy(message: Message):
metriox.enrich(plan="premium") # добавится к событию ЭТОГО обновления
metriox.track("purchase_completed", price=299) # отдельное событие; id пользователя из контекста
await message.answer("Готово") # учтётся автоматически как исходящее

enrich() не нужно ничего передавать: событие обрабатываемого обновления живёт в слоте contextvars на время работы обработчика. track() берёт оттуда же идентификатор пользователя — вне обработчика передайте user_id= явно, иначе событие будет отклонено при приёме.

Обработчики в фоне

Если обработчик выполняется вне окна захвата — block=False в python-telegram-bot или воркер-пул TeleBot(threaded=True) — событие обновления уже отправлено, поэтому enrich() там осознанно возвращает False. track() работает в обоих случаях и по-прежнему подставляет идентификатор пользователя.


Ответы бота в переписке (C#)

Bot API никогда не присылает боту его собственные отправленные сообщения — такого обновления просто не существует. Поэтому без участия бота его половина диалога в аналитике отсутствует: в переписке видны только сообщения пользователя, и все они выглядят как одна сторона разговора.

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

var bot = new TelegramBotClient(token).WithMetrioxCapture(sender, platformBotId: "my_bot");

await bot.SendMessage(chatId, "Выберите план", replyMarkup: markup);
// сообщение уже учтено: отдельный вызов не нужен

Обёртка перехватывает любой метод, который возвращает сообщение — SendMessage, SendPhoto, SendDocument, EditMessageText, CopyMessage и остальные, — поэтому список методов не нужно поддерживать вручную. Вместе с сообщением автоматически попадают его текст, форматирование ($tg.entities), inline-клавиатура и направление outbound.

Используйте обёрнутый клиент везде

Учитываются только отправки через возвращённый клиент. Если часть кода продолжает работать с исходным TelegramBotClient, эти сообщения в аналитику не попадут.

Сбой аналитики никогда не ломает отправку: если запись события не удалась, сообщение уже отправлено, и его результат возвращается без изменений.

Отдельные сообщения вручную

Если сообщение собирается вне обёрнутого клиента, событие можно отправить самостоятельно:

var sent = await bot.SendMessage(chatId, "Выберите план", replyMarkup: markup);

var ev = TelegramOutgoingMessageMapper.ToBotEvent(sent, platformBotId: "my_bot");
sender.TryEnqueue(ev);

Кто ваши пользователи (C#)

В аналитике человек может выглядеть как голый числовой id — без имени и без @username. Причина не в Telegram: в каждом апдейте Bot API присылает боту полный объект from (имя, @username, язык, Premium), но событие хранит только идентификатор — имя описывает человека, а не момент, и живёт в отдельном профиле.

Передавайте апдейт целиком, и профиль заполнится сам:

var mapper = new TelegramUpdateToBotEventMapper(platformBotId: "my_bot");

// было: sender.TryEnqueue(mapper.ToBotEvent(update));
sender.TryEnqueueUpdate(mapper, update);

TryEnqueueUpdate кладёт в очередь и событие, и личность отправителя. Личность берётся из любого апдейта, где она есть, — сообщение, нажатие кнопки, inline-запрос, вступление в чат, блокировка бота, — так что бот, где люди только жмут кнопки, перестаёт быть списком чисел.

Если событие собирается вручную, личность можно передать вторым аргументом:

sender.TryEnqueue(ev, TelegramUserSnapshotExtractor.From(update));
Для личных чатов ничего делать не нужно

Обёртка WithMetrioxCapture уже сообщает, кому бот пишет: в личном чате объект чата — это и есть пользователь.

Обновлять SDK не обязательно: Metriox дополнительно восстанавливает @username из самих событий. Но имя, фамилию, язык и Premium несёт только from, поэтому полный профиль получается лишь при передаче апдейта.


Клавиатура исходящего сообщения

Клавиатуру исходящего сообщения Metriox тоже видит только от бота (по той же причине). Через обёртку и ToBotEvent она передаётся автоматически; ниже — как собрать значение самостоятельно, если вы формируете событие вручную. Тогда в переписке будут видны предложенные кнопки, а по нажатому callback покажется его подпись, а не «сырой» payload.

SDK сериализует клавиатуру в компактную строку, которую платформа хранит в $tg.inline_keyboard (кнопки-callback сохраняют свои данные, кнопки-ссылки — url; остальные типы пропускаются). Ключи — это имена полей Bot API: text, callback_data, url; формат поля описан в Свойствах. Отправьте строку как tg.inline_keyboard в событии-сообщении с eventOrigin = "platform" и tg.from_is_bot = true — тогда оно отобразится как сообщение бота.

Обновление SDK не срочное

До 26.07.2026 SDK писал однобуквенные ключи (t/d/u для клавиатуры, t/o/l/u для форматирования). Платформа читает оба варианта, поэтому уже развёрнутый бот на старой версии SDK продолжает работать без изменений — обновляйтесь, когда удобно.

C#

// после того как бот отправил сообщение
var sent = await bot.SendMessage(chatId, "Выберите план", replyMarkup: markup);

// собрать событие и поставить в очередь на отправку
var ev = TelegramOutgoingMessageMapper.ToBotEvent(sent, platformBotId: "my_bot");
sender.TryEnqueue(ev);

Если вы собираете событие вручную, отдельно доступен InlineKeyboardSerializer.ToCompactJson(markup).

JavaScript/TypeScript

import { serializeInlineKeyboard } from "metriox-javascript";

const markup = {
inline_keyboard: [[{ text: "Купить", callback_data: "buy" }, { text: "Документация", url: "https://metriox.com" }]],
};

serializeInlineKeyboard(markup);
// => '[{"text":"Купить","callback_data":"buy"},{"text":"Документация","url":"https://metriox.com"}]'

Результат отправьте как tg.inline_keyboard в Telegram-событии с eventOrigin = "platform" — обычно на стороне сервера, потому что Bot API не присылает боту его собственные отправки.

Из Mini App это тоже работает

Раньше события WebApp считались пользовательскими и в $tg не переносились вовсе. Теперь ingest сам проставляет им platform-origin, поэтому свойство tg.inline_keyboard, отправленное из Mini App, попадает в $tg так же, как от любого другого источника. Указывать eventOrigin в запросе WebApp при этом не нужно — такого поля у endpoint нет.


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

Telegram присылает не «размеченный» текст, а обычный текст плюс список отрезков (жирный, ссылка, код) со смещениями. Чтобы в переписке отображалось форматирование, передайте этот список как tg.entities:

import { serializeMessageEntities } from "metriox-javascript";

serializeMessageEntities(message.entities);
// => '[{"type":"bold","offset":0,"length":5},{"type":"text_link","offset":6,"length":4,"url":"https://metriox.com"}]'

В C# то же делает MessageEntitySerializer.ToCompactJson(sent.Entities); при использовании обёртки WithMetrioxCapture это уже происходит само. Формат поля описан в Свойствах.


Идентификаторы событий

SDK вычисляет event_id детерминированно из координат Telegram (чат + сообщение, либо идентификатор запроса), теми же правилами, что и подключение по токену бота. Благодаря этому повторная доставка одного обновления не удваивает данные.

Если бот подключён и по токену, и через SDK, одно действие получает одинаковый event_id у обоих источников. Строк при этом сохраняется две — у них разный $source.channel, — но из квоты событий списывается одна. Подробнее в Событиях.

Готовые ключи доступны и напрямую, если вы формируете события сами:

import { tgEventKeys, telegramEventId } from "metriox-javascript";

const eventId = await telegramEventId(tgEventKeys.message(chatId, messageId));

Выключить приём от SDK

В настройках бота есть переключатель «Принимать события от SDK / API». Он включён по умолчанию: путь /tg открыт любому, у кого есть ваш API-ключ.

Выключите его, если хотите, чтобы Metriox записывал только то, что забирает из Telegram сам (подключение по токену бота). Это единственный способ выразить «только MTProto» — отзывать API-ключ для этого не нужно.

Что увидит SDK

Пока переключатель выключен, /tg отвечает 403, а не мнимым успехом. Это сделано намеренно: при 202 SDK счёл бы события доставленными и удалил бы их из очереди, а вы бы не увидели ни данных, ни причины.

403 — постоянная ошибка, поэтому корректный клиент прекращает попытки, а не копит их бесконечно.

Если выключить и этот переключатель, и «Enable bot polling», бот не будет записывать ничего — форма об этом предупредит.

Числовой id бота вводить не нужно

Раньше в настройках было поле для числового id бота. Его больше нет: id — это префикс самого токена (<id>:<секрет>), поэтому сервер извлекает его при сохранении.

Это не только удобство. Поле было подписано «необязательное», но при пустом значении все события Mini App отклонялись: id входит в строку, по которой Telegram считает подпись initData, и без него проверить подпись нечем.


Что дальше?