Initial commit: Hermes skill xmpp-server-prosody

This commit is contained in:
estorozhenko
2026-09-06 13:51:11 +00:00
commit 3da6c8709b
28 changed files with 2016 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
# OMEMO (XEP-0384) — проверка и публикация ключей (проверено 2026-08-28, nixg.ru)
## Главное: серверный модуль НЕ нужен
OMEMO полностью работает на клиентах. Prosody хранит ключи/девайс-листы через
PEP (`pep` в modules_enabled — уже включён). Если PEP включён — OMEMO работает
для любого клиента (Gajim, Conversations, Dino, Converse). Серверного
`mod_omemo` в prosody-modules НЕТ и он не нужен (это норма, не потеря).
## Converse v14 (веб-чат)
- OMEMO встроен: в dist лежат `libomemo.esm.min.js` + `curve25519_compiled.wasm`
(проверить: `grep -c omemo converse.min.js` > 0, есть `eu.siacs.conversations.axolotl`).
- ЕДИНСТВЕННАЯ опция в initialize(): `omemo_default: true` (default false) —
«шифровать по умолчанию, когда контакт поддерживает». Опции `allow_omemo`
в v14 нет (всегда доступен).
- При логине с omemo_default:true Converse сам генерит device id и публикует
devicelist + bundle в PEP. Проверять НЕ через UI — смотреть на диск.
## Авторитетная проверка — файлы PEP на диске (host bigbox)
Каталог данных: `data/` из docker-compose (в контейнере `/var/lib/prosody`),
имена узлов URL-encoded:
- devicelist: `data/<domain-encoded>/pep_eu%2esiacs%2econversations%2eaxolotl%2edevicelist/admin.list`
→ grep `["id"] = "N"` — все устройства пользователя (например 4040, 14035).
- bundle: `data/<domain>/pep_eu%2esiacs%2econversations%2eaxolotl%2ebundles%3a<deviceid>/admin.list`
→ должен содержать `identityKey`, 100× `preKeyPublic` (preKeyId 0..99),
`signedPreKeyPublic` + `signedPreKeySignature`, имя `bundle`.
- <domain-encoded>: nixg.ru → `nixg%2eru` (старый chin: `chat%2enixg%2eru`).
## slixmpp 1.17: publish в PEP-узел (рабочий рецепт)
Подключение по WebSocket, паттерн 1.17 (нет `.process()`, `connect` возвращает Future):
```python
import asyncio, ssl, slixmpp
from slixmpp.xmlstream import ET
ctx = ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE
bot = slixmpp.ClientXMPP('user@domain', 'pass')
bot.ssl_context = ctx
bot.register_plugin('xep_0060')
bot.add_event_handler('session_start', ...) # в handler: send_presence(); await get_roster()
fut = bot.connect(('wss://xmpp.example.com/xmpp-websocket',))
await asyncio.wait_for(fut, 20); await asyncio.sleep(6) # дать событиям отработать
```
Publish (payload — СЫРОЙ lxml-элемент, не высокоуровневый API!):
```python
lst = ET.Element('{eu.siacs.conversations.axolotl}list')
ET.SubElement(lst, '{eu.siacs.conversations.axolotl}device', {'id': '7777'})
await bot['xep_0060'].publish(jid=JID, node='eu.siacs.conversations.axolotl.devicelist',
id='current', payload=lst)
```
Засады:
- `iq['pubsub']['publish']['item']...` + `ElementBase.append('list', {'xmlns':...})`
падает: `TypeError: ElementBase.append() takes 2 positional arguments but 3 were given`.
- `get_items` может вернуть 0 items даже когда узел НЕ пуст (семантика доступа PEP) —
диск `admin.list` авторитетнее. Проверять publish через чтение файла.
## Проверка веб-клиента headless (camofox)
- `camofox-browser open https://chat...` затем `eval` с `JSON.stringify(...)` —
иначе возвращается только `resultType`, без значения.
- Форма входа: `input[name=jid]`, `input[name=password]`, чекбокс `trusted`;
submit-кнопка `button.btn-primary` (текст «Войти»). Заполнять через
`el.value=...; el.dispatchEvent(new Event('input',{bubbles:true}))`, потом click.
- После входа в DOM: `document.body.innerText` содержит «Я на связи».
В DOM появляется строка «OMEMO» (кнопка в чате).
- Новый device от сессии виден в devicelist admin.list + каталог bundles:<id>.
## Очистка/удаление своего устройства из devicelist
Republish devicelist с оставшимися id (паттерн выше), id='current' — примет,
тестовый id исчезает из admin.list.
## Сопутствующее
- `omemo_default` в доке Converse: https://conversejs.org/docs/configuration/
(redirect с /docs/html/...). Единственная omemo-опция.
- Converse api.omemo недоступен из window.converse — проверять по диску, не по JS API.