update-5, update-6

This commit is contained in:
bvn13 2026-03-23 12:12:55 +03:00
parent 9bd0ab2e72
commit 566d973f2a
4 changed files with 59 additions and 10 deletions

26
docs/update-5.md Normal file
View File

@ -0,0 +1,26 @@
# Обновление 5 — Нормализация URL картинок
## Проблема
Клиент Conversations (и его форки) не отображает OOB-картинки, если URL содержит символ `:`
в path-части (например, `…/1579780545_0:182:3048:1897_…jpg`).
При парсинге OOB `<url>` клиент интерпретирует `:` как разделитель схемы или порта
и обрезает URL — ссылка становится невалидной, картинка не загружается.
URL без двоеточий в пути отображаются корректно.
## Решение
URL-энкодировать path-часть URL картинки перед сохранением в `NewsItem`.
Символ `:` кодируется как `%3A`. Согласно RFC 3986, percent-encoded символы
эквивалентны оригинальным — CDN-серверы обязаны принимать такие URL.
## Техническая реализация
1. В `adapters/sources/rss/fetcher.py` добавить функцию `_normalize_image_url(url: str) -> str`
2. Функция использует `urllib.parse` (стандартная библиотека, без новых зависимостей):
- разобрать URL через `urlparse`
- перекодировать path через `quote(path, safe='/')` — слэши остаются, `:` кодируется
- собрать обратно через `urlunparse`
3. Вызывать `_normalize_image_url` в `_extract_image_url` перед возвратом URL

21
docs/update-6.md Normal file
View File

@ -0,0 +1,21 @@
# Обновление 6 — Убрать картинку из тела сообщения
## Контекст
В update-2 картинка добавлялась двумя способами:
- в XHTML-теле через `<p><img src="..." /></p>`
- в plain-text fallback через URL картинки первым блоком
В update-4 добавлен OOB (XEP-0066), который обеспечивает отображение картинки
в клиентах семейства Conversations. Таким образом, `<img>` и URL в fallback
стали избыточными и создают визуальный мусор в клиентах, не рендерящих XHTML.
## Задача
Убрать блок картинки из тела сообщения:
1. В `_build_xhtml` — убрать `<p><img src="..." /></p><br/><br/>`
2. В `_build_plain` — убрать добавление `image_url` в список блоков
Поле `image_url` в `NewsItem` и логика его извлечения в fetcher'е остаются —
оно по-прежнему используется для OOB.

View File

@ -22,10 +22,7 @@ class JabberNewsPublisher(NewsPublisher):
return await self._connection.send_to_room_xhtml(room_jid, plain, xhtml, item.image_url) return await self._connection.send_to_room_xhtml(room_jid, plain, xhtml, item.image_url)
def _build_plain(self, item: NewsItem) -> str: def _build_plain(self, item: NewsItem) -> str:
blocks = [] blocks = [item.title]
if item.image_url:
blocks.append(item.image_url)
blocks.append(item.title)
if item.summary: if item.summary:
blocks.append(item.summary) blocks.append(item.summary)
blocks.append(item.link) blocks.append(item.link)
@ -33,10 +30,7 @@ class JabberNewsPublisher(NewsPublisher):
def _build_xhtml(self, item: NewsItem) -> str: def _build_xhtml(self, item: NewsItem) -> str:
SEP = "<br/><br/>" SEP = "<br/><br/>"
parts = [] parts = [f'<p>{item.title}</p>{SEP}']
if item.image_url:
parts.append(f'<p><img src="{item.image_url}" alt="" /></p>{SEP}')
parts.append(f'<p>{item.title}</p>{SEP}')
if item.summary: if item.summary:
parts.append(f'<p>{item.summary}</p>{SEP}') parts.append(f'<p>{item.summary}</p>{SEP}')
parts.append(f'<p><a href="{item.link}">{item.link}</a></p>') parts.append(f'<p><a href="{item.link}">{item.link}</a></p>')

View File

@ -3,6 +3,7 @@ import logging
import re import re
from html import unescape from html import unescape
from typing import List, Optional from typing import List, Optional
from urllib.parse import quote, urlparse, urlunparse
import feedparser import feedparser
@ -19,11 +20,18 @@ def _strip_html(text: str) -> str:
return unescape(text).strip() return unescape(text).strip()
def _normalize_image_url(url: str) -> str:
"""URL-энкодирует path-часть, чтобы символы вроде ':' не ломали парсеры клиентов."""
p = urlparse(url)
return urlunparse(p._replace(path=quote(p.path, safe='/')))
def _extract_image_url(entry) -> Optional[str]: def _extract_image_url(entry) -> Optional[str]:
"""Возвращает URL первого enclosure с type image/*.""" """Возвращает нормализованный URL первого enclosure с type image/*."""
for enc in entry.get("enclosures", []): for enc in entry.get("enclosures", []):
if enc.get("type", "").startswith("image/"): if enc.get("type", "").startswith("image/"):
return enc.get("href") or enc.get("url") raw = enc.get("href") or enc.get("url")
return _normalize_image_url(raw) if raw else None
return None return None