Commit Graph

42 Commits

Author SHA1 Message Date
kpa39l c8ccd7e187 fix+docs: classifier/publisher правки, sources.yaml расширен, глоссарий CONTEXT.md + docs/agents, backup.sh; openspec post-translate/rewrite-as-author/rich-repost-card/selected-layout-stay 2026-09-22 17:39:23 +00:00
kpa39l a10ab9fa3c feat(crawler): rss-crawler + очередь кандидатов + воркер (фазы 1-3); веб-статус /crawlers; openspec rss-crawler, tg-crawler-publisher-prototype 2026-09-22 17:39:09 +00:00
kpa39l 56610fa836 feat(web): архив опубликованных — дерево навигации по годам/месяцам + пагинация (openspec archive-navigation) 2026-09-22 17:39:03 +00:00
kpa39l 6a1c894ff7 openspec: change-ы manual-direction (ручное направление без LLM) и extract-post-sources (извлечение источников из постов) + sources/extract.py 2026-09-22 17:37:07 +00:00
kpa39l 1ae4b14d24 docs: приём незакоммиченных наработок в контекст — задачи (выполненные/планируемые), статус, tasks.md 2026-09-20 23:12:31 +00:00
kpa39l 0d4ecd1c23 docs: пометить Markdown-редактор перенесённым в /opt/md-editor (2026-09-20) 2026-09-20 22:59:56 +00:00
kpa39l 48b10349ec docs: задачи кросспостинга личных источников и markdown-редактора (2026-09-18) 2026-09-20 18:54:36 +00:00
kpa39l 64baa7ea73 docs: закрытие сессии 2026-09-18 (post-translate) 2026-09-20 16:05:04 +00:00
kpa39l 4a166fa9d6 feat(web): перевод поста на русский локальной моделью (openspec post-translate)
Кнопка «🌐 Перевести» в карточках кандидатов и отобранных переводит пост
локальной моделью (qwen3:8b-nothink), результат в posts.translated_text
(идемпотентный ALTER при старте). Метка «Оригинальный пост» стала закладкой,
при готовом переводе рядом появляется закладка «Перевод» (активна по умолчанию),
переключение на vanilla JS. Повторный клик не тратит токены: перевод в БД.
call_ollama из classifier не подошла (парсит ответ как JSON) — прямой запрос
к Ollama /v1/chat/completions. POST /posts/{id}/translate, лимит 6000 символов.
2026-09-18 05:40:59 +00:00
kpa39l 745680a3a0 docs: закрытие сессии 2026-09-17 (keep-bulk-checkboxes) 2026-09-17 17:33:42 +00:00
kpa39l c8fe5f7c85 fix(web): keep bulk checkbox selection across post navigation (openspec change keep-bulk-checkboxes)
- Save/restore checked candidate ids in sessionStorage (vesti_bulk_candidates / vesti_bulk_selected)
- Restore checks after page reload when opening a post via ?selected=N
- Fix selector: checkboxes live outside <form> (form=bulk-form attribute), query input[name=ids] globally
- Openspec: new capability candidates-bulk-selection, change archived
2026-09-17 00:52:51 +00:00
kpa39l 8777a0c4a4 openspec: архив address-field-enhancement (одно поле Address, авто-название из TG/RSS) 2026-09-16 23:37:03 +00:00
kpa39l 48c25f13d4 feat(sources): форма добавления источника — одно поле Address, авто-название из TG/RSS (address-field-enhancement)
- parse_address(address, crawler) в sources/sources.py: telegram (t.me/name, @name,
  https://t.me/s/name) → slug/channel/url; rss → feed_url/url/slug из домена.
- GET /sources/preview: telegram — название через Telethon get_entity (title),
  rss — через feedparser (feed.title); slug и name возвращаются в JSON.
- _form_source: единое поле address вместо url/channel/feed_url; slug из адреса.
- /sources: direction datalist из DIRECTIONS + уникальные значения из БД,
  свободный ввод остаётся.
- Форма: поле Address + кнопка «Найти» (JS fetch → заполнение slug/name),
  direction datalist динамический.

Также в sources.py входит незакоммиченная ранее веб-CRUD-логика (upsert_source_yaml,
delete_source_yaml, sync_yaml_to_db_and_back и др.) — работала на проде, теперь в git.
2026-09-16 17:03:23 +00:00
kpa39l 87134ed839 docs(TODO): закрыты строки 54 (git-репо дубликат) и 55 (openspec archive — 14 change-ов заархивированы) 2026-09-16 17:01:21 +00:00
kpa39l 771f6a8276 openspec: архив 14 завершённых change-ов (веб-фиксы, crawler-queue, own-content-hub, publisher-service); спеки влиты в openspec/specs 2026-09-16 17:01:00 +00:00
kpa39l 584582a48c docs: AGENTS.md — автоподхват контекста проекта Hermes при старте сессии 2026-09-16 16:59:23 +00:00
kpa39l 8592a82381 fix(sources): поле channel в форме добавления телеграм-источника (fix-sources-add-channel)
Форма добавления не имела поля channel, валидатор требует его для
crawler=telegram → 'telegram-источнику нужен channel'.
Добавлено поле в add-форму (required для telegram через JS), показ channel
в таблице у telegram-источников. Backend не менялся.

web/templates/sources.html ранее отсутствовал в git — добавлен.
2026-09-16 16:37:24 +00:00
kpa39l 582dc3cf66 rss-formatting-preserve: сохранять абзацы и разметку из RSS (html_to_text/extract_full_text normalize; safe_html bleach для предпросмотра; пересборка старых постов) 2026-09-15 18:53:57 +00:00
kpa39l b25054e1df candidates: кнопка «Отклонить» рядом с «Отобрать» (single + bulk) 2026-09-15 18:36:03 +00:00
kpa39l 8d26c97e6b selected-layout-stay: отбор остаётся на кандидатах; отобранные — layout на всю высоту (резюме под кнопками, редактор+оригинал в рост, публикация внизу) 2026-09-15 18:17:07 +00:00
kpa39l 51a13de28c docs: закрытие сессии 2026-09-15 — TODO/WALKTHROUGH актуализированы (selected-list, почтовый UI); отмечены незакоммиченные наработки прошлых сессий 2026-09-15 17:51:37 +00:00
kpa39l ae8af8de9c feat(candidates): список отобранных между кандидатами и опубликованными
- новый статус selected; в кандидатах только «⭐ Отобрать» (single+bulk)
- /selected — отдельный список: вся обработка (классификация/переписывание/комментарий/публикация)
- классификация только по кнопке (токены на кандидатов не тратятся)
- рядом с оригиналом — поле «✍️ Мой пост» (rewritten_text всегда виден), туда ложится черновик «Переписать»
- все обработчики учитывают src (candidates|selected) для редиректов
2026-09-15 17:37:11 +00:00
kpa39l b542e9b4fa fix(candidates): кнопка Опубликовать — под полем комментария рядом с Сохранить; оригинальный пост растянут на всю высоту панели 2026-09-15 16:49:44 +00:00
kpa39l c09dff08ac feat(candidates): флаг прочитанности (is_read), жирные непрочитанные, голубая заливка выбранного, дефолтная группировка по дате 2026-09-15 16:42:55 +00:00
kpa39l 562d787f1b fix(candidates): после Отклонить остаёмся на кандидатах, кнопки действий над комментарием
- reject/reject-old (single + bulk): редирект на /candidates с текущей
  группировкой вместо /candidates?status=rejected — не прерываем отбор.
  Отклонённые доступны через фильтр статуса.
- candidates.html: Обработать/Переписать/Отклонить — один ряд над полем
  комментария (после переноса Опубликовать в approve-форму кнопки съехали).

changes: candidates-buttons-order, reject-stay-candidates (skip_specs, валидны).
2026-09-15 16:30:19 +00:00
kpa39l ee2f8ac61a fix(publisher): медиа постов не публиковалось в Telegram — контейнер монтировал media/media вместо media
Причина: compose монтировал ../../media/media:/srv/publisher/media, а краулер
с 2026-09-12 качает файлы в media/<file> (новая раскладка). В контейнере
Path(media/<file>).exists() = False → медиа молча пропускалось, уходил только
текст (пост #1017).

Фикс: монтировать весь ../../media + _resolve_media() в publisher
(пробует media/<file>, media/media/<basename>). GoToSocial тоже.
Change fix-media-mount (skip_specs: багфикс), e2e: media_message_id задан,
тестовые сообщения удалены.
2026-09-14 18:29:54 +00:00
kpa39l a6cf605f52 docs: WALKTHROUGH — сессия 2026-09-14 (кандидаты: внешние new + контекст после действий) 2026-09-14 17:55:03 +00:00
kpa39l e9f7c96e84 docs: закрытие сессии кандидаты-фиксы 2026-09-14 2026-09-14 17:48:31 +00:00
kpa39l 0999424baa fix(candidates): сохранять группировку после действий, чинить пустой ?status=rejected
- _fetch_candidates: жёсткое status='new' только при пустом status; явный
  статус (rejected/published) — единственный фильтр статуса → ?status=rejected
  больше не даёт пустой список
- формы approve/reclassify/rewrite/reject/comment: скрытые поля
  group_by/direction/own/q — контекст страницы
- POST-роуты: редирект через _cand_back() сохраняет группировку и фильтры
  (reject → ?status=rejected&group_by=...; approve → ?status=published&...)
2026-09-14 05:47:30 +00:00
kpa39l acb49cc702 fix(candidates): только внешние new-посты в списке кандидатов
- _fetch_candidates: в WHERE всегда is_own=0 AND status='new' → свои посты
  канала @dedinit (is_own=1, backfill) и published/rejected больше не
  показываются как кандидаты
- counts табов: считаются по внешним постам (не по жёсткому where), чтобы
  табы статусов показывали реальные счётчики
- change: candidates-only-external-new (skip_specs)
2026-09-14 05:11:54 +00:00
kpa39l e2bdb4a87b TODO: инсайты на страницу метрик (день/неделя/месяц × новые/одобрено/опубликовано, 9 карточек) — задача на потом 2026-09-14 04:41:35 +00:00
kpa39l f890ad71f1 fix(web): статус группы при группировке по дате + published-страница
- _fetch_candidates: статус группы = доминирующий (new>rejected>published),
  а не первого поста → при group_by=date группа «Ранее на этой неделе»
  больше не выглядит опубликованной (openchange fix-date-group-status)
- base.html: padding-top 1.5rem→4.5rem — заголовок не перекрывается fixed-top меню
- published.html: ID поста на карточках (#N · направление)
- AGENT.MD: бэкапы автоматические по крону, вручную не запускать
2026-09-14 04:39:08 +00:00
kpa39l c66eded7bf candidates: visible native checkboxes + tiny JS counter
- checkbox: form-check-input (Bootstrap) rendered invisible outside
  .form-check -> native checkbox with explicit width/height
- tiny JS: badge #sel-count updates on change of any #bulk-form checkbox
- base.html: added {% block scripts %}
- verified: render (200 checkboxes), JS counter logic (node), service restarted
2026-09-13 21:07:14 +00:00
kpa39l 0e487f672a candidates UI: full width, action and bulk buttons
- base.html: container -> container-fluid (nav and content full width)
- Right pane: large buttons Publish / Process with local model /
  Rewrite / Reject (for new candidates)
- Left pane bottom: bulk operations panel with 4 visible buttons
  (Publish / Process / Reject / Stale) instead of hidden select
- bulk: action 'process' reclassifies selected with local model
  (impl_reclassify extracted); ids optional (empty selection ->
  no_selection redirect instead of 422); redirect preserves filters
- Tests: full width, buttons present, bulk empty/process, published/metrics — green
2026-09-13 21:01:30 +00:00
kpa39l 60617de0cb candidates: фикс смены группировки (422 при сабмите bulk-формы)
- Группировка вынесена из onchange-сабмита bulk-формы в GET-ссылки
  (/candidates?group_by=...&filters...&selected=...)
- Чекбоксы bulk привязаны к форме атрибутом form='bulk-form' (без JS,
  HTML5) — устранены вложенные формы (невалидный HTML)
- Выбор поста — ссылкой GET (selected=id) вместо POST /candidates/select
- app.py: добавлен filters_qs (query-string фильтров для ссылок),
  импорт urllib.parse.quote; /candidates/select оставлен для обратной
  совместимости (сабмит старых форм)
- Тест: смена группировки, bulk reject, выбор поста, фильтры — зелёные
2026-09-13 20:57:48 +00:00
kpa39l fc6d725bd1 candidates-mail-ui: двухпанельный интерфейс кандидатов (почтовый клиент)
- БД: posts.comment TEXT, posts.rewritten_text TEXT (ALTER TABLE, миграция применена)
- GET /candidates: двухпанельный рендер — слева список с группировкой
  (source/date/status, свертываемые <details>) + чекбоксы bulk,
  справа детали поста (действия, комментарий, пересказ, оригинал с медиа)
- Роуты: POST /candidates/select, /candidates/bulk (approve/reject/reject-old),
  /posts/{id}/comment, /posts/{id}/reclassify (qwen), /posts/{id}/rewrite (qwen)
- approve вынесен в impl_approve (переиспользуется bulk-approve: publisher + бандл)
- без JS (только формы); HTML: Jinja2 + Bootstrap 5.3
- Протестировано TestClient: группировка, bulk-reject, comment, reclassify
  (реальный qwen), rewrite (реальный qwen), поиск, media, published — зелёные
- openspec: change candidates-mail-ui архивирован (2026-09-13-candidates-mail-ui),
  spec влита в openspec/specs/candidates-mail-ui; validate — чисто
- docs: STATUS.md, WALKTHROUGH.md обновлены
2026-09-13 20:53:53 +00:00
kpa39l 970bf14a9d TODO: восстановлена удалённая открытая задача «Отдельная страница управления источниками новостей» (случайно снесена в b2af4bb — была без trailing newline) 2026-09-13 20:39:49 +00:00
kpa39l b2af4bb7c8 session close: TODO.md — fediverse-публикация (gotosocial-publisher) закрыта, открытые задачи (bot-флаг vesti, фан-аут) 2026-09-13 20:30:14 +00:00
kpa39l 6b955aa143 openspec: архивирован change gotosocial-publisher (2026-09-13), delta влита в specs/gotosocial-publisher 2026-09-13 20:27:41 +00:00
kpa39l bf176cc02b fediverse: публикация в GoToSocial (@vesti@dedinit.ru) из publisher-service
- gotosocial.py: клиент GtS (httpx): post_status (markdown), upload_media v2, verify_credentials, delete_status
- config.py: gt_social_url/token/visibility/format/channels
- channels.py: fediverse-каналы (VESTI_GTS_CHANNELS, префикс gt:), gt: уходит только в fediverse
- main.py: _publish_gotosocial + healthz-блок gotosocial + dry_run; status_id (ULID) в ответе
- .env.example: GT_SOCIAL_* и VESTI_GTS_CHANNELS
- STATUS.md/WALKTHROUGH.md: fediverse-раздел, хронология
- openspec: change gotosocial-publisher (proposal/design/tasks/specs), validate чисто
2026-09-13 20:19:52 +00:00
kpa39l 71fd48aa06 Session 2026-09-13: unify directions (canon+LLM-fallback, 966/1019), publisher dry_run fix, git repo, archive 8 changes, candidates-mail-ui WIP, docs 2026-09-13 17:48:06 +00:00
kpa39l c3f59f7b7a Initial import: vesti.nixg.ru — новостной апрув-проект (web, crawler, classifier, publisher, openspec) 2026-09-13 15:58:32 +00:00