<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Gluek&apos;s blog</title><description>Персональный сайт и блог gluek.info</description><link>https://gluek.info/</link><item><title>Как боты съели 70 ГБ на моём сервере за 20 часов</title><link>https://gluek.info/blog/kak-boty-seli-70-gb/</link><guid isPermaLink="true">https://gluek.info/blog/kak-boty-seli-70-gb/</guid><description>Неожиданные грабли селфхостинга Forgejo: распределённый скрапинг публичных зеркал и бесконечный кэш repo-archive.</description><pubDate>Tue, 08 Sep 2026 00:25:00 GMT</pubDate><content:encoded>&lt;p&gt;Вчера у меня на сервере внезапно кончилось вообще всё свободное место (ровно в 0 байт), а боты начали падать с &lt;code&gt;disk I/O error&lt;/code&gt; из-за невозможности записать даже байт в SQLite.&lt;/p&gt;
&lt;p&gt;Полез разбираться с &lt;code&gt;ncdu&lt;/code&gt; и быстро нашёл виновника: папка &lt;code&gt;/root/forgejo/data/gitea/repo-archive/&lt;/code&gt; в контейнере &lt;a href=&quot;https://forgejo.org/&quot;&gt;Forgejo&lt;/a&gt; разрослась на &lt;strong&gt;70+ ГБ&lt;/strong&gt;. Внутри лежали сотни сгенерированных ZIP-архивов по 300–500 МБ каждый.&lt;/p&gt;
&lt;h3&gt;Что произошло?&lt;/h3&gt;
&lt;p&gt;Когда в веб-интерфейсе запрашивают архив релиза или ветки (&lt;code&gt;/archive/tag.zip&lt;/code&gt;), Forgejo на лету пакует его и сохраняет копию в кэш.&lt;/p&gt;
&lt;p&gt;Какой-то распределённый AI-краулер на резидентных прокси (IP со всего мира от Бразилии до Вьетнама) решил методично пройтись по всем тегам и коммитам в моём публичном зеркале крупного опенсорс-репозитория. На каждый чих бота Forgejo честно генерировал и складывал на диск полугигабайтный архив (стабильно держа около 15% CPU).&lt;/p&gt;
&lt;p&gt;Встроенный крон очистки в Forgejo по умолчанию срабатывает раз в сутки в полночь (&lt;code&gt;@midnight&lt;/code&gt;), удаляя архивы старше 24 часов. За 20 часов нагенерировалось 70 ГБ, и до полуночи сервер не дожил буквально час.&lt;/p&gt;
&lt;h3&gt;Как лечить&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Экстренная очистка&lt;/strong&gt; (безопасно, на сам Git не влияет):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rm -rf /root/forgejo/data/gitea/repo-archive/*
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Закрыть публичный доступ к зеркалам&lt;/strong&gt;:
Бороться с распределённым ботнетом через &lt;code&gt;fail2ban&lt;/code&gt; или &lt;code&gt;robots.txt&lt;/code&gt; бесполезно. Самое надёжное — перевести аккаунт &lt;code&gt;mirrors&lt;/code&gt; в режим Limited (только для авторизованных пользователей). Бот упирается в форму логина за 1 мс без какой-либо нагрузки на диск.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Зарезать генерацию архивов в &lt;code&gt;app.ini&lt;/code&gt;&lt;/strong&gt;:
Если архивы для репозиториев вообще не нужны:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[repository]
DISABLE_DOWNLOAD_SOURCE_ARCHIVES = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Либо чистить их кэш каждый час:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[cron.archive_cleanup]
SCHEDULE = @every 1h
OLDER_THAN = 2h
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Теория мёртвого интернета на практике&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Dead_Internet_theory&quot;&gt;Теория мёртвого интернета&lt;/a&gt; перестаёт казаться просто забавной конспирологией, когда сталкиваешься с ней на собственном сервере: чужие боты на резидентных прокси гоняют мой софт, заставляя его паковать сотни гигабайт архивов, которые ни один живой человек в жизни не откроет.&lt;/p&gt;
&lt;p&gt;Кстати, именно поэтому я закрыл доступ к зеркалам на &lt;a href=&quot;https://gluek.info/projects/git-gluek-info/&quot;&gt;git.gluek.info&lt;/a&gt; от анонимов. Если держите у себя публичные зеркала — имейте в виду эти грабли.&lt;/p&gt;
</content:encoded></item><item><title>t.gluek.info</title><link>https://gluek.info/projects/t-gluek-info/</link><guid isPermaLink="true">https://gluek.info/projects/t-gluek-info/</guid><description>Персональный легковесный редиректор Telegram-ссылок через Caddy без блокировок и трекеров</description><pubDate>Tue, 08 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Персональный легковесный редиректор ссылок в Telegram, работающий на собственном домене &lt;code&gt;t.gluek.info&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Служит независимой альтернативой официальному шортлинкеру &lt;code&gt;t.me&lt;/code&gt;, помогая открывать каналы, чаты, посты, стикеры и настройки в условиях региональных или провайдерских блокировок домена &lt;code&gt;t.me&lt;/code&gt;, не нагружая браузер лишними скриптами и внешними трекерами.&lt;/p&gt;
&lt;h2&gt;Проблема&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Блокировки домена &lt;code&gt;t.me&lt;/code&gt;&lt;/strong&gt;: В некоторых сетях и у отдельных провайдеров домен &lt;code&gt;t.me&lt;/code&gt; подвергается блокировкам или искусственному замедлению. В результате пользователи не могут открыть канал или перейти по ссылке.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ограничения deep link (&lt;code&gt;tg://&lt;/code&gt;)&lt;/strong&gt;: Прямой HTTP-редирект (301/302) на кастомную схему &lt;code&gt;tg://resolve?...&lt;/code&gt; блокируется большинством современных мобильных и десктопных браузеров (Chrome, Safari) с ошибкой &lt;code&gt;ERR_UNKNOWN_URL_SCHEME&lt;/code&gt;, если переход инициирован заголовком ответа, а не явным кликом пользователя.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Мусорный трафик и сканеры&lt;/strong&gt;: При использовании неразборчивого catch-all обработчика боты и сканеры уязвимостей (&lt;code&gt;/.env&lt;/code&gt;, &lt;code&gt;/wp-admin&lt;/code&gt;) получали бы &lt;code&gt;200 OK&lt;/code&gt; и пытались генерировать фиктивные ссылки.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Архитектура решения&lt;/h2&gt;
&lt;p&gt;Сервис работает &lt;strong&gt;полностью на уровне веб-сервера &lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt;&lt;/strong&gt; без единой строчки бэкенд-кода (Node.js, Go, Python или PHP не требуются).&lt;/p&gt;
&lt;h3&gt;Преимущества серверной маршрутизации в Caddy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Строгая валидация путей (Whitelist)&lt;/strong&gt;: Caddy проверяет пути регулярными выражениями. Любые посторонние запросы, сканеры ботов или битые URL сразу отсекаются и отправляются на страницу проекта или 404, не запуская клиентский код.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Серверные мета-теги (SSR)&lt;/strong&gt;: Заголовки страницы (&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;) и прямой URL для кнопки формируются сервером на этапе отдачи страницы через плейсхолдеры Caddy (&lt;code&gt;{re.user.1}&lt;/code&gt;, &lt;code&gt;{re.post.1}&lt;/code&gt; и т.д.).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Мгновенный переход + фоллбэк&lt;/strong&gt;: JavaScript в браузере сразу инициирует открытие Telegram, а если браузер запросил подтверждение — пользователь видит карточку с чёткой кнопкой прямого перехода (&lt;strong&gt;Open in Telegram&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Векторная фавиконка в стиле Telegram&lt;/strong&gt;: Caddy самостоятельно отдаёт легковесную SVG-фавиконку для &lt;code&gt;/favicon.ico&lt;/code&gt; и &lt;code&gt;/favicon.svg&lt;/code&gt; без графических файлов на диске.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Интернациональный интерфейс&lt;/strong&gt;: Все карточки оформлены на английском языке в лаконичном стиле &lt;code&gt;t.me&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Поддерживаемые форматы ссылок&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Формат &lt;code&gt;t.gluek.info&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Схема &lt;code&gt;tg://&lt;/code&gt; в приложении&lt;/th&gt;
&lt;th&gt;Назначение&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/Gluek&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://resolve?domain=Gluek&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Профиль, публичный канал или бот&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/my_bot?start=ref123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://resolve?domain=my_bot&amp;amp;start=ref123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Бот с параметрами запуска&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/gluekinfo/123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://resolve?domain=gluekinfo&amp;amp;post=123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Пост в публичном канале&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/c/1234567890/42&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://privatepost?channel=1234567890&amp;amp;post=42&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Пост в закрытом канале&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/group/12/34&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://resolve?domain=group&amp;amp;thread=12&amp;amp;post=34&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Сообщение в ветке форума&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+inviteCode&lt;/code&gt; / &lt;code&gt;/joinchat/...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://join?invite=inviteCode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Приглашение в чат или канал&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+79991234567&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://resolve?phone=79991234567&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Чат по номеру телефона&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/addstickers/pack_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://addstickers?set=pack_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Установка стикерпака&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/addemoji/pack_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://addemoji?set=pack_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Установка кастомных эмодзи&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/addlist/list_hash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://addlist?slug=list_hash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Добавление папки с чатами&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/proxy?...&lt;/code&gt; / &lt;code&gt;/socks?...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://proxy?...&lt;/code&gt; / &lt;code&gt;tg://socks?...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Классические MTProto / SOCKS5 прокси&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/webproxy?...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tg://webproxy?...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Новый экспериментальный WEB Proxy (WebSocket/HTTPS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Конфигурация Caddyfile&lt;/h2&gt;
&lt;p&gt;Ниже приведена полная конфигурация виртуального хоста для &lt;code&gt;Caddyfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t.gluek.info {
    # Фавиконка в стиле Telegram (SVG)
    @favicon path /favicon.ico /favicon.svg
    handle @favicon {
        header Content-Type &quot;image/svg+xml&quot;
        header Cache-Control &quot;public, max-age=604800&quot;
        respond &amp;lt;&amp;lt;SVG
&amp;lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&amp;gt;&amp;lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;12&quot; fill=&quot;#24A1DE&quot;/&amp;gt;&amp;lt;path fill=&quot;#ffffff&quot; d=&quot;M5.4 12l11.6-4.5c.5-.2 1 .1.9.7l-2 9.4c-.1.6-.6.8-1.1.5l-3.3-2.4-1.6 1.5c-.2.2-.4.3-.7.3l.2-3.4 6.2-5.6c.3-.3-.1-.4-.4-.2l-7.7 4.9-3.3-1c-.7-.2-.7-.7.2-1z&quot;/&amp;gt;&amp;lt;/svg&amp;gt;
SVG 200
    }

    # Общие заголовки
    header Content-Type &quot;text/html; charset=utf-8&quot;
    header Cache-Control &quot;no-cache&quot;

    # 1. Приватные посты в закрытых каналах: /c/1234567890/42
    @private_post path_regexp priv ^/c/([0-9]+)/([0-9]+)/?$
    handle @private_post {
        respond &amp;lt;&amp;lt;HTML
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&quot;en&quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;&amp;lt;title&amp;gt;Private Channel Post — t.gluek.info&amp;lt;/title&amp;gt;&amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.svg&quot; type=&quot;image/svg+xml&quot;&amp;gt;&amp;lt;style&amp;gt;body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#fff;text-align:center}.card{background:#1e293b;padding:2rem;border-radius:1rem;border:1px solid #334155;max-width:380px;width:90%}.btn{display:block;margin-top:1.5rem;background:#24A1DE;color:#fff;text-decoration:none;padding:.8rem;border-radius:.5rem;font-weight:600}.footer{margin-top:1.5rem;font-size:.8rem;color:#94a3b8}.footer a{color:#94a3b8}&amp;lt;/style&amp;gt;&amp;lt;script&amp;gt;window.location.href=&quot;tg://privatepost?channel={re.priv.1}&amp;amp;post={re.priv.2}&quot;;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div class=&quot;card&quot;&amp;gt;&amp;lt;h2&amp;gt;Private Channel&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Opening post #{re.priv.2}...&amp;lt;/p&amp;gt;&amp;lt;a class=&quot;btn&quot; href=&quot;tg://privatepost?channel={re.priv.1}&amp;amp;post={re.priv.2}&quot;&amp;gt;Open in Telegram&amp;lt;/a&amp;gt;&amp;lt;div class=&quot;footer&quot;&amp;gt;Redirector &amp;lt;a href=&quot;https://gluek.info/projects/t-gluek-info/&quot;&amp;gt;t.gluek.info&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
HTML 200
    }

    # 2. Стикеры, эмодзи и папки: /addstickers/pack, /addemoji/pack, /addlist/hash
    @packs path_regexp packs ^/(addstickers|addemoji|addlist)/([a-zA-Z0-9_]+)/?$
    handle @packs {
        respond &amp;lt;&amp;lt;HTML
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&quot;en&quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;&amp;lt;title&amp;gt;Telegram {re.packs.1} — t.gluek.info&amp;lt;/title&amp;gt;&amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.svg&quot; type=&quot;image/svg+xml&quot;&amp;gt;&amp;lt;style&amp;gt;body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#fff;text-align:center}.card{background:#1e293b;padding:2rem;border-radius:1rem;border:1px solid #334155;max-width:380px;width:90%}.btn{display:block;margin-top:1.5rem;background:#24A1DE;color:#fff;text-decoration:none;padding:.8rem;border-radius:.5rem;font-weight:600}.footer{margin-top:1.5rem;font-size:.8rem;color:#94a3b8}.footer a{color:#94a3b8}&amp;lt;/style&amp;gt;&amp;lt;script&amp;gt;
const type = &quot;{re.packs.1}&quot;;
const param = type === &quot;addlist&quot; ? &quot;slug&quot; : &quot;set&quot;;
window.location.href = `tg://${type}?${param}={re.packs.2}`;
&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div class=&quot;card&quot;&amp;gt;&amp;lt;h2&amp;gt;Add to Telegram&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Installing {re.packs.2}...&amp;lt;/p&amp;gt;&amp;lt;a class=&quot;btn&quot; href=&quot;tg://{re.packs.1}?set={re.packs.2}&quot;&amp;gt;Install in Telegram&amp;lt;/a&amp;gt;&amp;lt;div class=&quot;footer&quot;&amp;gt;Redirector &amp;lt;a href=&quot;https://gluek.info/projects/t-gluek-info/&quot;&amp;gt;t.gluek.info&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
HTML 200
    }

    # 3. Настройки прокси (MTProto, SOCKS5, WEB Proxy): /proxy?... /socks?... /webproxy?...
    @proxy path_regexp proxy ^/(proxy|socks|webproxy)/?$
    handle @proxy {
        respond &amp;lt;&amp;lt;HTML
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&quot;en&quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;&amp;lt;title&amp;gt;Telegram Proxy — t.gluek.info&amp;lt;/title&amp;gt;&amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.svg&quot; type=&quot;image/svg+xml&quot;&amp;gt;&amp;lt;style&amp;gt;body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#fff;text-align:center}.card{background:#1e293b;padding:2rem;border-radius:1rem;border:1px solid #334155;max-width:380px;width:90%}.btn{display:block;margin-top:1.5rem;background:#24A1DE;color:#fff;text-decoration:none;padding:.8rem;border-radius:.5rem;font-weight:600}.footer{margin-top:1.5rem;font-size:.8rem;color:#94a3b8}.footer a{color:#94a3b8}&amp;lt;/style&amp;gt;&amp;lt;script&amp;gt;window.location.href=&quot;tg://{re.proxy.1}?{query}&quot;;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div class=&quot;card&quot;&amp;gt;&amp;lt;h2&amp;gt;Connect to Proxy&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Applying {re.proxy.1} settings...&amp;lt;/p&amp;gt;&amp;lt;a class=&quot;btn&quot; href=&quot;tg://{re.proxy.1}?{query}&quot;&amp;gt;Connect in Telegram&amp;lt;/a&amp;gt;&amp;lt;div class=&quot;footer&quot;&amp;gt;Redirector &amp;lt;a href=&quot;https://gluek.info/projects/t-gluek-info/&quot;&amp;gt;t.gluek.info&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
HTML 200
    }

    # 4. Ссылка на телефон: /+79991234567
    @phone path_regexp phone ^/\+([0-9]{7,15})/?$
    handle @phone {
        respond &amp;lt;&amp;lt;HTML
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&quot;en&quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;&amp;lt;title&amp;gt;Telegram Contact — t.gluek.info&amp;lt;/title&amp;gt;&amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.svg&quot; type=&quot;image/svg+xml&quot;&amp;gt;&amp;lt;style&amp;gt;body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#fff;text-align:center}.card{background:#1e293b;padding:2rem;border-radius:1rem;border:1px solid #334155;max-width:380px;width:90%}.btn{display:block;margin-top:1.5rem;background:#24A1DE;color:#fff;text-decoration:none;padding:.8rem;border-radius:.5rem;font-weight:600}.footer{margin-top:1.5rem;font-size:.8rem;color:#94a3b8}.footer a{color:#94a3b8}&amp;lt;/style&amp;gt;&amp;lt;script&amp;gt;window.location.href=&quot;tg://resolve?phone={re.phone.1}&quot;;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div class=&quot;card&quot;&amp;gt;&amp;lt;h2&amp;gt;Contact&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Opening chat with +{re.phone.1}...&amp;lt;/p&amp;gt;&amp;lt;a class=&quot;btn&quot; href=&quot;tg://resolve?phone={re.phone.1}&quot;&amp;gt;Open in Telegram&amp;lt;/a&amp;gt;&amp;lt;div class=&quot;footer&quot;&amp;gt;Redirector &amp;lt;a href=&quot;https://gluek.info/projects/t-gluek-info/&quot;&amp;gt;t.gluek.info&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
HTML 200
    }

    # 5. Темы форумов (topic + post): /group/12/34
    @topic path_regexp topic ^/([a-zA-Z0-9_]+)/([0-9]+)/([0-9]+)/?$
    handle @topic {
        respond &amp;lt;&amp;lt;HTML
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&quot;en&quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;&amp;lt;title&amp;gt;Topic in @{re.topic.1} — t.gluek.info&amp;lt;/title&amp;gt;&amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.svg&quot; type=&quot;image/svg+xml&quot;&amp;gt;&amp;lt;style&amp;gt;body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#fff;text-align:center}.card{background:#1e293b;padding:2rem;border-radius:1rem;border:1px solid #334155;max-width:380px;width:90%}.btn{display:block;margin-top:1.5rem;background:#24A1DE;color:#fff;text-decoration:none;padding:.8rem;border-radius:.5rem;font-weight:600}.footer{margin-top:1.5rem;font-size:.8rem;color:#94a3b8}.footer a{color:#94a3b8}&amp;lt;/style&amp;gt;&amp;lt;script&amp;gt;window.location.href=&quot;tg://resolve?domain={re.topic.1}&amp;amp;thread={re.topic.2}&amp;amp;post={re.topic.3}&quot;;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div class=&quot;card&quot;&amp;gt;&amp;lt;h2&amp;gt;Topic in @{re.topic.1}&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Opening discussion thread...&amp;lt;/p&amp;gt;&amp;lt;a class=&quot;btn&quot; href=&quot;tg://resolve?domain={re.topic.1}&amp;amp;thread={re.topic.2}&amp;amp;post={re.topic.3}&quot;&amp;gt;Open in Telegram&amp;lt;/a&amp;gt;&amp;lt;div class=&quot;footer&quot;&amp;gt;Redirector &amp;lt;a href=&quot;https://gluek.info/projects/t-gluek-info/&quot;&amp;gt;t.gluek.info&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
HTML 200
    }

    # 6. Пост в канале: /Gluek/123
    @post path_regexp post ^/([a-zA-Z0-9_]+)/([0-9]+)/?$
    handle @post {
        respond &amp;lt;&amp;lt;HTML
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&quot;en&quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;&amp;lt;title&amp;gt;Post in @{re.post.1} — t.gluek.info&amp;lt;/title&amp;gt;&amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.svg&quot; type=&quot;image/svg+xml&quot;&amp;gt;&amp;lt;style&amp;gt;body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#fff;text-align:center}.card{background:#1e293b;padding:2rem;border-radius:1rem;border:1px solid #334155;max-width:380px;width:90%}.btn{display:block;margin-top:1.5rem;background:#24A1DE;color:#fff;text-decoration:none;padding:.8rem;border-radius:.5rem;font-weight:600}.footer{margin-top:1.5rem;font-size:.8rem;color:#94a3b8}.footer a{color:#94a3b8}&amp;lt;/style&amp;gt;&amp;lt;script&amp;gt;window.location.href=&quot;tg://resolve?domain={re.post.1}&amp;amp;post={re.post.2}&quot;;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div class=&quot;card&quot;&amp;gt;&amp;lt;h2&amp;gt;Post in @{re.post.1}&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Opening post #{re.post.2}...&amp;lt;/p&amp;gt;&amp;lt;a class=&quot;btn&quot; href=&quot;tg://resolve?domain={re.post.1}&amp;amp;post={re.post.2}&quot;&amp;gt;Open in Telegram&amp;lt;/a&amp;gt;&amp;lt;div class=&quot;footer&quot;&amp;gt;Redirector &amp;lt;a href=&quot;https://gluek.info/projects/t-gluek-info/&quot;&amp;gt;t.gluek.info&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
HTML 200
    }

    # 7. Инвайты: /+inviteHash или /joinchat/inviteHash
    @invite path_regexp invite ^/(?:\+|joinchat/)([a-zA-Z0-9_-]+)/?$
    handle @invite {
        respond &amp;lt;&amp;lt;HTML
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&quot;en&quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;&amp;lt;title&amp;gt;Telegram Invite — t.gluek.info&amp;lt;/title&amp;gt;&amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.svg&quot; type=&quot;image/svg+xml&quot;&amp;gt;&amp;lt;style&amp;gt;body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#fff;text-align:center}.card{background:#1e293b;padding:2rem;border-radius:1rem;border:1px solid #334155;max-width:380px;width:90%}.btn{display:block;margin-top:1.5rem;background:#24A1DE;color:#fff;text-decoration:none;padding:.8rem;border-radius:.5rem;font-weight:600}.footer{margin-top:1.5rem;font-size:.8rem;color:#94a3b8}.footer a{color:#94a3b8}&amp;lt;/style&amp;gt;&amp;lt;script&amp;gt;window.location.href=&quot;tg://join?invite={re.invite.1}&quot;;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div class=&quot;card&quot;&amp;gt;&amp;lt;h2&amp;gt;Telegram Invite&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Opening invitation link...&amp;lt;/p&amp;gt;&amp;lt;a class=&quot;btn&quot; href=&quot;tg://join?invite={re.invite.1}&quot;&amp;gt;Join in Telegram&amp;lt;/a&amp;gt;&amp;lt;div class=&quot;footer&quot;&amp;gt;Redirector &amp;lt;a href=&quot;https://gluek.info/projects/t-gluek-info/&quot;&amp;gt;t.gluek.info&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
HTML 200
    }

    # 8. Пользователь / канал / бот: /Gluek?start=123
    @user path_regexp user ^/([a-zA-Z0-9_]+)/?$
    handle @user {
        respond &amp;lt;&amp;lt;HTML
&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html lang=&quot;en&quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;&amp;lt;title&amp;gt;@{re.user.1} — t.gluek.info&amp;lt;/title&amp;gt;&amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.svg&quot; type=&quot;image/svg+xml&quot;&amp;gt;&amp;lt;style&amp;gt;body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0f172a;color:#fff;text-align:center}.card{background:#1e293b;padding:2rem;border-radius:1rem;border:1px solid #334155;max-width:380px;width:90%}.btn{display:block;margin-top:1.5rem;background:#24A1DE;color:#fff;text-decoration:none;padding:.8rem;border-radius:.5rem;font-weight:600}.footer{margin-top:1.5rem;font-size:.8rem;color:#94a3b8}.footer a{color:#94a3b8}&amp;lt;/style&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div class=&quot;card&quot;&amp;gt;&amp;lt;h2&amp;gt;@{re.user.1}&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Opening channel or profile...&amp;lt;/p&amp;gt;&amp;lt;a id=&quot;btn&quot; class=&quot;btn&quot; href=&quot;tg://resolve?domain={re.user.1}&quot;&amp;gt;Open in Telegram&amp;lt;/a&amp;gt;&amp;lt;div class=&quot;footer&quot;&amp;gt;Redirector &amp;lt;a href=&quot;https://gluek.info/projects/t-gluek-info/&quot;&amp;gt;t.gluek.info&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;script&amp;gt;
const query = &quot;{query}&quot; ? &quot;&amp;amp;{query}&quot; : &quot;&quot;;
const tgUrl = &quot;tg://resolve?domain={re.user.1}&quot; + query;
const btn = document.getElementById(&quot;btn&quot;);
if (btn) btn.href = tgUrl;
window.location.href = tgUrl;
&amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
HTML 200
    }

    # 9. Всё остальное (корень или невалидные пути) -&amp;gt; на описание проекта
    handle {
        redir https://gluek.info/projects/t-gluek-info/ 302
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>git.gluek.info</title><link>https://gluek.info/projects/git-gluek-info/</link><guid isPermaLink="true">https://gluek.info/projects/git-gluek-info/</guid><description>Мой персональный Git-сервис на Forgejo с встроенным CI/CD (Forgejo Actions) и зеркалами репозиториев</description><pubDate>Mon, 07 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Собственный легковесный Git-сервис на базе &lt;strong&gt;&lt;a href=&quot;https://forgejo.org/&quot;&gt;Forgejo&lt;/a&gt;&lt;/strong&gt; — свободного, открытого и управляемого сообществом форка Gitea. Сервис развернут на личном VPS и служит единым источником правды (&lt;strong&gt;Single Source of Truth&lt;/strong&gt;) для репозиториев, исходного кода сайта &lt;code&gt;gluek.info&lt;/code&gt;, скриптов автоматизации и вспомогательных ботов.&lt;/p&gt;
&lt;p&gt;Проект полностью укладывается в философию &lt;strong&gt;IndieWeb / POSSE&lt;/strong&gt;: владение собственными данными, независимость от коммерческих Git-платформ и бесшовная интеграция с персональной инфраструктурой.&lt;/p&gt;
&lt;h2&gt;Стек&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://forgejo.org/&quot;&gt;Forgejo&lt;/a&gt;&lt;/strong&gt; в &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt;-контейнере&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt;&lt;/strong&gt; в роли reverse proxy (с автоматическим HTTPS от &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let&apos;s Encrypt&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://forgejo.org/docs/latest/user/actions/&quot;&gt;Forgejo Actions&lt;/a&gt;&lt;/strong&gt; (&lt;a href=&quot;https://gitea.com/gitea/act_runner&quot;&gt;act runner&lt;/a&gt; в Docker) для сборки и автоматизации&lt;/li&gt;
&lt;li&gt;REST API для внешних интеграций&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Как я это использую&lt;/h2&gt;
&lt;h3&gt;1. Автоматический деплой сайта и кросспостинг&lt;/h3&gt;
&lt;p&gt;Все коммиты в ветку &lt;code&gt;main&lt;/code&gt; блога автоматически подхватываются раннером:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Собирается статический сайт на &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Подтягивается кэш npm и сгенерированных OG-картинок (&lt;code&gt;.og-cache&lt;/code&gt;), так что пересборка 1300+ постов занимает буквально считанные секунды.&lt;/li&gt;
&lt;li&gt;Готовый &lt;code&gt;dist/&lt;/code&gt; копируется в директорию Caddy на сервере.&lt;/li&gt;
&lt;li&gt;Скрипты проверяют свежие посты и автоматом закидывают анонсы в мой Telegram-канал (&lt;a href=&quot;https://t.me/gluekinfo&quot;&gt;@gluekinfo&lt;/a&gt;) и Mastodon (&lt;a href=&quot;https://mastodon.social/@gluek&quot;&gt;@gluek@mastodon.social&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Постинг со смартфона в один коммит&lt;/h3&gt;
&lt;p&gt;Через REST API инстанс связан с моим ботом &lt;a href=&quot;https://gluek.info/projects/deltachat-publish/&quot;&gt;Delta Chat Publish&lt;/a&gt;: я могу набросать заметку с фотографиями прямо в мессенджере на телефоне, а бот отправит всё одним запросом через &lt;code&gt;ChangeFilesOptions&lt;/code&gt; — ровно один коммит, один триггер пайплайна, и через минуту пост на сайте.&lt;/p&gt;
&lt;h3&gt;3. Зеркала репозиториев (&lt;code&gt;mirrors&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;В отдельный аккаунт &lt;strong&gt;&lt;code&gt;mirrors&lt;/code&gt;&lt;/strong&gt; я настроил зеркалирование внешних репозиториев и зависимостей, которые использую в своих проектах — чтобы они всегда были под рукой локально на случай, если с оригинальным репозиторием что-то случится.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Из-за постоянного наплыва ботов и скрейперов аккаунт &lt;code&gt;mirrors&lt;/code&gt; сейчас закрыт от анонимного просмотра и доступен только залогиненным пользователям. Если вам нужен доступ — просто скажите мне, и я сделаю аккаунт.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;4. Автономность&lt;/h3&gt;
&lt;p&gt;Свои секреты в репозиториях, SSH-ключи, двухфакторка и полный контроль над своими данными без лишнего трекинга и корпоративного контроля.&lt;/p&gt;
</content:encoded></item><item><title>Сколько вешать в .gram-ах?</title><link>https://gluek.info/blog/skolko-veshat-v-gram-akh/</link><guid isPermaLink="true">https://gluek.info/blog/skolko-veshat-v-gram-akh/</guid><description>Дуров всё-таки сделал выводы после блокировки t.me и анонсировал, что Telegram подал заявку на доменную зону .gram. Обещают каждому пользователю до...</description><pubDate>Tue, 18 Aug 2026 20:11:48 GMT</pubDate><content:encoded>&lt;p&gt;Дуров всё-таки сделал выводы после блокировки t.me и анонсировал, что Telegram подал заявку на доменную зону .gram. Обещают каждому пользователю до домену вида gluek.gram и возможность быстро запустить там некий &quot;интерактивный сайт&quot;. А с другой стороны и заблокировать будет проще.&lt;/p&gt;
</content:encoded></item><item><title>Google Pixel Tag</title><link>https://gluek.info/blog/google-pixel-tag/</link><guid isPermaLink="true">https://gluek.info/blog/google-pixel-tag/</guid><description>Отдельно впечатление про их тег - и поздно, и дорого. Вроде есть поддежка Ultra-Wideband для точного поиска - но она была и у Moto-тегов за те же д...</description><pubDate>Wed, 12 Aug 2026 22:04:24 GMT</pubDate><content:encoded>&lt;p&gt;Отдельно впечатление про их тег - и поздно, и дорого. Вроде есть поддежка Ultra-Wideband для точного поиска - но она была и у Moto-тегов за те же деньги и пару лет назад, и они хоть по размеру совпадали с Air-тегами от Apple. А тут выходит и размер не такой, и по цене как 4 штуки от Ugreen, и офлайн этот весь поиск полностью не работоспособен становится.&lt;/p&gt;
&lt;p&gt;Как кто-то написал в комментариях - в ноябре они его выпустят, в декабре про него забудут.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://gluek.info/blog/google-pixel-tag/566751283e558e7fb818daeec7a30ec.webp&quot; alt=&quot;566751283e558e7fb818daeec7a30ec.webp&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Google Pixel 11 &amp; Android 17</title><link>https://gluek.info/blog/google-pixel-11-android-17/</link><guid isPermaLink="true">https://gluek.info/blog/google-pixel-11-android-17/</guid><description>Мне кажется вообще ничего нового не появилось ни в самом телефоне, ни в новой версии андроида (которая уже пришла на мой Pixel 10 Pro XL - буквальн...</description><pubDate>Wed, 12 Aug 2026 21:31:29 GMT</pubDate><content:encoded>&lt;p&gt;Мне кажется вообще ничего нового не появилось ни в самом телефоне, ни в новой версии андроида (которая уже пришла на мой Pixel 10 Pro XL - буквально App Bubbles... и всё?), просто минимум смысла обновляться, хоть и сделали нормальный оффер:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Trade-in
Google Pixel 10 Pro XL (512GB)
£770.00 estimated value, applied instantly
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;P.S. С бонусом £150 за раннее бронирование вышло бы £479 за Pixel 11 Pro XL Obsidian (matte) 512GB (Unlocked)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://gluek.info/blog/google-pixel-11-android-17/f1ccd85292433dc5f4c4e7e2b268721.webp&quot; alt=&quot;f1ccd85292433dc5f4c4e7e2b268721.webp&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>deltachat_publish</title><link>https://gluek.info/projects/deltachat-publish/</link><guid isPermaLink="true">https://gluek.info/projects/deltachat-publish/</guid><description>Бот для Delta Chat для публикации заметок и фотографий в блог на Astro в один коммит</description><pubDate>Thu, 30 Jul 2026 00:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Раньше для каждого нового поста в блог приходилось открывать ноутбук или просить агента сделать коммит. Чтобы публиковать короткие заметки и фотографии прямо с телефона на ходу, я написал бота для децентрализованного мессенджера &lt;strong&gt;&lt;a href=&quot;https://delta.chat&quot;&gt;Delta Chat&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Это практическое воплощение принципов &lt;strong&gt;IndieWeb / POSSE&lt;/strong&gt; (&lt;em&gt;Publish on Own Site, Syndicate Elsewhere&lt;/em&gt;): мой репозиторий на &lt;a href=&quot;https://gluek.info/projects/git-gluek-info/&quot;&gt;git.gluek.info&lt;/a&gt; остаётся единственным источником правды (&lt;strong&gt;Single Source of Truth&lt;/strong&gt;), а отправка сообщения в чат превращается в публикацию на сайте буквально за минуту.&lt;/p&gt;
&lt;h2&gt;Стек&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Язык&lt;/strong&gt;: &lt;a href=&quot;https://www.python.org/&quot;&gt;Python 3.11&lt;/a&gt; (asyncio)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Мессенджер SDK&lt;/strong&gt;: &lt;a href=&quot;https://pypi.org/project/deltachat2/&quot;&gt;&lt;code&gt;deltachat2&lt;/code&gt;&lt;/a&gt; RPC + &lt;code&gt;deltabot_cli&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Интеграция с Git&lt;/strong&gt;: REST API &lt;a href=&quot;https://forgejo.org/&quot;&gt;Forgejo&lt;/a&gt; / Gitea (&lt;code&gt;ChangeFilesOptions&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;База данных&lt;/strong&gt;: &lt;a href=&quot;https://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt; (авторизация владельца, лог постов и статистика)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Деплой&lt;/strong&gt;: &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; + Docker Compose + &lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Уведомления&lt;/strong&gt;: &lt;a href=&quot;https://ntfy.sh/&quot;&gt;ntfy&lt;/a&gt; (&lt;code&gt;ntfy.gluek.info&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Как это устроено&lt;/h2&gt;
&lt;h3&gt;1. Атомарный single-commit постинг&lt;/h3&gt;
&lt;p&gt;Главная проблема обычной загрузки файлов через API — если грузить текст статьи и каждую фотографию по отдельности, на каждый файл создаётся отдельный коммит и каждый раз запускается тяжёлый CI/CD пайплайн сборки.&lt;/p&gt;
&lt;p&gt;Здесь используется эндпоинт &lt;code&gt;ChangeFilesOptions&lt;/code&gt; в REST API Forgejo. Текст &lt;code&gt;.md&lt;/code&gt; и все прикреплённые картинки упаковываются в один JSON-запрос. В итоге в Git создаётся &lt;strong&gt;ровно один коммит&lt;/strong&gt;, а сборка сайта в &lt;a href=&quot;https://gluek.info/projects/git-gluek-info/&quot;&gt;Forgejo Actions&lt;/a&gt; запускается один раз.&lt;/p&gt;
&lt;h3&gt;2. Автоматическая обработка на лету&lt;/h3&gt;
&lt;p&gt;Я просто пишу боту сообщение:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Первая строка становится заголовком (автоматом транслитерируется в аккуратный URL-slug, например: &lt;code&gt;Про сервис One-Face&lt;/code&gt; → &lt;code&gt;pro-servis-one-face&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Первые 150 символов уходят в &lt;code&gt;description&lt;/code&gt; для превью и RSS.&lt;/li&gt;
&lt;li&gt;Прикреплённые фотографии бот сам прописывает в Markdown как &lt;code&gt;![filename](path)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Поддерживаются оба формата структуры &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;: как &lt;code&gt;single_file&lt;/code&gt; (пост в &lt;code&gt;src/content/blog/&amp;lt;slug&amp;gt;.md&lt;/code&gt;, картинки в &lt;code&gt;public/images/&amp;lt;slug&amp;gt;/&lt;/code&gt;), так и &lt;code&gt;folder&lt;/code&gt; (пост в &lt;code&gt;src/content/blog/&amp;lt;slug&amp;gt;/index.md&lt;/code&gt; с медиафайлами рядом).&lt;/p&gt;
&lt;h3&gt;3. Безопасность&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;E2EE из коробки&lt;/strong&gt;: всё общение с ботом идёт через децентрализованный почтовый транспорт с автоматическим сквозным шифрованием &lt;a href=&quot;https://autocrypt.org/&quot;&gt;Autocrypt / OpenPGP&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Строгая авторизация&lt;/strong&gt;: бот принимает сообщения только от владельца. При инициализации проверяется не только email, но и PGP fingerprint отправителя — любые чужие сообщения отсекаются с &lt;code&gt;⛔ Access Denied&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Токены&lt;/strong&gt;: &lt;code&gt;FORGEJO_TOKEN&lt;/code&gt; живёт только в &lt;code&gt;.env&lt;/code&gt; на сервере и клиентам не светится.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Исходный код&lt;/h2&gt;
&lt;p&gt;Репозиторий открыт на GitHub: &lt;a href=&quot;https://github.com/mrgluek/deltachat_publish&quot;&gt;mrgluek/deltachat_publish&lt;/a&gt;, а также зеркалируется на моём сервере: &lt;a href=&quot;https://git.gluek.info/gluek/deltachat_publish&quot;&gt;git.gluek.info/gluek/deltachat_publish&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Подробнее о запуске я также рассказывал в &lt;a href=&quot;https://gluek.info/blog/novyy-bot-delta-chat-dlya-mobilnogo-postinga-v-blog/&quot;&gt;отдельной заметке в блоге&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Новый бот Delta Chat для мобильного постинга в блог</title><link>https://gluek.info/blog/novyy-bot-delta-chat-dlya-mobilnogo-postinga-v-blog/</link><guid isPermaLink="true">https://gluek.info/blog/novyy-bot-delta-chat-dlya-mobilnogo-postinga-v-blog/</guid><description>Решил упростить процесс публикации коротких заметок с телефона в мой новый блог на Astro. Раньше для каждого нового поста требовалось открывать ноу...</description><pubDate>Wed, 29 Jul 2026 21:45:00 GMT</pubDate><content:encoded>&lt;p&gt;Решил упростить процесс публикации коротких заметок с телефона в мой новый блог на Astro.&lt;/p&gt;
&lt;p&gt;Раньше для каждого нового поста требовалось открывать ноутбук или просить добавить коммит агента. Чтобы делать это удобнее, я написал специального бота &lt;a href=&quot;https://gluek.info/projects/deltachat-publish/&quot;&gt;Delta Chat Publish Bot&lt;/a&gt;, который работает поверх децентрализованного мессенджера &lt;a href=&quot;https://delta.chat&quot;&gt;Delta Chat&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Как это работает?&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Я отправляю боту обычное сообщение с телефона: первая строка — заголовок, ниже — текст в формате Markdown, и при необходимости прикрепляю фотографии.&lt;/li&gt;
&lt;li&gt;Бот транслитерирует заголовок в красивый URL slug, формирует YAML Frontmatter и заливает файл статьи вместе с картинками через Forgejo REST API.&lt;/li&gt;
&lt;li&gt;Все файлы передаются в &lt;strong&gt;одном HTTP-запросе&lt;/strong&gt;, создавая &lt;strong&gt;ровно один коммит&lt;/strong&gt; в репозитории.&lt;/li&gt;
&lt;li&gt;Автоматически запускается CI/CD пайплайн (Forgejo Actions), и через минуту пост уже на сайте.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Решение полностью соответствует философии &lt;strong&gt;IndieWeb / POSSE&lt;/strong&gt; (&lt;em&gt;Publish on Own Site, Syndicate Elsewhere&lt;/em&gt;): репозиторий остаётся единым источником правды, а публикация происходит буквально за минуту и не выходя из привычного мессенжера.&lt;/p&gt;
&lt;p&gt;Исходный код бота открыт на GitHub: &lt;a href=&quot;https://github.com/mrgluek/deltachat_publish&quot;&gt;mrgluek/deltachat_publish&lt;/a&gt; или как зеркало на  моём Forgejo: &lt;a href=&quot;https://git.gluek.info/gluek/deltachat_publish&quot;&gt;gluek/deltachat_publish&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Автоматический кросспостинг в Telegram и Mastodon</title><link>https://gluek.info/blog/crossposting-to-telegram-and-mastodon/</link><guid isPermaLink="true">https://gluek.info/blog/crossposting-to-telegram-and-mastodon/</guid><description>Настроил автоматическую публикацию анонсов новых постов из блога прямо в Telegram-канал и Mastodon при коммите в репозиторий.</description><pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;В блог добавлен автоматический кросспостинг! Теперь при публикации новой статьи через коммит в репозиторий CI-пайплайн автоматически отправляет анонс свежего поста:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✈️ В Telegram-канал &lt;a href=&quot;https://t.me/gluekinfo&quot;&gt;@gluekinfo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🐘 В Mastodon (&lt;a href=&quot;https://mastodon.social/@gluek&quot;&gt;@gluek@mastodon.social&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Анонсы содержат заголовок, краткое описание статьи и ссылку на новую публикацию.&lt;/p&gt;
&lt;p&gt;Исходный код сайта и скриптов кросспостинга открыт в публичном репозитории: &lt;a href=&quot;https://git.gluek.info/gluek/gluek.info&quot;&gt;git.gluek.info/gluek/gluek.info&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>ip.gluek.info</title><link>https://gluek.info/projects/ip-gluek-info/</link><guid isPermaLink="true">https://gluek.info/projects/ip-gluek-info/</guid><description>Простой сервис определения IP-адреса на Cloudflare Worker</description><pubDate>Fri, 24 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Простой сервис на базе Cloudflare Worker, который возвращает ваш публичный IP-адрес в формате plain text.&lt;/p&gt;
&lt;h3&gt;Пример использования&lt;/h3&gt;
&lt;p&gt;Вы можете легко получить свой IP-адрес через терминал с помощью &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl ip.gluek.info
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Код Cloudflare Worker&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;export default {
  async fetch(request, env, ctx) {
    const ip = request.headers.get(&apos;CF-Connecting-IP&apos;) || &apos;Unknown&apos;;
    return new Response(ip, {
      headers: { &apos;content-type&apos;: &apos;text/plain;charset=UTF-8&apos; },
    });
  }
};
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>