Команды и deep links

Команды /start, /help

Научитесь обрабатывать команды /start и /help на PHP. Разбор структуры bot_command, регистрация через setMyCommands и парсинг аргументов в групповых чатах.

Команды — это специальный формат текстовых сообщений, которые начинаются со символа слэша / (например, /start или /help). Telegram обрабатывает их на уровне интерфейса приложения: подсвечивает синим цветом, делает кликабельными и выводит в специальном меню подсказок.

Для любого качественного бота две команды являются обязательным стандартом:

  • /start — первичный запуск. Пользователь видит кнопку «Запустить» (или «Старт») при первом открытии диалога. Нажатие на неё отправляет эту команду. Также она используется для передачи реферальных параметров.
  • /help — вызов справки. Краткое руководство, список доступных функций и контакты поддержки.

Как Telegram передаёт команды в API

Когда пользователь отправляет команду, серверы Telegram упаковывают её в объект Update. Внутри объекта Message, помимо сырого текста в поле text, появляется массив entities. В нём Telegram размечает тип данных — bot_command.

Вот пример структуры входящего Update, когда пользователь отправляет команду /start test_payload:

{
"ok": true,
"result": [
{
"update_id": 987654321,
"message": {
"message_id": 105,
"from": {
"id": 123456789,
"is_bot": false,
"first_name": "Alex",
"username": "alex_dev"
},
"chat": {
"id": 123456789,
"first_name": "Alex",
"username": "alex_dev",
"type": "private"
},
"date": 1710000000,
"text": "/start test_payload",
"entities": [
{
"offset": 0,
"length": 6,
"type": "bot_command"
}
]
}
}
]
}

Разберём ключевые поля этого JSON:

  • text — содержит полную строку, отправленную пользователем, включая саму команду и любые параметры после пробела.
  • entities — массив объектов разметки. Поле offset: 0 указывает, что команда начинается с самого первого символа строки. Поле length: 6 говорит о том, что длина команды составляет 6 символов (включая слэш, то есть /start).
  • type: "bot_command" — явный маркер того, что Telegram распознал этот кусок текста как команду.

Регистрация команд через setMyCommands

Чтобы у пользователя в левом углу поля ввода появилась кнопка «Меню» со списком команд, их необходимо зарегистрировать на серверах Telegram. Это можно сделать вручную через бота @BotFather, но гораздо удобнее и гибче управлять этим процессом динамически через метод API setMyCommands.

Используем функцию telegramApi(), которую мы сделали в главе Первый запрос: getMe. Здесь только вызываем её — реализацию не копируем.

<?php

// Подключаем файл с функцией telegramApi(), созданной в предыдущих главах
require_once 'telegram_api.php';

// Список команд, которые мы хотим зарегистрировать в интерфейсе
$commands = [
[
'command' => 'start',
'description' => 'Запустить бота и получить приветствие'
],
[
'command' => 'help',
'description' => 'Показать справку по командам'
],
[
'command' => 'catalog',
'description' => 'Открыть каталог товаров'
]
];

try {
$response = telegramApi('setMyCommands', [
'commands' => $commands
]);

echo "Команды успешно зарегистрированы!\n";
var_dump($response);
} catch (Exception $e) {
echo "Ошибка при регистрации команд: " . $e->getMessage() . "\n";
}

В случае успешного выполнения запроса Telegram возвращает стандартный JSON-ответ:

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

Если в массиве команд была допущена ошибка (например, имя команды содержит заглавные буквы или спецсимволы, кроме подчёркивания), поле ok вернёт false, а в поле description будет указана причина ошибки (например, Bad Request: invalid command name).

Парсинг команд и обработка аргументов на PHP

Когда бот работает в группах, пользователи могут вызывать команды с указанием юзернейма бота, например: /start@MyAwesomeBot payload. Это сделано для того, чтобы несколько ботов в одном чате не реагировали на одну и ту же команду. Ваш парсер должен уметь корректно отрезать юзернейм бота и отделять саму команду от переданных аргументов.

Используем функцию telegramApi(), которую мы сделали в главе sendMessage. Здесь только вызываем её — реализацию не копируем.

<?php

require_once 'telegram_api.php';

/**
* Разбирает входящий текст на команду и аргументы.
* Учитывает обращение к конкретному боту в группах (/help@MyBot).
*/
function parseCommand(string $text, string $botUsername = ''): ?array
{
if ($text === '' || $text[0] !== '/') {
return null;
}

// Работаем только с первой строкой (игнорируем переносы строк)
$firstLine = strtok($text, "\n") ?: '';
$parts = explode(' ', $firstLine, 2);
$rawCommand = $parts[0];
$argument = trim($parts[1] ?? '');

// Проверяем, есть ли в команде юзернейм бота (например, /start@MyBot)
if (($pos = strpos($rawCommand, '@')) !== false) {
$commandName = substr($rawCommand, 0, $pos);
$targetBot = substr($rawCommand, $pos + 1);

// Если команда адресована другому боту — игнорируем её
if ($botUsername !== '' && strcasecmp($targetBot, $botUsername) !== 0) {
return null;
}
} else {
$commandName = $rawCommand;
}

return [
'name' => strtolower($commandName), // приводим к нижнему регистру
'argument' => $argument
];
}

// Получаем входящее обновление от Telegram
$rawInput = file_get_contents('php://input');
$update = json_decode($rawInput, true);

if (isset($update['message'])) {
$message = $update['message'];
$text = $message['text'] ?? '';
$chatId = $message['chat']['id'];

// Проверяем, является ли сообщение командой по разметке entities
$isCommand = false;
if (isset($message['entities'])) {
foreach ($message['entities'] as $entity) {
if (($entity['type'] ?? '') === 'bot_command' && ($entity['offset'] ?? -1) === 0) {
$isCommand = true;
break;
}
}
}

if ($isCommand) {
// Укажите юзернейм вашего бота без знака @ для фильтрации групповых команд
$parsed = parseCommand($text, 'MyAwesomeBot');

if ($parsed !== null) {
switch ($parsed['name']) {
case '/start':
$payload = $parsed['argument'];
if ($payload !== '') {
$reply = "Привет! Вы запустили бота с параметром: <code>" . htmlspecialchars($payload) . "</code>";
} else {
$reply = "Привет! Добро пожаловать. Напишите /help для получения списка команд.";
}
break;

case '/help':
$reply = "<b>Доступные команды:</b>\n\n" .
"/start — Начать работу\n" .
"/help — Показать это меню\n" .
"/catalog — Перейти в каталог";
break;

default:
$reply = "Извините, команда <code>" . htmlspecialchars($parsed['name']) . "</code> не поддерживается.";
break;
}

// Отправляем ответ пользователю
telegramApi('sendMessage', [
'chat_id' => $chatId,
'text' => $reply,
'parse_mode' => 'HTML'
]);
}
}
}

Отладка входящих команд

Для того чтобы увидеть, как именно ваш скрипт разбирает входящие данные, записывайте сырые обновления в лог-файл. Это избавит вас от необходимости гадать, почему команда не сработала.

<?php
// Быстрое логирование входящего запроса для анализа структуры
file_put_contents(
'bot_debug.log',
file_get_contents('php://input') . "\n",
FILE_APPEND
);

Чтобы отслеживать запросы в реальном времени на вашем сервере, выполните команду в терминале:

tail -f bot_debug.log | grep --line-buffered "" | jq .

Эта команда выводит новые строки из лога по мере их появления, а утилита jq красиво форматирует JSON-структуру прямо в консоли, подсвечивая синтаксис.

Дальше: Deep links