update-4
This commit is contained in:
parent
05c4016747
commit
5138497f5d
68
docs/update-4.md
Normal file
68
docs/update-4.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Обновление 4 — Исправление отображения картинок
|
||||
|
||||
## Контекст
|
||||
|
||||
В update-2 и update-3 была реализована отправка новостей через XHTML-IM (XEP-0071) с картинкой через тег `<img>`.
|
||||
По результатам тестирования картинки не отображаются:
|
||||
- **Gajim** — показывает URL картинки вместо изображения (использует plain-text fallback)
|
||||
- **Monocle Chat (форк Conversations)** — показывает незагруженную заглушку
|
||||
|
||||
Причины установлены в ходе анализа лога stanza.
|
||||
|
||||
---
|
||||
|
||||
## Исправление 1 — двойной `<body>` в XHTML-IM
|
||||
|
||||
### Проблема
|
||||
|
||||
slixmpp xep_0071 при присвоении `msg["html"]["body"] = xhtml_body` самостоятельно оборачивает
|
||||
содержимое в `<body xmlns="http://www.w3.org/1999/xhtml">`. Текущая реализация `_build_xhtml`
|
||||
также включает этот тег, в результате в stanza попадает вложенный `<body>`:
|
||||
|
||||
```xml
|
||||
<body xmlns="http://www.w3.org/1999/xhtml"> ← добавляет slixmpp
|
||||
<body> ← добавляет наш код
|
||||
<p>...</p>
|
||||
</body>
|
||||
</body>
|
||||
```
|
||||
|
||||
Это невалидный XHTML-IM, что приводит к использованию plain-text fallback в строгих клиентах.
|
||||
|
||||
### Решение
|
||||
|
||||
В методе `_build_xhtml` убрать обёртку `<body xmlns="...">...</body>` — возвращать только
|
||||
внутренние элементы (`<p>`, `<br/>`). slixmpp добавит `<body>` сам.
|
||||
|
||||
---
|
||||
|
||||
## Исправление 2 — OOB для отображения картинок в Conversations-клиентах
|
||||
|
||||
### Проблема
|
||||
|
||||
Клиенты семейства Conversations (включая Monocle Chat) блокируют загрузку внешних изображений
|
||||
из XHTML-IM `<img src="...">` по умолчанию — это намеренная защита от трекинг-пикселей.
|
||||
Картинка парсится, но не загружается.
|
||||
|
||||
### Решение
|
||||
|
||||
Дополнительно к XHTML-IM прикреплять URL картинки как **Out-of-Band Data (XEP-0066)**.
|
||||
Conversations и его форки умеют рендерить OOB-ссылки как inline-превью.
|
||||
|
||||
Для этого к сообщению добавляется элемент:
|
||||
|
||||
```xml
|
||||
<x xmlns="jabber:x:oob">
|
||||
<url>https://example.com/image.jpg</url>
|
||||
</x>
|
||||
```
|
||||
|
||||
### Техническая реализация
|
||||
|
||||
1. Зарегистрировать плагин `xep_0066` в `JabberConnection`
|
||||
2. В методе `send_to_room_xhtml` добавить OOB-поле к сообщению, если передан `image_url`:
|
||||
```python
|
||||
msg["oob"]["url"] = image_url
|
||||
```
|
||||
3. Сигнатуру `send_to_room_xhtml` расширить необязательным параметром `image_url: str | None = None`
|
||||
4. В `JabberNewsPublisher.publish` передавать `item.image_url` в `send_to_room_xhtml`
|
||||
@ -25,6 +25,7 @@ class JabberConnection(ClientXMPP, JabberRoomJoiner, JabberRoomLeaver, MucJidRes
|
||||
self._rooms_on_start: List[str] = []
|
||||
|
||||
self.register_plugin("xep_0045") # MUC
|
||||
self.register_plugin("xep_0066") # Out-of-Band Data
|
||||
self.register_plugin("xep_0071") # XHTML-IM
|
||||
self.register_plugin("xep_0199") # XMPP Ping
|
||||
|
||||
@ -106,16 +107,25 @@ class JabberConnection(ClientXMPP, JabberRoomJoiner, JabberRoomLeaver, MucJidRes
|
||||
logger.warning("Не удалось отправить сообщение в %s", room_jid)
|
||||
return False
|
||||
|
||||
async def send_to_room_xhtml(self, room_jid: str, plain_text: str, xhtml_body: str) -> bool:
|
||||
async def send_to_room_xhtml(
|
||||
self,
|
||||
room_jid: str,
|
||||
plain_text: str,
|
||||
xhtml_body: str,
|
||||
image_url: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Отправляет XHTML-IM сообщение в конференцию (XEP-0071).
|
||||
plain_text — fallback для клиентов без поддержки XHTML.
|
||||
image_url — если указан, добавляется OOB (XEP-0066) для клиентов семейства Conversations.
|
||||
Возвращает False если возникла ошибка.
|
||||
"""
|
||||
try:
|
||||
msg = self.make_message(mto=room_jid, mtype="groupchat")
|
||||
msg["body"] = plain_text
|
||||
msg["html"]["body"] = xhtml_body
|
||||
if image_url:
|
||||
msg["oob"]["url"] = image_url
|
||||
msg.send()
|
||||
return True
|
||||
except Exception:
|
||||
|
||||
@ -19,7 +19,7 @@ class JabberNewsPublisher(NewsPublisher):
|
||||
"""
|
||||
plain = self._build_plain(item)
|
||||
xhtml = self._build_xhtml(item)
|
||||
return await self._connection.send_to_room_xhtml(room_jid, plain, xhtml)
|
||||
return await self._connection.send_to_room_xhtml(room_jid, plain, xhtml, item.image_url)
|
||||
|
||||
def _build_plain(self, item: NewsItem) -> str:
|
||||
blocks = []
|
||||
@ -33,12 +33,11 @@ class JabberNewsPublisher(NewsPublisher):
|
||||
|
||||
def _build_xhtml(self, item: NewsItem) -> str:
|
||||
SEP = "<br/><br/>"
|
||||
parts = ['<body xmlns="http://www.w3.org/1999/xhtml">']
|
||||
parts = []
|
||||
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:
|
||||
parts.append(f'<p>{item.summary}</p>{SEP}')
|
||||
parts.append(f'<p><a href="{item.link}">{item.link}</a></p>')
|
||||
parts.append('</body>')
|
||||
return ''.join(parts)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user