Я давно привык жить по правилам DevOps: один узел это хорошо, два узла это спокойный сон. С контентом та же история. Если весь трафик и подписчики завязаны на одну площадку, любая нестабильность превращается в головную боль. Поэтому я решил сделать нормальный запасной канал и автоматизировать публикации.
В качестве второй площадки я выбрал MAX. Дальше была цель простая: пост вышел на сайте, пост улетел в канал. Без копипаста, без “сейчас, минутку, я руками перекину”.
В итоге я собрал рабочую схему и дописал свой плагин под WordPress. Ниже показываю путь от создания канала и получения токена до очереди отправки и загрузки картинок через API.
Что я хотел получить по факту
Мне нужна была не “интеграция ради интеграции”, а практичный автопостинг.
1) Публикация новых записей из WordPress в канал MAX.
2) Нормальный внешний вид: заголовок, короткий текст, ссылка, кнопка “Читать”.
3) Картинка к посту, чтобы лента выглядела как медиа, а не как лог с сервера.
4) Очередь, повторы и логи. Чтобы редактор нажал “Опубликовать”, а дальше уже работала автоматика, даже если сеть моргнула.
Этап 1. Канал и доступ к партнёрской панели
С MAX всё начинается с партнёрской панели. Там создаётся канал и там же выдаётся токен для интеграции. Без токена дальше делать нечего.
Важно: я сразу завёл отдельного бота именно под автопостинг. Не смешиваю в одну кучу “бот отвечает людям” и “бот постит новости”. Так проще поддерживать и безопаснее.
Этап 2. Токен, права и главный нюанс с админством
После создания бота мне понадобились две вещи: токен и идентификатор канала (Chat ID). Токен это ключ от двери. Chat ID это адрес, куда стучаться.
Самый частый затык на старте выглядит так: токен есть, запросы уходят, а посты не публикуются. Причина банальная. Бот должен быть администратором канала. Если он просто добавлен “как участник”, права на публикацию могут не сработать.
Я это проверял просто: отправлял тестовое сообщение из админки плагина. Если тест уходит, значит токен рабочий, Chat ID правильный, права выставлены.
Этап 3. Админка WordPress и настройки плагина
Я сделал отдельную страницу настроек в админке WordPress. Минимум полей, максимум пользы: Token, Chat ID, галка “картинка”, галка “кнопка”, плюс кнопка “Отправить тест”.
Токен я храню в настройках сайта и не свечу нигде на фронте. Доступ к странице настроек только у админов. На сохранение настроек повесил nonce, потому что я люблю, когда чужие руки не трогают мои ключи.
Как устроена отправка постов и почему я сделал очередь
Сначала я пробовал отправлять пост “в момент публикации”. Работает, пока всё идеально. Потом ловишь ситуацию: сеть зависла, API ответило медленно, редактор висит, WordPress думает, что жизнь боль, и ты начинаешь ненавидеть всё живое.
Поэтому я сделал очередь.
При публикации поста я не отправляю его сразу. Я ставлю метку “в очереди” (post meta) и складываю ID в список. Дальше по расписанию это разгребает фоновая задача (WP Cron или системный cron, как настроишь).
Плюсы простые: админка не тормозит, посты уходят стабильно, при ошибке можно повторить попытку, логи сохраняются.
Картинка к посту: почему просто URL это не всегда надёжно
Самая “весёлая” часть была с картинками. Хотелось сделать по взрослому: брать featured image из WordPress и прикреплять к сообщению так, чтобы в MAX она отображалась нормально.
Ссылкой на картинку можно жить, но это не всегда красиво и не всегда стабильно. Поэтому я пошёл по пути загрузки файла через multipart запрос. Беру физический файл с сервера, отправляю в API загрузки, получаю токен, дальше прикрепляю токен к сообщению.
Да, это чуть больше кода. Зато результат предсказуемый, и внешний вид постов одинаковый.
Форматирование: один пост или два сообщения
Я быстро понял, что у формата “картинка плюс длинная простыня” есть побочные эффекты. Где то подпись обрезалась, где то выглядело не так, как я ожидал на телефоне.
Поэтому я сделал два режима.
Режим 1: одно сообщение, где картинка и подпись вместе.
Режим 2: сначала улетает картинка, следом улетает текст с заголовком, анонсом и кнопкой “Читать”. На практике второй вариант выглядит аккуратнее и стабильнее.
Пример логики отправки (упрощённо)
Ниже упрощённый пример того, как выглядит “ядро” отправки: собрал текст, загрузил картинку, сформировал payload, отправил запрос. В реальном плагине вокруг этого ещё очередь, повторы и логирование.
public static function send_post_to_max(int $post_id): bool {
$s = self::get_settings();
$title = get_the_title($post_id);
$url = get_permalink($post_id);
$raw = get_post_field('post_content', $post_id);
$text = wp_trim_words(wp_strip_all_tags($raw), 45);
// 1) Загружаю featured image и получаю токен (если есть картинка)
$img_token = null;
if (!empty($s['with_image'])) {
$img_token = self::upload_featured_image_and_get_token($post_id);
}
// 2) Формирую сообщение
$msg = "<b>" . esc_html($title) . "</b>\n\n"
. esc_html($text) . "\n\n"
. esc_html($url);
$payload = [
'chat_id' => $s['chat_id'],
'text' => $msg,
'attachments' => []
];
// 3) Прикрепляю картинку по токену
if ($img_token) {
$payload['attachments'][] = [
'type' => 'image',
'payload' => ['token' => $img_token]
];
}
// 4) Кнопка Читать
if (!empty($s['with_button'])) {
$payload['attachments'][] = [
'type' => 'inline_keyboard',
'payload' => [
'buttons' => [
[
[
'type' => 'link',
'text' => 'Читать',
'url' => $url
]
]
]
]
];
}
// 5) Отправляю
$resp = wp_remote_post(self::api_url('/messages.send'), [
'timeout' => 15,
'headers' => [
'Authorization' => 'Bearer ' . $s['token'],
'Content-Type' => 'application/json; charset=utf-8',
],
'body' => wp_json_encode($payload, JSON_UNESCAPED_UNICODE),
]);
return !is_wp_error($resp) && wp_remote_retrieve_response_code($resp) >= 200
&& wp_remote_retrieve_response_code($resp) < 300;
} Да, API endpoints и названия методов могут отличаться в зависимости от текущей версии. Суть тут в архитектуре: не блокировать публикацию, грузить картинку файлом, логировать ответ, уметь повторить.
Итог
В итоге я получил нормальную связку: WordPress публикует, плагин ставит задачу в очередь, фоновая отправка пушит пост в MAX, а подписчики видят красивый пост с кнопкой. Это ровно тот случай, когда один вечер разработки экономит кучу времени дальше.
Дальше я планирую допилить массовую публикацию старых записей с выбором “с новых к старым” и сделать более удобный экран очереди. Чтобы канал можно было наполнить за час, а не за неделю.