Users & chats
Channels
Learn how to work with Telegram channels in PHP: sending posts, editing, getting subscriber counts, and the specifics of working with discussion groups.
A Telegram channel is a tool for broadcasting public messages to an unlimited audience. Unlike groups (which we discussed in chapter Группы), regular channel members cannot send messages, share media files, or view the list of other subscribers. Only administrators (including your bot) can post to a channel, while users can only read posts, leave reactions, and write comments in the linked discussion group.
Channel Identifiers
Every channel has two types of identifiers:
- Public username — for example,
@my_channel_news. It is configured in the public channel settings. You can use it directly in the Bot API instead of a numerical ID. - Internal numerical identifier (chat_id) — a unique ID that always starts with the
-100prefix (for example,-1001234567890).
For reliable bot operation, always use the numerical chat_id. Channel administrators can change the channel username at any time, which will break the bot's logic if the username is hardcoded in the code. The numerical channel ID remains unchanged throughout its entire lifetime.
How do you find out a channel's ID? The easiest way is to forward any post from that channel to a special bot (for example, @userinfobot), or call the getChat method by passing the channel's public username.
Adding a Bot to a Channel
A bot cannot subscribe to or join a channel on its own. For the bot to be able to publish posts, it must be manually added to the channel as an administrator (via the Telegram interface: Channel Info → Administrators → Add Administrator).
When adding it, make sure to grant the bot the following permissions:
post_messages(Post messages) — without this permission, the bot will not be able to send posts.edit_messages(Edit messages) — allows modifying posts already published by the bot.delete_messages(Delete messages) — grants the right to delete posts.
If you attempt to send a message to a channel where the bot is not an administrator with posting rights, the Telegram API will return a 400 Bad Request: chat not found or 403 Forbidden: bot is not a member of the channel error.
Sending Posts to a Channel
The standard sendMessage method is used to send posts. You can format text using HTML or Markdown, attach media files, and add inline buttons.
We use the telegramApi() function that we created in chapter Первый запрос: getMe. Here we call it to send a formatted post to the channel.
<?php
// Токен получаем исключительно из переменных окружения
$token = getenv('TELEGRAM_BOT_TOKEN');
if (!$token) {
throw new RuntimeException('Переменная окружения TELEGRAM_BOT_TOKEN не задана');
}
// ID канала (всегда начинается с -100)
$channelId = '-1001234567890';
try {
// Отправляем пост с HTML-разметкой
$response = telegramApi($token, 'sendMessage', [
'chat_id' => $channelId,
'text' => "📢 <strong>Важное обновление проекта!</strong>\n\nМы запустили интеграцию с Telegram-каналами. Теперь бот умеет публиковать посты автоматически.",
'parse_mode' => 'HTML',
'disable_web_page_preview' => true
]);
// Выводим результат для отладки в терминале
echo "Пост успешно отправлен!\n";
echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
} catch (Exception $e) {
echo "Ошибка при отправке: " . $e->getMessage() . "\n";
}
On a successful call, the API returns a Message object. Note that the response structure for a channel has its own peculiarities:
{
"ok": true,
"result": {
"message_id": 145,
"sender_chat": {
"id": -1001234567890,
"title": "Мой тестовый канал",
"username": "my_test_channel",
"type": "channel"
},
"chat": {
"id": -1001234567890,
"title": "Мой тестовый канал",
"username": "my_test_channel",
"type": "channel"
},
"date": 1710000000,
"text": "📢 Важное обновление проекта!\n\nМы запустили интеграцию с Telegram-каналами. Теперь бот умеет публиковать посты автоматически.",
"entities": [
{
"offset": 2,
"length": 26,
"type": "bold"
}
]
}
}
What to analyze in the response:
message_id— the unique identifier of the post within this channel. Save it to a database if you plan to edit or delete this post later.chat.typehas the valuechannel, confirming that it was sent specifically to a channel.- The
sender_chatfield indicates that the author of the message is the channel itself, rather than a specific user.
Getting Channel Information
Sometimes a bot needs to check the number of channel subscribers or get its current description. The getChat method is used for this purpose.
We use the telegramApi() function that we created in chapter Первый запрос: getMe. We call the getChat method to get the channel's metadata.
<?php
$token = getenv('TELEGRAM_BOT_TOKEN');
if (!$token) {
throw new RuntimeException('Переменная окружения TELEGRAM_BOT_TOKEN не задана');
}
$channelId = '-1001234567890';
try {
$chatInfo = telegramApi($token, 'getChat', [
'chat_id' => $channelId
]);
echo "Информация о канале:\n";
echo "Название: " . $chatInfo['title'] . "\n";
echo "Описание: " . ($chatInfo['description'] ?? 'нет описания') . "\n";
// Количество подписчиков доступно в поле member_count при вызове getChat
if (isset($chatInfo['member_count'])) {
echo "Количество подписчиков: " . $chatInfo['member_count'] . "\n";
}
} catch (Exception $e) {
echo "Не удалось получить информацию: " . $e->getMessage() . "\n";
}
Example JSON response from the getChat method for a channel:
{
"ok": true,
"result": {
"id": -1001234567890,
"title": "Мой тестовый канал",
"username": "my_test_channel",
"type": "channel",
"description": "Канал для тестирования Bot API на PHP",
"member_count": 1420,
"linked_chat_id": -1001987654321
}
}
Important fields:
member_count— current number of channel subscribers. Note: this value is cached on Telegram's side and may be updated with a delay of several minutes.linked_chat_id— identifier of the supergroup linked to the channel as a discussion chat (comments). If no chat is linked, this field will be absent from the response.
Discussion Group (Comments)
If a discussion group is linked to a channel, a "Leave a comment" button appears under each post.
From a technical standpoint, when a bot or administrator publishes a post in a channel, Telegram automatically forwards this post to the linked supergroup. All user comments on this post are written inside the supergroup as replies to the forwarded message.
If your bot is present in both the channel and the discussion group, it will receive updates of type message from the discussion group. In such messages, the message.reply_to_message field will contain the original post from the channel. Read more about working with supergroups in chapter Группы.
Editing and Deleting Posts
Editing posts in a channel works exactly the same as in regular chats, but requires specifying the channel's chat_id and the post's message_id.
We use the safeEditMessageText() function that we implemented in chapter Редактирование и удаление. It allows safely updating the text of a previously sent post.
<?php
$token = getenv('TELEGRAM_BOT_TOKEN');
if (!$token) {
throw new RuntimeException('Переменная окружения TELEGRAM_BOT_TOKEN не задана');
}
$channelId = '-1001234567890';
$messageId = 145; // ID поста, полученный при отправке
// Безопасно редактируем текст поста
$success = safeEditMessageText(
$token,
$channelId,
$messageId,
"📢 <strong>Обновление проекта!</strong>\n\nТекст поста был успешно отредактирован ботом.",
'HTML'
);
if ($success) {
echo "Пост успешно отредактирован!\n";
} else {
echo "Не удалось отредактировать пост.\n";
}
Key Differences Between Channels and Groups
When developing logic for channels, keep in mind the following architectural differences:
- No join/leave events: Telegram does not send standard
new_chat_membersorleft_chat_memberupdates when users subscribe to or leave a channel. To track subscriptions, you need to use a special update type:chat_member(requires prior webhook or getUpdates configuration). - Inline button specifics: If you attach an inline keyboard to a channel post, clicking buttons with
callback_datawill not sendcallback_queryupdates directly from the channel. Telegram will deliver thecallback_queryto your bot only if the user has previously started the bot in direct messages. Buttons withurlalways work without restrictions. - Author anonymity: By default, posts are published on behalf of the channel. In the channel settings, you can enable "Sign messages", in which case the
Messageobject will include theauthor_signaturefield with the name of the administrator who published the post.
Next: HTTP-клиент на PHP