Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

17.04.2023

Бот для Телеграму вчиться генерувати посилання на пости в Телеграмі

Ще одно покращення для бота. Якщо до вчорашнього посту посилання на минулі пости вели на сайт, то тепер вони вестимуть на пости в Телеграмі. Але то тільки якщо читати їх в Телеграмі; бо пости на сайті будуть як і раніше вести на сайт. Тобто посилання зберігатиме поточний спосіб перегляду. Все це працює повністю автоматично. Розкажу, як.

Загальна ідея: впровадити особливу розмітку для посилань на пости. Власне, розмітка така вже є — це шорткоди ref та relref в Hugo. Оскільки пости на сайті обробляються саме через Hugo, то іншого варіанту, практично, немає. Посилання виглядає приблизно так: [вчорашній пост](/stendap/2023-04-16/). Залишається тільки додати підтримку такого коду в рендерер постів для Телеграму.

Як я писав раніше, рендерер використовує той самий пакет Goldmark, як і сам Hugo. Тому перша ідея була — використати код Hugo. Я навіть знайшов місце, яке обробляє шорткоди, втім, рендерер Hugo виявився дуже складним, та не модульним. Оскільки мені тільки потрібно переробляти один-єдиний шорткод, то я не став зʼясовувати, як саме витягнути той рендерер з коду Hugo (на що він не розрахований) та адаптувати для мене.

Тому почав копати в бік розширення Goldmark власноруч. Для впровадження власного коду розширяють парсер. Для того треба реалізувати інтерфейс parser.InlineParser, та визначити маркер початку коду, та парсер для нього. Парсер може бути просто регуляркою. (Власне, про це можна та треба велику статтю писати.) Все було б добре, але ні — виявилось, що власний код не може бути всередині адреси посилання. Це досить логічно, якщо подумати, бо в адресі не може бути й жирного шрифту або, наприклад, іншого посилання. Технічно так відбувається тому, що зміст адреси зчитується парсером посилань як єдине ціле, та не передається для подальшого розбору.

Наступна спроба — трансформувати адреси. Для цього є інше місце розширення Goldmark - parser.ASTTransformer. Типовий підхід тут — викликати вбудовану функцію обходу синтаксичного дерева ast.Walk, та виконувати дії над конкретними вузлами. В моєму випадку, якщо бачу посилання, та в адресі сидить шорткод — то його можна регуляркою замінити на справжню адресу. Це майже розвʼязало задачу, от тільки Goldmark має деякі обмеження на зміст адрес, та шорткод в чистому вигляді ламає парсер адреси та залишає посилання необробленими.

Остаточне рішення гібридне. Спочатку регуляркою знаходжу по тексту поста шорткод, та заміняю на форму, що підходить для адреси: PLACEHOLDER/docname/. Потім — пропускаю через Goldmark, та за допомогою трансформера перетворюю цю форму в адреси. Нарешті — після Goldmark знову регуляркою повертаю решту замінених шорткодів назад в оригінальну форму. (Останній крок потрібний, наприклад, щоб зробити приклад шорткоду з другого параграфу.)