summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLance Stout <lancestout@gmail.com>2012-03-10 10:30:32 -0800
committerLance Stout <lancestout@gmail.com>2012-03-10 10:30:32 -0800
commit549a9ab472263cdf1b045caa8791926071547e95 (patch)
treecf3884c2f4499eb0f6ed3384b4249662451e942e
parent09720dcf429713e18014e1656ff859fce2b23237 (diff)
downloadslixmpp-549a9ab472263cdf1b045caa8791926071547e95.tar.gz
slixmpp-549a9ab472263cdf1b045caa8791926071547e95.tar.bz2
slixmpp-549a9ab472263cdf1b045caa8791926071547e95.tar.xz
slixmpp-549a9ab472263cdf1b045caa8791926071547e95.zip
Add support for XEP-0118.
See examples/user_tune.py for a demonstration using the currently playing song in iTunes.
-rw-r--r--examples/user_tune.py137
-rwxr-xr-xsetup.py1
-rw-r--r--sleekxmpp/plugins/xep_0118/__init__.py11
-rw-r--r--sleekxmpp/plugins/xep_0118/stanza.py25
-rw-r--r--sleekxmpp/plugins/xep_0118/user_tune.py98
5 files changed, 272 insertions, 0 deletions
diff --git a/examples/user_tune.py b/examples/user_tune.py
new file mode 100644
index 00000000..09e050f0
--- /dev/null
+++ b/examples/user_tune.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+
+import sys
+import logging
+import getpass
+from optparse import OptionParser
+
+try:
+ from appscript import *
+except ImportError:
+ print('This demo requires the appscript package to interact with iTunes.')
+ sys.exit()
+
+from sleekxmpp import ClientXMPP
+
+
+class TuneBot(ClientXMPP):
+
+ def __init__(self, jid, password):
+ super(TuneBot, self).__init__(jid, password)
+
+ # Check for the current song every 5 seconds.
+ self.schedule('Check Current Tune', 5, self._update_tune, repeat=True)
+
+ self.add_event_handler('session_start', self.start)
+ self.add_event_handler('user_tune_publish', self.user_tune_publish)
+
+ self.register_plugin('xep_0004')
+ self.register_plugin('xep_0030')
+ self.register_plugin('xep_0060')
+ self.register_plugin('xep_0115')
+ self.register_plugin('xep_0118')
+ self.register_plugin('xep_0128')
+ self.register_plugin('xep_0163')
+
+ self.current_tune = None
+
+ def start(self, event):
+ self.send_presence()
+ self.get_roster()
+ self['xep_0115'].update_caps()
+
+ def _update_tune(self):
+ itunes_count = app('System Events').processes[its.name == 'iTunes'].count()
+ if itunes_count > 0:
+ iTunes = app('iTunes')
+ if iTunes.player_state.get() == k.playing:
+ track = iTunes.current_track.get()
+ length = track.time.get()
+ if ':' in length:
+ minutes, secs = map(int, length.split(':'))
+ secs += minutes * 60
+ else:
+ secs = int(length)
+
+ artist = track.artist.get()
+ title = track.name.get()
+ source = track.album.get()
+ rating = track.rating.get() / 10
+
+ tune = (artist, secs, rating, source, title)
+ if tune != self.current_tune:
+ self.current_tune = tune
+
+ # We have a new song playing, so publish it.
+ self['xep_0118'].publish_tune(
+ artist=artist,
+ length=secs,
+ title=title,
+ rating=rating,
+ source=source)
+ else:
+ # No song is playing, clear the user tune.
+ tune = None
+ if tune != self.current_tune:
+ self.current_tune = tune
+ self['xep_0118'].stop()
+
+ def user_tune_publish(self, msg):
+ tune = msg['pubsub_event']['items']['item']['tune']
+ print("%s is listening to: %s" % (msg['from'], tune['title']))
+
+
+if __name__ == '__main__':
+ # Setup the command line arguments.
+ optp = OptionParser()
+
+ # Output verbosity options.
+ optp.add_option('-q', '--quiet', help='set logging to ERROR',
+ action='store_const', dest='loglevel',
+ const=logging.ERROR, default=logging.INFO)
+ optp.add_option('-d', '--debug', help='set logging to DEBUG',
+ action='store_const', dest='loglevel',
+ const=logging.DEBUG, default=logging.INFO)
+ optp.add_option('-v', '--verbose', help='set logging to COMM',
+ action='store_const', dest='loglevel',
+ const=5, default=logging.INFO)
+
+ # JID and password options.
+ optp.add_option("-j", "--jid", dest="jid",
+ help="JID to use")
+ optp.add_option("-p", "--password", dest="password",
+ help="password to use")
+
+ opts, args = optp.parse_args()
+
+ # Setup logging.
+ logging.basicConfig(level=opts.loglevel,
+ format='%(levelname)-8s %(message)s')
+
+ if opts.jid is None:
+ opts.jid = raw_input("Username: ")
+ if opts.password is None:
+ opts.password = getpass.getpass("Password: ")
+
+ xmpp = TuneBot(opts.jid, opts.password)
+
+ # If you are working with an OpenFire server, you may need
+ # to adjust the SSL version used:
+ # xmpp.ssl_version = ssl.PROTOCOL_SSLv3
+
+ # If you want to verify the SSL certificates offered by a server:
+ # xmpp.ca_certs = "path/to/ca/cert"
+
+ # Connect to the XMPP server and start processing XMPP stanzas.
+ if xmpp.connect():
+ # If you do not have the dnspython library installed, you will need
+ # to manually specify the name of the server if it does not match
+ # the one in the JID. For example, to use Google Talk you would
+ # need to use:
+ #
+ # if xmpp.connect(('talk.google.com', 5222)):
+ # ...
+ xmpp.process(block=True)
+ print("Done")
+ else:
+ print("Unable to connect.")
diff --git a/setup.py b/setup.py
index 8c5594e0..b2a6a858 100755
--- a/setup.py
+++ b/setup.py
@@ -71,6 +71,7 @@ packages = [ 'sleekxmpp',
'sleekxmpp/plugins/xep_0086',
'sleekxmpp/plugins/xep_0092',
'sleekxmpp/plugins/xep_0115',
+ 'sleekxmpp/plugins/xep_0118',
'sleekxmpp/plugins/xep_0128',
'sleekxmpp/plugins/xep_0184',
'sleekxmpp/plugins/xep_0199',
diff --git a/sleekxmpp/plugins/xep_0118/__init__.py b/sleekxmpp/plugins/xep_0118/__init__.py
new file mode 100644
index 00000000..f8560211
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0118/__init__.py
@@ -0,0 +1,11 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.plugins.xep_0118 import stanza
+from sleekxmpp.plugins.xep_0118.stanza import UserTune
+from sleekxmpp.plugins.xep_0118.user_tune import xep_0118
diff --git a/sleekxmpp/plugins/xep_0118/stanza.py b/sleekxmpp/plugins/xep_0118/stanza.py
new file mode 100644
index 00000000..80e0358a
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0118/stanza.py
@@ -0,0 +1,25 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.xmlstream import ElementBase, ET
+
+
+class UserTune(ElementBase):
+
+ name = 'tune'
+ namespace = 'http://jabber.org/protocol/tune'
+ plugin_attrib = 'tune'
+ interfaces = set(['artist', 'length', 'rating', 'source',
+ 'title', 'track', 'uri'])
+ sub_interfaces = interfaces
+
+ def set_length(self, value):
+ self._set_sub_text('length', str(value))
+
+ def set_rating(self, value):
+ self._set_sub_text('rating', str(value))
diff --git a/sleekxmpp/plugins/xep_0118/user_tune.py b/sleekxmpp/plugins/xep_0118/user_tune.py
new file mode 100644
index 00000000..5beb7bc4
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0118/user_tune.py
@@ -0,0 +1,98 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import MatchXPath
+from sleekxmpp.plugins.base import base_plugin
+from sleekxmpp.plugins.xep_0118 import stanza, UserTune
+
+
+log = logging.getLogger(__name__)
+
+
+class xep_0118(base_plugin):
+
+ """
+ XEP-0118: User Tune
+ """
+
+ def plugin_init(self):
+ self.xep = '118'
+ self.description = 'User Tune'
+ self.stanza = stanza
+
+ def post_init(self):
+ pubsub_stanza = self.xmpp['xep_0060'].stanza
+ register_stanza_plugin(pubsub_stanza.EventItem, UserTune)
+ self.xmpp['xep_0163'].add_interest(UserTune.namespace)
+ self.xmpp['xep_0060'].map_node_event(UserTune.namespace, 'user_tune')
+
+ def publish_tune(self, artist=None, length=None, rating=None, source=None,
+ title=None, track=None, uri=None, options=None,
+ ifrom=None, block=True, callback=None, timeout=None):
+ """
+ Publish the user's current tune.
+
+ Arguments:
+ artist -- The artist or performer of the song.
+ length -- The length of the song in seconds.
+ rating -- The user's rating of the song (from 1 to 10)
+ source -- The album name, website, or other source of the song.
+ title -- The title of the song.
+ track -- The song's track number, or other unique identifier.
+ uri -- A URL to more information about the song.
+ options -- Optional form of publish options.
+ ifrom -- Specify the sender's JID.
+ block -- Specify if the send call will block until a response
+ is received, or a timeout occurs. Defaults to True.
+ timeout -- The length of time (in seconds) to wait for a response
+ before exiting the send call if blocking is used.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ callback -- Optional reference to a stream handler function. Will
+ be executed when a reply stanza is received.
+ """
+ tune = UserTune()
+ tune['artist'] = artist
+ tune['length'] = length
+ tune['rating'] = rating
+ tune['source'] = source
+ tune['title'] = title
+ tune['track'] = track
+ tune['uri'] = uri
+ self.xmpp['xep_0163'].publish(tune,
+ node=UserTune.namespace,
+ options=options,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)
+
+ def stop(self, ifrom=None, block=True, callback=None, timeout=None):
+ """
+ Clear existing user tune information to stop notifications.
+
+ Arguments:
+ ifrom -- Specify the sender's JID.
+ block -- Specify if the send call will block until a response
+ is received, or a timeout occurs. Defaults to True.
+ timeout -- The length of time (in seconds) to wait for a response
+ before exiting the send call if blocking is used.
+ Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
+ callback -- Optional reference to a stream handler function. Will
+ be executed when a reply stanza is received.
+ """
+ tune = UserTune()
+ self.xmpp['xep_0163'].publish(tune,
+ node=UserTune.namespace,
+ ifrom=ifrom,
+ block=block,
+ callback=callback,
+ timeout=timeout)