← Все гайды AI Release
EN Подписаться на @ai_release1

Как отслеживать новые ИИ-модели и обновления LLM: RSS, GitHub и Telegram-уведомления

Обновлено: 20.10.2026 · AI Release · @ai_release1
Как отслеживать новые ИИ-модели и обновления LLM: RSS, GitHub и Telegram-уведомления

TL;DR

Требования и подготовка

Иллюстрация к разделу «Требования и подготовка»
Иллюстрация к разделу «Требования и подготовка»

Что отслеживать — определитесь до настройки. Ориентир по самим моделям: Лучшие нейросети и ИИ-модели 2026.

Чек-лист готовности:

Пример: проверка окружения одной строкой.

python3 -V && curl --version | head -n 1

Ожидаемый вывод — две строки, например Python 3.12.x и curl 8.x.x. Если вторая строка отсутствует, установите curl пакетным менеджером.

Шаг 1. Соберите и проверьте источники (RSS и GitHub)

Что сделать: составьте файл feeds.json, где ключ — человекочитаемое имя источника, значение — URL ленты. Для GitHub не нужен API-ключ: у каждого репозитория есть Atom-лента релизов вида https://github.com/<owner>/<repo>/releases.atom.

Зачем: один скрипт читает разнородные источники одинаково — RSS 2.0 и Atom парсятся одним кодом, а имена ключей потом попадают в заголовок уведомления.

Как проверить: каждая лента должна отдавать HTTP 200 и XML, а не HTML-страницу.

Пример: feeds.json с реальным репозиторием библиотеки Transformers.

{
  "transformers-releases": "https://github.com/huggingface/transformers/releases.atom",
  "arxiv-cs-cl": "https://arxiv.org/rss/cs.CL"
}

Второй источник — пример научной ленты; доступность конкретной ленты проверьте в браузере, адреса и форматы могут меняться, сверьтесь с документацией источника.

curl -sI https://github.com/huggingface/transformers/releases.atom | head -n 3
curl -s https://arxiv.org/rss/cs.CL | head -c 120

Ожидаемо: первая команда печатает HTTP/2 200 (или HTTP/1.1 200), вторая — начало XML с тегом <rss. Если вместо XML пришёл <html>, лента недоступна или требует другого URL.

Почему это работает: GitHub отдаёт релизы как обычный Atom-фид, поэтому отдельный парсер GitHub не нужен — отличие только в тегах entry вместо item.

Если вы следите за открытыми весами моделей, список репозиториев удобно взять отсюда: Какие ИИ-модели имеют открытые веса и как их запустить бесплатно. Добавляйте в feeds.json только те репозитории, где релизы действительно публикуются.

ИсточникЧто даётАвторизацияЧастота опроса
GitHub `releases.atom`номера версий, теги, changelogне нужна1–2 раза в час
RSS 2.0 блога/хабаанонсы моделей и статейне нужнараз в час
GitHub REST `/releases`JSON, фильтры по `tag_name`токен, лимиты — сверьтесь с документацией GitHubпо расписанию

Шаг 2. Настройте сбор и доставку в Telegram

Иллюстрация к разделу «Шаг 2. Настройте сбор и доставку в Telegram»
Иллюстрация к разделу «Шаг 2. Настройте сбор и доставку в Telegram»

Что сделать: положите в каталог проекта скрипт watch.py. Он читает feeds.json, скачивает ленты, сравнивает идентификаторы записей с state.json и отправляет только новые записи в Telegram через Bot API.

Зачем: дедупликация по guid/id защищает от повторных уведомлений при каждом запуске cron; файл состояния переживает перезапуски.

Как проверить: запустите скрипт вручную и посмотрите на консольный вывод и сообщение в чате.

Пример: сохраните как watch.py.

#!/usr/bin/env python3
import json, os, urllib.request, urllib.parse
import xml.etree.ElementTree as ET
from pathlib import Path

