Greetings.
I am working on a Chawan script to download data from XMPP PubSub nodes.
#!/usr/bin/env python3
# coding: utf-8
#
# Usage:
#
# printf "xmpp:pubsub.movim.eu?;node=berlin-xmpp-meetup" | python pubsub.py > berlin-xmpp-meetup.pubsub
import asyncio
import configparser
import datetime
import os
import select
import sys
from urllib.parse import urlparse
import xml.etree.ElementTree as ET
class Chawan:
def directives(cha_control, content=None, content_type=None, status_code=None) -> None:
if status_code:
sys.stdout.write(f"Status: {status_code}\n")
sys.stdout.write(f"Cha-Control: {cha_control}\n")
if content_type:
sys.stdout.write(f"Content-Type: {content_type}\n")
if content:
sys.stdout.write("\n")
sys.stdout.write(content)
sys.stdout.write("\n")
sys.stdout.flush()
sys.exit()
try:
from slixmpp import ClientXMPP
from slixmpp.exceptions import IqError, IqTimeout
from slixmpp.stanza.iq import Iq
#from slixmpp.xmlstream import ET
except Exception as e:
cha_control = f"ConnectionError InternalError {str(e)}"
content = content_type = status_code = None
Chawan.directives(cha_control, content, content_type, status_code)
class Xmpp:
async def pubsub(
client_xmpp: ClientXMPP, jid_bare: str, node_name=None, item_id=None
) -> dict:
result = {}
iq = None
try:
if jid_bare and node_name and item_id:
iq = await client_xmpp.plugin["xep_0060"].get_item(
jid_bare, node_name, item_id, timeout=5)
elif jid_bare and node_name:
iq = await client_xmpp.plugin["xep_0060"].get_items(
jid_bare, node_name, timeout=15)
elif jid_bare:
iq = await client_xmpp.plugin["xep_0060"].get_nodes(
jid_bare, timeout=10)
except (IqError, IqTimeout) as e:
error_condition = e.iq["error"]["condition"]
error_text = e.iq["error"]["text"]
if iq:
if "pubsub" in iq:
#if "items" in iq["pubsub"] and "item" in iq["pubsub"]["items"]:
if iq["pubsub"]["items"]["item"]["payload"] is not None:
result = {"content" : str(iq["pubsub"]),
"message" : "Success",
"error" : False}
else:
# This is not an error. It is a state of event of an empty node.
result = {"content" : None,
"message" : "PubSub node appears to be empty",
"error" : True}
elif "error" in iq:
result = {"content" : None,
"message" : f"{error_condition}: {error_text}",
"error" : True}
else:
result = {"content" : None,
"message" : f"Unknown error: {str(iq)}",
"error" : True}
else:
result = {"content" : None,
"message" : "Unknown error: Stanza IQ is empty",
"error" : True}
return result
if __name__ == "__main__":
directory_data = (
os.environ.get("CHA_DATA_DIR") or
os.path.join(os.environ.get("HOME"), ".local/share/chawan"))
credentials = os.path.join(directory_data, "credentials.ini")
config = configparser.ConfigParser()
config.read(credentials)
if ("xmpp" in config and
"address" in config["xmpp"] and
"password" in config["xmpp"]):
address = config["xmpp"]["address"]
password = config["xmpp"]["password"]
stdin_input = sys.stdin.read()
if stdin_input:
uri = stdin_input
else:
uri_scheme = os.environ.get("MAPPED_URI_SCHEME")
uri_path = os.environ.get("MAPPED_URI_PATH")
uri_query = os.environ.get("MAPPED_URI_QUERY")
uri = f"{uri_scheme}:{uri_path}?{uri_query}"
fragmented_uri = urlparse(uri)
if fragmented_uri.scheme == "xmpp":
jid_bare = fragmented_uri.path
item_id = node_name = None
for parameter in fragmented_uri.query.split(";"):
if parameter.startswith("node="):
node_name = parameter[5:]
if parameter.startswith("item="):
item_id = parameter[5:]
if jid_bare or jid_bare and node_name:
client_xmpp = ClientXMPP(address, password)
client_xmpp.register_plugin("xep_0060")
#client_xmpp.register_plugin("xep_0199")
#client_xmpp.connect()
# ERROR:slixmpp.features.feature_mechanisms.mechanisms:No appropriate login method.
loop = asyncio.get_event_loop()
#rtt = loop.run_until_complete(client_xmpp.plugin["xep_0199"].ping())
#if rtt:
try:
client_xmpp.connect()
#result = asyncio.run(
# Xmpp.pubsub(client_xmpp, jid_bare, node_name, item_id))
result = loop.run_until_complete(
Xmpp.pubsub(client_xmpp, jid_bare, node_name, item_id))
client_xmpp.disconnect(
ignore_send_queue=False,
reason="Chawan: Receiving PubSub data was completed.",
wait=2.0)
# TODO if no items in result node (an empty node)
if result["error"]:
message = result["message"]
cha_control = f"ConnectionError ConnectionRefused {message}."
content = content_type = status_code = None
else:
"""
xml = ET.fromstring(result["content"])
namespaces = {
"atom" : "http://www.w3.org/2005/Atom",
"pubsub" : "http://jabber.org/protocol/pubsub",
"xhtml" : "http://www.w3.org/1999/xhtml"}
for namespace in namespaces:
ET.register_namespace(
namespace, namespaces[namespace])
ET.indent(xml, space=" ", level=0)
content = ET.tostring(xml, encoding="unicode")
"""
content = result["content"]
cha_control = "ControlDone"
content_type = "application/pubsub+xml"
status_code = "200"
except Exception as e:
message = f"Check whether credentials or XMPP URI are valid; {str(e)}"
cha_control = f"ConnectionError ConnectionRefused {message}."
content = content_type = status_code = None
else:
message = f"Jabber ID or Node Name appear to be missing: {uri}"
cha_control = f"ConnectionError InvalidURL {message}."
content = content_type = status_code = None
else:
message = f"Invalid XMPP PubSub Query URI: {uri}"
cha_control = f"ConnectionError InvalidURL {message}."
content = content_type = status_code = None
else:
message = f"XMPP credentials are missing {credentials}"
cha_control = f"ConnectionError InternalError {message}."
content = content_type = status_code = None
Chawan.directives(cha_control, content, content_type, status_code)
Interestingly, when downloading from xmpp:publisher@slixfeed.goodbytes.im?;node=urn:xmpp:microblog:0 only a single item is downloaded, which is the most recent uploaded item.
Yet, when downloading from XMPP URI xmpp:publisher@slixfeed.goodbytes.im?;node=urn:xmpp:microblog:0;item=2026-01-01-recommended-xmpp-desktop-chat-clients it is possible to retrieve the data, albeit it is not appear to be listed, as Openfire indicates that only a single item is present.
printf “xmpp:publisher@slixfeed.goodbytes.im?;node=urn:xmpp:microblog:0;item=2026-01-01-recommended-xmpp-desktop-chat-clients” | python pubsub.cgi
Status: 200
Cha-Control: ControlDone
Content-Type: application/pubsub+xml
<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="urn:xmpp:microblog:0"><item id="2026-01-01-recommended-xmpp-desktop-chat-clients"><entry xmlns="http://www.w3.org/2005/Atom" xml:lang="en-ps"><title type="text">Recommended XMPP desktop chat clients</title><summary type="text">Recommended XMPP chat clients for desktop.</summary><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
<p>This is a sample article that showcases a custom XSLT stylesheet which is
intended for multimedia media galleries.</p>
<p>This gallery is realized by setting preambles of "links" with rel "related"
and "image" of the same title.</p>
<pre class="code rst literal-block"><code>title,href,rel,type<span class="whitespace">
</span>Converse, /graphics/conversejs.svg, image, image/svg+xml<span class="whitespace">
</span>Converse, https://conversejs.org, related</code></pre>
<p>Schapps recommends and endorses these chat clients, although there is no attempt
to judge or compare them based on any criterion other than freedom; the software
herein are listed by the ease of installation and common purposes for home
computers.</p>
</div>
</content><category term="chat" label="Chat" scheme="/tag" /><category term="jabber" label="Jabber" scheme="/tag" /><category term="voip" label="VoIP" scheme="/tag" /><category term="xmpp" label="XMPP" scheme="/tag" /><link title="Recommended XMPP desktop chat clients" href="/posts/2026-01-01-recommended-xmpp-desktop-chat-clients" rel="base" /><link title="Recommended XMPP desktop chat clients" href="/posts/2026-01-01-recommended-xmpp-desktop-chat-clients/index.atom" rel="self" /><link title="Download this article" href="/posts/2026-01-01-recommended-xmpp-desktop-chat-clients/enclosures.meta4" type="application/metalink4-xml" rel="metalink" /><link title="Article source" href="/posts/2026-01-01-recommended-xmpp-desktop-chat-clients/source.rst" type="text/x-rst" rel="source" /><link title="17 XMPP chat clients" href="/posts/2026-01-01-recommended-xmpp-desktop-chat-clients/clients.ms.txt" type="text/plain" rel="microsummary" /><link title="css" href="/css/gallery.css" type="text/css" rel="stylesheet" /><link title="html" href="/xslt/page-gallery.xslt" type="application/xslt+xml" rel="stylesheet" /><link title="The motto of XMPP" href="/graphics/xmpp_motto.svg" type="image/svg+xml" rel="photo" /><link title="The logo of the protocol XMPP" href="/graphics/xmpp.svg" type="image/svg+xml" rel="avatar" /><link title="Psi" href="/graphics/psi.svg" type="image/svg+xml" rel="image" /><link title="Miranda NG" href="/graphics/miranda.svg" type="image/svg+xml" rel="image" /><link title="UWPX" href="/graphics/uwpx.svg" type="image/svg+xml" rel="image" /><link title="Pidgin" href="/graphics/pidgin.svg" type="image/svg+xml" rel="image" /><link title="Converse" href="/graphics/conversejs.svg" type="image/svg+xml" rel="image" /><link title="CoyIM" href="/graphics/coyim.svg" type="image/svg+xml" rel="image" /><link title="Beagle IM" href="/graphics/beagle.svg" type="image/svg+xml" rel="image" /><link title="Monal" href="/graphics/monal.svg" type="image/svg+xml" rel="image" /><link title="Spark" href="/graphics/spark.svg" type="image/svg+xml" rel="image" /><link title="Pàdé" href="/graphics/pade.svg" type="image/svg+xml" rel="image" /><link title="Fluux" href="/graphics/fluux.svg" type="image/svg+xml" rel="image" /><link title="Smuxi" href="/graphics/smuxi.svg" type="image/svg+xml" rel="image" /><link title="Kaidan" href="/graphics/kaidan.svg" type="image/svg+xml" rel="image" /><link title="Gajim" href="/graphics/gajim.svg" type="image/svg+xml" rel="image" /><link title="Dino" href="/graphics/dino.svg" type="image/svg+xml" rel="image" /><link title="Squawk" href="/graphics/squawk.svg" type="image/svg+xml" rel="image" /><link title="Swift" href="/graphics/swift.svg" type="image/svg+xml" rel="image" /><link title="Prose" href="/graphics/prose.svg" type="image/svg+xml" rel="image" /><link title="Dergchat" href="/graphics/dergchat.svg" type="image/svg+xml" rel="image" /><link title="Beagle IM" href="https://beagle.im" rel="related" /><link title="Converse" href="https://conversejs.org" rel="related" /><link title="CoyIM" href="https://coy.im" rel="related" /><link title="Dino" href="https://dino.im" rel="related" /><link title="Dergchat" href="https://codeberg.org/Mizah/Dergchat" rel="related" /><link title="Fluux" href="https://github.com/processone/fluux-messenger" rel="related" /><link title="Gajim" href="https://gajim.org" rel="related" /><link title="Kaidan" href="https://kaidan.im" rel="related" /><link title="Miranda NG" href="https://miranda-ng.org" rel="related" /><link title="Monal" href="https://monal-im.org" rel="related" /><link title="Pàdé" href="https://igniterealtime.org/projects/pade/" rel="related" /><link title="Pidgin" href="https://pidgin.im" rel="related" /><link title="Psi" href="https://psi-im.org" rel="related" /><link title="Prose" href="https://prose.org" rel="related" /><link title="Spark" href="https://igniterealtime.org/projects/spark/" rel="related" /><link title="Squawk" href="https://macaw.me/projects/squawk/" rel="related" /><link title="Smuxi" href="https://smuxi.im" rel="related" /><link title="Swift" href="https://swift.im" rel="related" /><link title="UWPX" href="https://uwpx.org" rel="related" /><link title="XMPP as the internet" href="/posts/2024-11-05-xmpp-as-the-internet" rel="related" /><link title="Recommended XMPP mobile chat clients" href="/posts/2026-01-01-recommended-xmpp-mobile-chat-clients" rel="related" /><link title="CoyIM" href="https://coy.im/feed.xml" rel="feed" /><link title="Dino" href="https://dino.im/index.xml" rel="feed" /><link title="Gajim" href="https://gajim.org/index.xml" rel="feed" /><link title="Kaidan" href="https://kaidan.im/atom.xml" rel="feed" /><link title="Miranda NG" href="https://miranda-ng.org/index.xml" rel="feed" /><link title="Monal" href="https://monal-im.org/index.xml" rel="feed" /><link title="Smuxi" href="https://smuxi.im/news/index.rss" rel="feed" /><link title="Swift" href="https://swift.im/wordpress/index.php/feed/atom/" rel="feed" /><link href="/posts/2026-01-01-recommended-xmpp-desktop-chat-clients/index.atom" type="application/atom+xml" rel="alternate" /><link href="xmpp:publisher@slixfeed.goodbytes.im?;node=urn:xmpp:microblog:0;item=2026-01-01-recommended-xmpp-desktop-chat-clients" type="application/pubsub+xml" rel="self" /><link href="xmpp:publisher@slixfeed.goodbytes.im?;node=urn:xmpp:microblog:0;item=2026-01-01-recommended-xmpp-desktop-chat-clients" type="application/pubsub+xml" rel="alternate" /><link title="comments" href="xmpp:discussions.slixfeed.goodbytes.im?;node=publisher@slixfeed.goodbytes.im/urn:xmpp:microblog:0/2026-01-01-recommended-xmpp-desktop-chat-clients" type="application/pubsub+xml" rel="replies" /><link title="comments" href="xmpp:discussions.slixfeed.goodbytes.im?;node=publisher@slixfeed.goodbytes.im/urn:xmpp:microblog:0/2026-01-01-recommended-xmpp-desktop-chat-clients" type="application/pubsub+xml" rel="replies" /><link title="Recommended Linux desktop distros" href="xmpp:publisher@slixfeed.goodbytes.im?;node=urn:xmpp:microblog:0;item=2026-01-01-recommended-linux-desktop-distros" rel="previous" /><link title="Recommended XMPP mobile chat clients" href="xmpp:publisher@slixfeed.goodbytes.im?;node=urn:xmpp:microblog:0;item=2026-01-01-recommended-xmpp-mobile-chat-clients" rel="next" /><published>2026-01-01 0:00:00 UTC</published></entry></item></items></pubsub>
Could it be that this item, and other items, are temporarily cached?