2014-08-16 20:37:29 +00:00
|
|
|
#!/usr/bin/env python3
|
2011-03-24 03:00:41 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
2014-07-17 12:19:04 +00:00
|
|
|
Slixmpp: The Slick XMPP Library
|
2011-03-24 03:00:41 +00:00
|
|
|
Copyright (C) 2010 Nathanael C. Fritz
|
2014-07-17 12:19:04 +00:00
|
|
|
This file is part of Slixmpp.
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
See the file LICENSE for copying permission.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import logging
|
2014-08-16 20:37:29 +00:00
|
|
|
from getpass import getpass
|
2014-08-16 20:37:29 +00:00
|
|
|
from argparse import ArgumentParser
|
2011-03-24 03:00:41 +00:00
|
|
|
|
2014-07-17 12:19:04 +00:00
|
|
|
import slixmpp
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
|
2014-07-17 12:19:04 +00:00
|
|
|
class CommandBot(slixmpp.ClientXMPP):
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
"""
|
2014-07-17 12:19:04 +00:00
|
|
|
A simple Slixmpp bot that provides a basic
|
2011-03-24 03:00:41 +00:00
|
|
|
adhoc command.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, jid, password):
|
2014-07-17 12:19:04 +00:00
|
|
|
slixmpp.ClientXMPP.__init__(self, jid, password)
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
# The session_start event will be triggered when
|
|
|
|
# the bot establishes its connection with the server
|
|
|
|
# and the XML streams are ready for use. We want to
|
2012-01-02 20:59:39 +00:00
|
|
|
# listen for this event so that we we can initialize
|
2011-03-24 03:00:41 +00:00
|
|
|
# our roster.
|
|
|
|
self.add_event_handler("session_start", self.start)
|
|
|
|
|
|
|
|
def start(self, event):
|
|
|
|
"""
|
|
|
|
Process the session_start event.
|
|
|
|
|
|
|
|
Typical actions for the session_start event are
|
2012-01-02 20:59:39 +00:00
|
|
|
requesting the roster and broadcasting an initial
|
2011-03-24 03:00:41 +00:00
|
|
|
presence stanza.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
event -- An empty dictionary. The session_start
|
|
|
|
event does not provide any additional
|
|
|
|
data.
|
|
|
|
"""
|
|
|
|
self.send_presence()
|
|
|
|
self.get_roster()
|
|
|
|
|
|
|
|
# We add the command after session_start has fired
|
|
|
|
# to ensure that the correct full JID is used.
|
|
|
|
|
|
|
|
# If using a component, may also pass jid keyword parameter.
|
|
|
|
|
|
|
|
self['xep_0050'].add_command(node='greeting',
|
|
|
|
name='Greeting',
|
|
|
|
handler=self._handle_command)
|
|
|
|
|
|
|
|
def _handle_command(self, iq, session):
|
|
|
|
"""
|
2012-01-02 20:59:39 +00:00
|
|
|
Respond to the initial request for a command.
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
Arguments:
|
|
|
|
iq -- The iq stanza containing the command request.
|
|
|
|
session -- A dictionary of data relevant to the command
|
|
|
|
session. Additional, custom data may be saved
|
|
|
|
here to persist across handler callbacks.
|
|
|
|
"""
|
2016-09-20 06:58:29 +00:00
|
|
|
form = self['xep_0004'].make_form('form', 'Greeting')
|
2011-08-18 08:06:59 +00:00
|
|
|
form['instructions'] = 'Send a custom greeting to a JID'
|
2011-03-24 03:00:41 +00:00
|
|
|
form.addField(var='greeting',
|
|
|
|
ftype='text-single',
|
|
|
|
label='Your greeting')
|
|
|
|
|
|
|
|
session['payload'] = form
|
|
|
|
session['next'] = self._handle_command_complete
|
|
|
|
session['has_next'] = False
|
|
|
|
|
|
|
|
# Other useful session values:
|
|
|
|
# session['to'] -- The JID that received the
|
|
|
|
# command request.
|
|
|
|
# session['from'] -- The JID that sent the
|
|
|
|
# command request.
|
|
|
|
# session['has_next'] = True -- There are more steps to complete
|
|
|
|
# session['allow_complete'] = True -- Allow user to finish immediately
|
|
|
|
# and possibly skip steps
|
|
|
|
# session['cancel'] = handler -- Assign a handler for if the user
|
|
|
|
# cancels the command.
|
|
|
|
# session['notes'] = [ -- Add informative notes about the
|
|
|
|
# ('info', 'Info message'), command's results.
|
|
|
|
# ('warning', 'Warning message'),
|
|
|
|
# ('error', 'Error message')]
|
|
|
|
|
|
|
|
return session
|
|
|
|
|
|
|
|
def _handle_command_complete(self, payload, session):
|
|
|
|
"""
|
|
|
|
Process a command result from the user.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
payload -- Either a single item, such as a form, or a list
|
|
|
|
of items or forms if more than one form was
|
|
|
|
provided to the user. The payload may be any
|
|
|
|
stanza, such as jabber:x:oob for out of band
|
|
|
|
data, or jabber:x:data for typical data forms.
|
|
|
|
session -- A dictionary of data relevant to the command
|
|
|
|
session. Additional, custom data may be saved
|
|
|
|
here to persist across handler callbacks.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# In this case (as is typical), the payload is a form
|
|
|
|
form = payload
|
|
|
|
|
|
|
|
greeting = form['values']['greeting']
|
2011-08-18 08:06:59 +00:00
|
|
|
|
2011-08-23 21:14:21 +00:00
|
|
|
self.send_message(mto=session['from'],
|
|
|
|
mbody="%s, World!" % greeting,
|
2011-08-18 08:06:59 +00:00
|
|
|
mtype='chat')
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
# Having no return statement is the same as unsetting the 'payload'
|
|
|
|
# and 'next' session values and returning the session.
|
|
|
|
|
|
|
|
# Unless it is the final step, always return the session dictionary.
|
|
|
|
|
|
|
|
session['payload'] = None
|
|
|
|
session['next'] = None
|
|
|
|
|
|
|
|
return session
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
# Setup the command line arguments.
|
2014-08-16 20:37:29 +00:00
|
|
|
parser = ArgumentParser()
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
# Output verbosity options.
|
2014-08-16 20:37:29 +00:00
|
|
|
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
|
|
|
|
action="store_const", dest="loglevel",
|
|
|
|
const=logging.ERROR, default=logging.INFO)
|
|
|
|
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
|
|
|
|
action="store_const", dest="loglevel",
|
|
|
|
const=logging.DEBUG, default=logging.INFO)
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
# JID and password options.
|
2014-08-16 20:37:29 +00:00
|
|
|
parser.add_argument("-j", "--jid", dest="jid",
|
|
|
|
help="JID to use")
|
|
|
|
parser.add_argument("-p", "--password", dest="password",
|
|
|
|
help="password to use")
|
2011-03-24 03:00:41 +00:00
|
|
|
|
2014-08-16 20:37:29 +00:00
|
|
|
args = parser.parse_args()
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
# Setup logging.
|
2014-08-16 20:37:29 +00:00
|
|
|
logging.basicConfig(level=args.loglevel,
|
2011-03-24 03:00:41 +00:00
|
|
|
format='%(levelname)-8s %(message)s')
|
|
|
|
|
2014-08-16 20:37:29 +00:00
|
|
|
if args.jid is None:
|
|
|
|
args.jid = input("Username: ")
|
|
|
|
if args.password is None:
|
|
|
|
args.password = getpass("Password: ")
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
# Setup the CommandBot and register plugins. Note that while plugins may
|
|
|
|
# have interdependencies, the order in which you register them does
|
|
|
|
# not matter.
|
2014-08-16 20:37:29 +00:00
|
|
|
xmpp = CommandBot(args.jid, args.password)
|
2011-03-24 03:00:41 +00:00
|
|
|
xmpp.register_plugin('xep_0030') # Service Discovery
|
|
|
|
xmpp.register_plugin('xep_0004') # Data Forms
|
|
|
|
xmpp.register_plugin('xep_0050') # Adhoc Commands
|
2011-08-18 08:06:59 +00:00
|
|
|
xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15})
|
2011-03-24 03:00:41 +00:00
|
|
|
|
|
|
|
# Connect to the XMPP server and start processing XMPP stanzas.
|
2014-08-16 20:37:29 +00:00
|
|
|
xmpp.connect()
|
|
|
|
xmpp.process()
|