core, dependencies: update Tx-XMPP to v0.13.0:

`DiscoFeature.identities` format has evolved so `(category, map)` can be checked directly
again. The code using identities has been updated.
This commit is contained in:
Goffi 2026-04-18 02:02:29 +02:00
commit fd58b78762
11 changed files with 70 additions and 56 deletions

View file

@ -960,16 +960,17 @@ class LiberviaBackend(service.Service):
disco = await self.get_disco_infos(client, res_jid)
for (category, type_, lang), name in disco.identities.items():
identities = res_data.setdefault("identities", [])
identity_entry = {
"name": name,
"category": category,
"type": type_,
}
if lang:
identity_entry["lang"] = lang
identities.append(identity_entry)
for (category, type_), identities_map in disco.identities.items():
for lang, identity in identities_map.items():
identities = res_data.setdefault("identities", [])
identity_entry = {
"name": identity.name,
"category": identity.category,
"type": identity.type,
}
if lang is not None:
identity_entry["lang"] = lang
identities.append(identity_entry)
ret_data.append(res_data)
@ -1433,13 +1434,14 @@ class LiberviaBackend(service.Service):
).format(service_jid=service_jid.full())
)
continue
if identities is not None and not set(
k[:2] for k in infos.identities.keys()
).issuperset(identities):
if identities is not None and not set(infos.identities.keys()).issuperset(
identities
):
continue
found_identities = [
(cat, type_, lang, name or "")
for (cat, type_, lang), name in infos.identities.items()
(cat, type_, lang, identity.name or "")
for (cat, type_), lang_identities in infos.identities.items()
for lang, identity in lang_identities.items()
]
found_service[service_jid.full()] = found_identities
@ -1497,12 +1499,13 @@ class LiberviaBackend(service.Service):
continue
if infos.features.issuperset(namespaces):
if identities is not None and not set(
k[:2] for k in infos.identities.keys()
infos.identities.keys()
).issuperset(identities):
continue
found_identities = [
(cat, type_, lang, name or "")
for (cat, type_, lang), name in infos.identities.items()
(cat, type_, lang, identity.name or "")
for (cat, type_), lang_identities in infos.identities.items()
for lang, identity in lang_identities.items()
]
found[full_jid.full()] = found_identities

View file

@ -1489,13 +1489,17 @@ class SatMessageProtocol(xmppim.MessageProtocol):
)
else:
domain_disco_infos = disco_infos
if any(k[:2] == ("client", "bot") for k in disco_infos.identities):
if ("client", "bot") in disco_infos.identities:
extra["is-bot"] = True
for (category, type_, lang), label in domain_disco_infos.identities.items():
if category == "gateway":
extra["origin"] = type_
if label:
extra["origin_name"] = label
for (
category,
type_,
), identities_map in domain_disco_infos.identities.items():
for lang, identity in identities_map.items():
if category == "gateway":
extra["origin"] = identity.type
if identity.name:
extra["origin_name"] = identity.name
# message
for e in message_elt.elements(C.NS_CLIENT, "body"):

View file

@ -111,9 +111,7 @@ class Discovery:
# Cache entries are tuples: (data, expiry_timestamp)
# - (disco.DiscoInfo, float): successful query, expires at timestamp
# - (None, float): failed query, retry after DISCO_RETRY_DELAY
self._jid_disco_cache: dict[
jid.JID, tuple[disco.DiscoInfo | None, float]
] = {}
self._jid_disco_cache: dict[jid.JID, tuple[disco.DiscoInfo | None, float]] = {}
def load(self):
"""Load persistent hashes"""
@ -178,9 +176,7 @@ class Discovery:
if not set(features).issubset(disco_infos.features):
raise exceptions.FeatureNotFound()
if identity is not None and not any(
k[:2] == identity for k in disco_infos.identities
):
if identity is not None and identity not in disco_infos.identities:
raise exceptions.FeatureNotFound()
async def has_identity(
@ -200,7 +196,7 @@ class Discovery:
@return: True if the entity has the given identity
"""
disco_infos = await self.get_infos(client, jid_, node)
return any(k[:2] == (category, type_) for k in disco_infos.identities)
return (category, type_) in disco_infos.identities
def get_infos(
self,
@ -357,7 +353,7 @@ class Discovery:
found_entities = set()
def infos_cb(infos, entity_jid):
if any(k[:2] == (category, type_) for k in infos.identities):
if (category, type_) in infos.identities:
found_entities.add(entity_jid)
def got_items(items):
@ -402,9 +398,7 @@ class Discovery:
if entity is None:
log.warning(_("Received an item without JID"))
return
if identity is not None and not any(
k[:2] == identity for k in infos.identities
):
if identity is not None and identity not in infos.identities:
return
if features.issubset(infos.features):
found_entities.add(entity)
@ -454,8 +448,9 @@ class Discovery:
return (
[str(f) for f in disco_infos.features],
[
(cat, type_, lang, name or "")
for (cat, type_, lang), name in list(disco_infos.identities.items())
(cat, type_, lang, identity.name or "")
for (cat, type_), lang_identities in list(disco_infos.identities.items())
for lang, identity in lang_identities.items()
],
extensions,
)

View file

@ -728,9 +728,10 @@ class APGateway:
async def is_pubsub(self, jid_: jid.JID) -> bool:
"""Indicate if a JID is a Pubsub service"""
host_disco = await self.host.get_disco_infos(self.client, jid_)
return any(
k[:2] == ("pubsub", "service") for k in host_disco.identities
) and not any(k[:2] == ("pubsub", "pep") for k in host_disco.identities)
return ("pubsub", "service") in host_disco.identities and not (
"pubsub",
"pep",
) in host_disco.identities
async def get_jid_and_node(self, ap_account: str) -> tuple[jid.JID, str | None]:
"""Decode raw AP account handle to get XMPP JID and Pubsub Node

View file

@ -753,11 +753,16 @@ class Identity:
except Exception as e:
log.exception(f"Can't get disco infos for {entity}.")
else:
for (category, type_, lang), name in disco_infos.identities.items():
identity_entry = {"category": category, "type": type_, "name": name}
if lang:
identity_entry["lang"] = lang
identities.append(identity_entry)
for (category, type_), identities_map in disco_infos.identities.items():
for lang, identity in identities_map.items():
identity_entry = {
"category": identity.category,
"type": identity.type,
"name": identity.name,
}
if lang is not None:
identity_entry["lang"] = lang
identities.append(identity_entry)
return id_data

View file

@ -503,7 +503,11 @@ class XEP_0060:
infos = await self.host.memory.disco.get_infos(client, service_jid)
if not DEFAULT_PUBSUB_MIN_FEAT.issubset(infos.features):
continue
names = {(n or "").lower() for n in infos.identities.values()}
names = {
(i.name or "").lower()
for identities_map in infos.identities.values()
for i in identities_map.values()
}
if "libervia pubsub service" in names:
# this is the name of Libervia's side project pubsub service, we know
# that it is a suitable default pubsub service

View file

@ -299,7 +299,7 @@ class XEP_0100(object):
else:
entity = items[idx].entity
gateways = [
(identity, result.identities[identity])
(identity, list(result.identities[identity].values())[0])
for identity in result.identities
if identity[0] == "gateway"
]
@ -308,7 +308,7 @@ class XEP_0100(object):
_("Found gateway [{jid}]: {identity_name}").format(
jid=entity.full(),
identity_name=" - ".join(
[gateway[1] for gateway in gateways]
[gateway[1].name for gateway in gateways]
),
)
)

