bontchev
bontchev

Reputation: 568

How to send messages to an XMPP server using only Twisted?

I need to send messages to an XMPP server. Only send - I don't need to process replies, make a bot, or anything like that. When using the xmpppy module, it goes something like this:

from xmpp import Client
from xmpp.protocol import JID, Message


jabberid = '[email protected]'
password = 'secret'
receiver = '[email protected]'
message = 'Hello world!'

jid = JID(jabberid)
connection = Client(server=jid.getDomain(), debug=None)
connection.connect()
connection.auth(user=jid.getNode(), password=password, resource=jid.getResource())
connection.send(Message(to=receiver, body=message))

However, I need to do this using Twisted. Unfortunately, the documentation is mostly useless (seems machine-generated) and I have absolutely no idea what I am supposed to do. :-(

Maybe something like

from twisted.words.protocols.jabber.jid import JID
from twisted.words.protocols.jabber.client import XMPPAuthenticator

jabberid = '[email protected]'
password = 'secret'

jid = JID(jabberid)
XMPPAuthenticator(jid, password)

but what then?

Upvotes: 0

Views: 61

Answers (2)

Guus
Guus

Reputation: 3006

The Ignite Realtime community has a document that provides a minimal working example of a client implementation using the Twisted Words library, making it connect to a running Openfire server.

https://download.igniterealtime.org/openfire/docs/latest/documentation/client-minimal-working-example-twistedwords.html

The example that is used there is copied here (but the document contains more information).

#!/usr/bin/python
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
A very simple twisted xmpp-client (Jabber ID)

To run the script:
$ python xmpp_client.py <jid> <secret>
"""


import sys

from twisted.internet.defer import Deferred
from twisted.internet.task import react
from twisted.internet._sslverify import (ClientTLSOptions,
                                         OpenSSLCertificateOptions)
from twisted.names.srvconnect import SRVConnector
from twisted.words.xish import domish
from twisted.words.protocols.jabber import xmlstream, client
from twisted.words.protocols.jabber.jid import JID


class Client:
    def __init__(self, reactor, jid, secret, configurationForTLS):
        self.reactor = reactor
        f = client.XMPPClientFactory(jid, secret,
                                     configurationForTLS=configurationForTLS)
        f.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT, self.connected)
        f.addBootstrap(xmlstream.STREAM_END_EVENT, self.disconnected)
        f.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self.authenticated)
        f.addBootstrap(xmlstream.INIT_FAILED_EVENT, self.init_failed)
        connector = SRVConnector(reactor, "xmpp-client", jid.host, f, defaultPort=5222)
        connector.connect()
        self.finished = Deferred()

    def rawDataIn(self, buf):
        print("RECV: %r" % buf)

    def rawDataOut(self, buf):
        print("SEND: %r" % buf)

    def connected(self, xs):
        print("Connected.")

        self.xmlstream = xs

        # Log all traffic
        xs.rawDataInFn = self.rawDataIn
        xs.rawDataOutFn = self.rawDataOut

    def disconnected(self, reason):
        print("Disconnected.")
        print(reason)

        self.finished.callback(None)

    def authenticated(self, xs):
        print("Authenticated.")

        presence = domish.Element((None, "presence"))
        xs.send(presence)

        self.reactor.callLater(5, xs.sendFooter)

    def init_failed(self, failure):
        print("Initialization failed.")
        print(failure)

        self.xmlstream.sendFooter()


def main(reactor, jid, secret):
    """
    Connect to the given Jabber ID and return a L{Deferred} which will be
    called back when the connection is over.

    @param reactor: The reactor to use for the connection.
    @param jid: A L{JID} to connect to.
    @param secret: A C{str}
    """
    configurationForTLS = ClientTLSOptions(JID(jid).host,
                                           OpenSSLCertificateOptions().getContext())
    return Client(reactor, JID(jid), secret,
                  configurationForTLS=configurationForTLS).finished


if __name__ == "__main__":
    react(main, sys.argv[1:])

Upvotes: 0

Jean-Paul Calderone
Jean-Paul Calderone

Reputation: 48335

From https://twisted.org/ click the "View Document" button, then choose "Twisted Words (IRC and XMPP)" from the index on the left, then choose "Examples" on the resulting page, then download the xmpp_client.py example.

In this example, you'll see how to use the reactor to make your client connect to a server, how to use the bootstrap APIs to handle basic connection management events, and how to do basic presence management.

After that, it depends what kind of messages you want to send (presence is managed by sending messages, for example). If you want to send a XEP-0045 "groupchat" message, such as is described at https://xmpp.org/extensions/xep-0045.html#message, construct one using the xish APIs and send it instead of or along with your presence message.

If you want to send a message like:

<message
    to='[email protected]'
    from='[email protected]/balcony'
    type='chat'
    xml:lang='en'>
  <body>Wherefore art thou, Romeo?</body>
</message>

which I'm guessing is what this line from your question does:

connection.send(Message(to=receiver, body=message))

Then you can do something like:

from twisted.words.xish import domish

message = domish.Element((None, 'message'))
message['to'] = '[email protected]'
message.addElement('body', content='Hi!')

This is adapted from an example in the documentation.

Then you can send the message after your client authenticates. If you modify the xmpp_client.py example by replacing the authenticated method with this one:

    def authenticated(self, xs):
        print("Authenticated.")

        message = domish.Element((None, 'message'))
        message['to'] = '[email protected]'
        message.addElement('body', content='Hi!')
        xs.send(message)

        self.reactor.callLater(5, xs.sendFooter)

Upvotes: 0

Related Questions