import sys
import time
from twisted.words.protocols.jabber import xmlstream, client, jid
from twisted.internet import reactor
from twisted.words.xish import domish
from twisted.internet.defer import Deferred
from twisted.internet.error import ReactorNotRunning
from twisted.internet import stdio
class XMPPClient( object ):
def __init__( self ):
self.xmppClientFactory = None
self.full_jid = None
self.password = None
self.session_id = None
self.reactor = None # so the client can control it's own reactor.stop()
def initialize_factory( self ):
""" initializes the factory with an authenticator, uses xmpp-tls
"""
self.xmppClientFactory = client.XMPPClientFactory( self.full_jid, self.password )
# auth = domish.Element((NS_XMPP_SASL, 'auth')) , auth.toXml() -> <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'/>
# jabber.sasl.sendAuth()
def connected( self, xml_stream ):
""" override the xmlstream methods
"""
print 'Connected.'
xml_stream.rawDataOutFn = self.rawDataOut
xml_stream.rawDataInFn = self.rawDataIn
def rawDataOut( self, buf ):
print "SEND: %s" % unicode( buf , 'utf-8' ).encode( 'ascii' , 'replace' )
def rawDataIn( self, buf ):
print "RECV: %s" % unicode( buf , 'utf-8' ).encode( 'ascii' , 'replace' )
def disconnected( self, xml_stream ):
""" called when there's an EndStreamEvent (i.e. other side closed the connection)
"""
print 'Disconnected.'
# try:
# self.reactor.stop()
# except ReactorNotRunning:
# pass
# # TODO: exit the application (use a Deferred?)
def authenticated( self, xml_stream ):
print "Authenticated."
# presence = domish.Element( (None , 'presence') ) # <presence/>
# xml_stream.send( presence )
# RECV: <iq type='result' id='H_1'/>
# RECV: <presence to='48340_325144@chat.hipchat.com' from='48340_325144@chat.hipchat.com/lenovo'/>
# deferred = self.send_discovery( self.full_jid.host, xml_stream )
# deferred.addCallbacks( XMPPClient.deferred_default, XMPPClient.deferred_error )
# # TODO: extract the items, and continue the discovery process (recursion?)
# time.sleep( 1 )
# xml_stream.sendFooter() # http://xmpp.org/extensions/xep-0198.html#closure , the server sets status unavailable
@staticmethod
def deferred_default( result ):
print "DEFERRED FIRED", type( result )
print result.toXml()
@staticmethod
def deferred_error( failure ):
print "Deferred error"
print failure
# TODO: check if reactor is running first?
reactor.stop()
@staticmethod
def deferred_quit( ):
reactor.stop()
def send_presence( self, xml_stream, status ):
presence = domish.Element( (None , 'presence') ) # <presence/>
presence['type'] = status
xml_stream.send( presence )
def send_discovery( self, to, xml_stream ):
discovery_iq = xmlstream.IQ( xml_stream, "get" )
discovery_iq[ 'to' ] = to
query = domish.Element(( "http://jabber.org/protocol/disco#items", 'query') )
discovery_iq.addChild( query )
iq_deferred = discovery_iq.send() # returns a deferred that is fired when the response is received
return iq_deferred
if __name__ == '__main__':
if len( sys.argv ) != 3 :
print "correct usage: python xmppclient.py fulljid@chat.hipchat.com/device password"
sys.exit( 1 )
xmpp_client = XMPPClient()
xmpp_client.full_jid = jid.JID( sys.argv[1] )
xmpp_client.password = sys.argv[2]
xmpp_client.initialize_factory()
xmpp_client.xmppClientFactory.addBootstrap( xmlstream.STREAM_CONNECTED_EVENT , xmpp_client.connected ) # when there's a connected event, use the connected function
xmpp_client.xmppClientFactory.addBootstrap( xmlstream.STREAM_END_EVENT , xmpp_client.disconnected ) # when there's a stream end event, use the disconnected function
xmpp_client.xmppClientFactory.addBootstrap( xmlstream.STREAM_AUTHD_EVENT , xmpp_client.authenticated )
xmpp_client.reactor = reactor
xmpp_client.reactor.connectTCP( 'chat.hipchat.com' ,5222, xmpp_client.xmppClientFactory ) # connects to the host:port, then depends on the factory's callBacks
xmpp_client.reactor.run()