STATE, FEEDS = Path("state.json"), Path("feeds.json")
TG_TOKEN, TG_CHAT = os.environ["TG_TOKEN"], os.environ["TG_CHAT_ID"]

def fetch(url):
    req = urllib.request.Request(url, headers={"User-Agent": "ai-watch/1.0"})
    with urllib.request.urlopen(req, timeout=30) as r:
        return r.read()

def parse(xml_bytes):
    root, items = ET.fromstring(xml_bytes), []
    for it in root.iter("item"):                     # RSS 2.0
        items.append({"id": (it.findtext("guid") or it.findtext("link") or "").strip(),
                      "title": (it.findtext("title") or "").strip(),
                      "link": (it.findtext("link") or "").strip()})
    if not items:                                    # Atom
        ns = {"a": "http://www.w3.org/2005/Atom"}
        for e in root.findall("a:entry", ns):
            link = e.find("a:link", ns)
            items.append({"id": (e.findtext("a:id", namespaces=ns) or "").strip(),
                          "title": (e.findtext("a:title", namespaces=ns) or "").strip(),
                          "link": link.get("href") if link is not None else ""})
    return [i for i in items if i["id"]]

def tg_send(text):
    data = urllib.parse.urlencode({"chat_id": TG_CHAT, "text": text,
                                   "parse_mode": "HTML",
                                   "disable_web_page_preview": "true"}).encode()
    req = urllib.request.Request(f"https://api.telegram.org/bot{TG_TOKEN}/sendMessage", data=data)
    with urllib.request.urlopen(req, timeout=30) as r:
        return r.status

def main():
    feeds = json.loads(FEEDS.read_text(encoding="utf-8"))
    seen = json.loads(STATE.read_text(encoding="utf-8")) if STATE.exists() else {}
    for name, url in feeds.items():
        try:
            items = parse(fetch(url))
        except Exception as e:
            print(f"[ERR] {name}: {e}")
            continue
        known = set(seen.get(name, []))
        new = [i for i in items if i["id"] not in known]
        for i in reversed(new):
            tg_send(f"<b>{name}</b>\n{i['title']}\n{i['link']}")
        seen[name] = list(known | {i["id"] for i in new})[-200:]
        print(f"[OK] {name}: new={len(new)}")
    STATE.write_text(json.dumps(seen, ensure_ascii=False, indent=2), encoding="utf-8")

if __name__ == "__main__":
    main()

Узнайте chat_id: отправьте боту сообщение и выполните запрос getUpdates.

curl -s "https://api.telegram.org/bot$TG_TOKEN/getUpdates"

В ответе найдите "chat":{"id":123456789,...} — это и есть TG_CHAT_ID. Затем запуск:

export TG_TOKEN="<токен-из-BotFather>"
export TG_CHAT_ID="<ваш-chat_id>"
python3 watch.py

Ожидаемый вывод при первом запуске: строки [OK] transformers-releases: new=<N> и [OK] arxiv-cs-cl: new=<N>, а в чате — пачка сообщений. Повторный запуск должен дать new=0.

Поставьте опрос на расписание — например, каждые 30 минут:

crontab -e
*/30 * * * * cd /opt/ai-watch && TG_TOKEN=<токен> TG_CHAT_ID=<chat_id> /usr/bin/python3 watch.py >> watch.log 2>&1

Альтернатива без своего сервера — GitHub Actions; секреты задаются в Settings → Secrets and variables → Actions, расписание в cron-синтаксисе:

name: watch
on:
  schedule: [{cron: "0 */6 * * *"}]
  workflow_dispatch:
jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: python3 watch.py
        env:
          TG_TOKEN: ${{ secrets.TG_TOKEN }}
          TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }}

Почему это работает: cron не наследует переменные из интерактивной оболочки, поэтому TG_TOKEN и TG_CHAT_ID указаны прямо в строке задания.

Шаг 3. Проверьте работу и настройте контроль