View file

@ -141,7 +141,7 @@ class XEP_0353:
else:
raise e
else:
categories = {c for c, __, __ in infos.identities}
categories = {c for c, __ in infos.identities}
if "component" in categories:
# we don't use message initiation with components
return True

View file

@ -1108,10 +1108,12 @@ class XEP_0373:
"Error performing service discovery on the own bare JID."
) from e
identities = cast(dict[tuple[str, str, str], str], infos.identities)
identities = cast(
dict[tuple[str, str], dict[str | None, disco.DiscoIdentity]], infos.identities
)
features = cast(set[DiscoFeature], infos.features)
if not any(k[:2] == ("pubsub", "pep") for k in identities):
if ("pubsub", "pep") not in identities:
raise exceptions.FeatureNotFound("Server doesn't support PEP.")
if "http://jabber.org/protocol/pubsub#access-whitelist" not in features:

View file

@ -60,7 +60,7 @@ dependencies = [
"urwid >= 1.2.0, < 3",
"urwid-satext",
"xmlschema < 4.3.1",
"tx-xmpp>=0.12",
"tx-xmpp>=0.13",
]
keywords = [
"XMPP",

8
uv.lock generated
View file

@ -887,7 +887,7 @@ requires-dist = [
{ name = "treq", specifier = "~=24.9.0" },
{ name = "twisted", extras = ["tls"], specifier = ">=22.10" },
{ name = "twomemo", specifier = ">=1.0.0,<2" },
{ name = "tx-xmpp", specifier = ">=0.12" },
{ name = "tx-xmpp", specifier = ">=0.13" },
{ name = "txdbus" },
{ name = "txtorcon", marker = "extra == 'all'", specifier = "~=24.8.0" },
{ name = "txtorcon", marker = "extra == 'main'", specifier = "~=24.8.0" },
@ -2296,16 +2296,16 @@ wheels = [
[[package]]
name = "tx-xmpp"
version = "0.12.0"
version = "0.13.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "constantly" },
{ name = "python-dateutil" },
{ name = "twisted", extra = ["tls"] },
]
sdist = { url = "https://files.pythonhosted.org/packages/9c/cd/fb4adb527b0de086ca23d9e081b12bc1a1e780a451105d6fbc7dd8c34a2f/tx_xmpp-0.12.0.tar.gz", hash = "sha256:a71f288f5055e0f5aef52934e456f4ca016b4763d08dfa02b2817b94b8b93380", size = 129364, upload-time = "2026-04-17T11:45:57.026Z" }
sdist = { url = "https://files.pythonhosted.org/packages/78/70/d69eb2e624288dd645996b218021e496d6db78d5112006bbfd64c43e790f/tx_xmpp-0.13.0.tar.gz", hash = "sha256:0458df37900671968448ed1cae43f18cd5ab17d1df8e759d9048ed341c706ac0", size = 129484, upload-time = "2026-04-17T23:28:44.947Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/af/94374605bfed72d71336cc22161a698f66a8acff23d0b4274ce91484560f/tx_xmpp-0.12.0-py3-none-any.whl", hash = "sha256:95abbd5209b19265e802b7d44d0589e72612b3b200528ef0eef4b8aa2ca4e97c", size = 143268, upload-time = "2026-04-17T11:45:55.62Z" },
{ url = "https://files.pythonhosted.org/packages/af/ca/fd0f4a5a5e8a0c39da6255256d36ced30b6e73357e35b98a4aa084ffab80/tx_xmpp-0.13.0-py3-none-any.whl", hash = "sha256:181945a84d72851d335bcd053d1f7b4d2030f3926b1c0e7aa935c590984696a1", size = 143279, upload-time = "2026-04-17T23:28:46.337Z" },
]
[[package]]