Иллюстрация к разделу «Шаг 3. Проверьте работу и настройте контроль»
Иллюстрация к разделу «Шаг 3. Проверьте работу и настройте контроль»

Что сделать: проведите три проверки — свежесть лога, отсутствие дублей и доставку тестового уведомления.

Зачем: молчащий бот неотличим от сломанного скрипта, поэтому нужен явный признак успеха на каждой итерации.

Как проверить:

tail -n 20 /opt/ai-watch/watch.log
docker --version 2>/dev/null | head -n 1
python3 - <<'PY'
import json, pathlib
s = json.loads(pathlib.Path("state.json").read_text(encoding="utf-8"))
print({k: len(v) for k, v in s.items()})
PY

Ожидаемый результат:

1. В watch.log появляются новые строки [OK] ... примерно с той же периодичностью, что задана в cron.

2. Словарь на экране содержит по ключу на каждый источник из feeds.json, счётчики не равны нулю.

3. Строк [ERR] в логе нет.

Тестовая доставка в обход расписания:

curl -s -X POST "https://api.telegram.org/bot$TG_TOKEN/sendMessage" \
  -d chat_id="$TG_CHAT_ID" -d text="ai-watch: проверка канала доставки"

Ожидаемо: JSON {"ok":true,"result":{...}} и сообщение в чате. Если пришло "ok":false, смотрите раздел проблем.

Настройте ротацию, иначе лог вырастет незаметно:

sudo tee /etc/logrotate.d/ai-watch >/dev/null <<'EOF'
/opt/ai-watch/watch.log {
  weekly
  rotate 4
  compress
  missingok
  notifempty
}
EOF

Почему это работает: logrotate переименовывает файл и создаёт новый, а cron продолжает писать в тот же путь.

Итог шага: у вас есть расписание, лог с меткой [OK] на каждый источник и подтверждённый канал доставки. Смежные сценарии автоматизации описаны в гайде по ИИ-помощнику для интернет-магазина на локальной модели — там тот же принцип «скрипт + расписание + внешний сервис».

Возможные проблемы и решения

Проблема. Лента не парсится, в логе ошибка синтаксиса XML

curl -sI "<URL-ленты>" | head -n 3
curl -s "<URL-ленты>" | head -c 200

Если видите HTTP/2 404 или <html> — URL устарел, сверьтесь с документацией источника.

Проблема. Бот не доставляет сообщения

curl -s "https://api.telegram.org/bot$TG_TOKEN/getUpdates" | grep -o '"chat":{"id":-\?[0-9]*'

Проблема. Cron падает сразу при старте

crontab -l | grep watch.py

Команда должна показать строку с обеими переменными перед /usr/bin/python3.

Резюме и следующие шаги

Вы получили автономный конвейер: feeds.json с источниками, watch.py с дедупликацией через state.json и cron, который раз в 30 минут отправляет новые записи в Telegram. Дальше добавьте источники, которых не хватает, и настройте фильтр по ключевым словам в заголовке, чтобы снизить шум.

FAQ

Чем GitHub Releases отличается от RSS-ленты?

Ничем по механике: releases.atom — это Atom-фид, тот же формат, что и RSS, просто с тегами entry вместо item, поэтому один парсер читает оба.

Обязательно ли держать свой сервер?

Нет: тот же скрипт запускается по расписанию в GitHub Actions, секреты TG_TOKEN и TG_CHAT_ID хранятся в настройках репозитория.

Почему приходит старая запись повторно?

Скорее всего удалён или пересоздан state.json — идентификаторы потеряны. Скрипт считает все записи новыми; верните файл из резервной копии или удалите лишние сообщения вручную.

Какой интервал опроса выбрать?

Начните с 30–60 минут: этого достаточно для релизов и анонсов, а лимиты API и rate limit остаются в норме. Точные ограничения конкретного источника сверьте с его документацией.

🤖 Кратко для ИИ