Merge branch 'master' into develop
This commit is contained in:
commit
16ec0f151a
174
examples/set_avatar.py
Normal file
174
examples/set_avatar.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import imghdr
|
||||||
|
import logging
|
||||||
|
import getpass
|
||||||
|
import threading
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
import sleekxmpp
|
||||||
|
from sleekxmpp.exceptions import XMPPError
|
||||||
|
|
||||||
|
|
||||||
|
# Python versions before 3.0 do not use UTF-8 encoding
|
||||||
|
# by default. To ensure that Unicode is handled properly
|
||||||
|
# throughout SleekXMPP, we will set the default encoding
|
||||||
|
# ourselves to UTF-8.
|
||||||
|
if sys.version_info < (3, 0):
|
||||||
|
reload(sys)
|
||||||
|
sys.setdefaultencoding('utf8')
|
||||||
|
else:
|
||||||
|
raw_input = input
|
||||||
|
|
||||||
|
|
||||||
|
class AvatarSetter(sleekxmpp.ClientXMPP):
|
||||||
|
|
||||||
|
"""
|
||||||
|
A basic script for downloading the avatars for a user's contacts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, jid, password, filepath):
|
||||||
|
sleekxmpp.ClientXMPP.__init__(self, jid, password)
|
||||||
|
|
||||||
|
self.add_event_handler("session_start", self.start, threaded=True)
|
||||||
|
|
||||||
|
self.filepath = filepath
|
||||||
|
|
||||||
|
def start(self, event):
|
||||||
|
"""
|
||||||
|
Process the session_start event.
|
||||||
|
|
||||||
|
Typical actions for the session_start event are
|
||||||
|
requesting the roster and broadcasting an initial
|
||||||
|
presence stanza.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
event -- An empty dictionary. The session_start
|
||||||
|
event does not provide any additional
|
||||||
|
data.
|
||||||
|
"""
|
||||||
|
self.send_presence()
|
||||||
|
self.get_roster()
|
||||||
|
|
||||||
|
avatar_file = None
|
||||||
|
try:
|
||||||
|
avatar_file = open(os.path.expanduser(self.filepath))
|
||||||
|
except IOError:
|
||||||
|
print('Could not find file: %s' % self.filepath)
|
||||||
|
return self.disconnect()
|
||||||
|
|
||||||
|
avatar = avatar_file.read()
|
||||||
|
|
||||||
|
avatar_type = 'image/%s' % imghdr.what('', avatar)
|
||||||
|
avatar_id = self['xep_0084'].generate_id(avatar)
|
||||||
|
avatar_bytes = len(avatar)
|
||||||
|
|
||||||
|
avatar_file.close()
|
||||||
|
|
||||||
|
used_xep84 = False
|
||||||
|
try:
|
||||||
|
print('Publish XEP-0084 avatar data')
|
||||||
|
self['xep_0084'].publish_avatar(avatar)
|
||||||
|
used_xep84 = True
|
||||||
|
except XMPPError:
|
||||||
|
print('Could not publish XEP-0084 avatar')
|
||||||
|
|
||||||
|
try:
|
||||||
|
print('Update vCard with avatar')
|
||||||
|
self['xep_0153'].set_avatar(avatar=avatar, mtype=avatar_type)
|
||||||
|
except XMPPError:
|
||||||
|
print('Could not set vCard avatar')
|
||||||
|
|
||||||
|
if used_xep84:
|
||||||
|
try:
|
||||||
|
print('Advertise XEP-0084 avatar metadata')
|
||||||
|
self['xep_0084'].publish_avatar_metadata([
|
||||||
|
{'id': avatar_id,
|
||||||
|
'type': avatar_type,
|
||||||
|
'bytes': avatar_bytes}
|
||||||
|
# We could advertise multiple avatars to provide
|
||||||
|
# options in image type, source (HTTP vs pubsub),
|
||||||
|
# size, etc.
|
||||||
|
# {'id': ....}
|
||||||
|
])
|
||||||
|
except XMPPError:
|
||||||
|
print('Could not publish XEP-0084 metadata')
|
||||||
|
|
||||||
|
print('Wait for presence updates to propagate...')
|
||||||
|
self.schedule('end', 5, self.disconnect, kwargs={'wait': True})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Setup the command line arguments.
|
||||||
|
optp = OptionParser()
|
||||||
|
optp.add_option('-q','--quiet', help='set logging to ERROR',
|
||||||
|
action='store_const',
|
||||||
|
dest='loglevel',
|
||||||
|
const=logging.ERROR,
|
||||||
|
default=logging.ERROR)
|
||||||
|
optp.add_option('-d','--debug', help='set logging to DEBUG',
|
||||||
|
action='store_const',
|
||||||
|
dest='loglevel',
|
||||||
|
const=logging.DEBUG,
|
||||||
|
default=logging.ERROR)
|
||||||
|
optp.add_option('-v','--verbose', help='set logging to COMM',
|
||||||
|
action='store_const',
|
||||||
|
dest='loglevel',
|
||||||
|
const=5,
|
||||||
|
default=logging.ERROR)
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
optp.add_option("-f", "--file", dest="filepath",
|
||||||
|
help="path to the avatar file")
|
||||||
|
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: ")
|
||||||
|
if opts.filepath is None:
|
||||||
|
opts.filepath = raw_input("Avatar file location: ")
|
||||||
|
|
||||||
|
xmpp = AvatarSetter(opts.jid, opts.password, opts.filepath)
|
||||||
|
xmpp.register_plugin('xep_0054')
|
||||||
|
xmpp.register_plugin('xep_0153')
|
||||||
|
xmpp.register_plugin('xep_0084')
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
else:
|
||||||
|
print("Unable to connect.")
|
@ -101,8 +101,10 @@ class APIRegistry(object):
|
|||||||
|
|
||||||
if not jid:
|
if not jid:
|
||||||
jid = self.xmpp.boundjid
|
jid = self.xmpp.boundjid
|
||||||
if jid and not isinstance(jid, JID):
|
elif jid and not isinstance(jid, JID):
|
||||||
jid = JID(jid)
|
jid = JID(jid)
|
||||||
|
elif jid == JID(''):
|
||||||
|
jid = self.xmpp.boundjid
|
||||||
|
|
||||||
if node is None:
|
if node is None:
|
||||||
node = ''
|
node = ''
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from sleekxmpp import JID
|
||||||
from sleekxmpp.stanza import Presence
|
from sleekxmpp.stanza import Presence
|
||||||
from sleekxmpp.xmlstream import register_stanza_plugin
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
from sleekxmpp.xmlstream.matcher import StanzaPath
|
from sleekxmpp.xmlstream.matcher import StanzaPath
|
||||||
@ -30,11 +32,14 @@ class XEP_0153(BasePlugin):
|
|||||||
def plugin_init(self):
|
def plugin_init(self):
|
||||||
self._hashes = {}
|
self._hashes = {}
|
||||||
|
|
||||||
|
self._allow_advertising = threading.Event()
|
||||||
|
|
||||||
register_stanza_plugin(Presence, VCardTempUpdate)
|
register_stanza_plugin(Presence, VCardTempUpdate)
|
||||||
|
|
||||||
self.xmpp.add_filter('out', self._update_presence)
|
self.xmpp.add_filter('out', self._update_presence)
|
||||||
|
|
||||||
self.xmpp.add_event_handler('session_start', self._start)
|
self.xmpp.add_event_handler('session_start', self._start)
|
||||||
|
self.xmpp.add_event_handler('session_end', self._end)
|
||||||
|
|
||||||
self.xmpp.add_event_handler('presence_available', self._recv_presence)
|
self.xmpp.add_event_handler('presence_available', self._recv_presence)
|
||||||
self.xmpp.add_event_handler('presence_dnd', self._recv_presence)
|
self.xmpp.add_event_handler('presence_dnd', self._recv_presence)
|
||||||
@ -44,10 +49,12 @@ class XEP_0153(BasePlugin):
|
|||||||
|
|
||||||
self.api.register(self._set_hash, 'set_hash', default=True)
|
self.api.register(self._set_hash, 'set_hash', default=True)
|
||||||
self.api.register(self._get_hash, 'get_hash', default=True)
|
self.api.register(self._get_hash, 'get_hash', default=True)
|
||||||
|
self.api.register(self._reset_hash, 'reset_hash', default=True)
|
||||||
|
|
||||||
def plugin_end(self):
|
def plugin_end(self):
|
||||||
self.xmpp.del_filter('out', self._update_presence)
|
self.xmpp.del_filter('out', self._update_presence)
|
||||||
self.xmpp.del_event_handler('session_start', self._start)
|
self.xmpp.del_event_handler('session_start', self._start)
|
||||||
|
self.xmpp.del_event_handler('session_end', self._end)
|
||||||
self.xmpp.del_event_handler('presence_available', self._recv_presence)
|
self.xmpp.del_event_handler('presence_available', self._recv_presence)
|
||||||
self.xmpp.del_event_handler('presence_dnd', self._recv_presence)
|
self.xmpp.del_event_handler('presence_dnd', self._recv_presence)
|
||||||
self.xmpp.del_event_handler('presence_xa', self._recv_presence)
|
self.xmpp.del_event_handler('presence_xa', self._recv_presence)
|
||||||
@ -56,15 +63,25 @@ class XEP_0153(BasePlugin):
|
|||||||
|
|
||||||
def set_avatar(self, jid=None, avatar=None, mtype=None, block=True,
|
def set_avatar(self, jid=None, avatar=None, mtype=None, block=True,
|
||||||
timeout=None, callback=None):
|
timeout=None, callback=None):
|
||||||
|
if jid is None:
|
||||||
|
jid = self.xmpp.boundjid.bare
|
||||||
|
|
||||||
vcard = self.xmpp['xep_0054'].get_vcard(jid, cached=True)
|
vcard = self.xmpp['xep_0054'].get_vcard(jid, cached=True)
|
||||||
vcard = vcard['vcard_temp']
|
vcard = vcard['vcard_temp']
|
||||||
vcard['PHOTO']['TYPE'] = mtype
|
vcard['PHOTO']['TYPE'] = mtype
|
||||||
vcard['PHOTO']['BINVAL'] = avatar
|
vcard['PHOTO']['BINVAL'] = avatar
|
||||||
|
|
||||||
self.xmpp['xep_0054'].publish_vcard(jid=jid, vcard=vcard)
|
self.xmpp['xep_0054'].publish_vcard(jid=jid, vcard=vcard)
|
||||||
self._reset_hash(jid)
|
|
||||||
|
self.api['reset_hash'](jid)
|
||||||
|
self.xmpp.roster[jid].send_last_presence()
|
||||||
|
|
||||||
def _start(self, event):
|
def _start(self, event):
|
||||||
self.xmpp['xep_0054'].get_vcard()
|
vcard = self.xmpp['xep_0054'].get_vcard()
|
||||||
|
self._allow_advertising.set()
|
||||||
|
|
||||||
|
def _end(self, event):
|
||||||
|
self._allow_advertising.clear()
|
||||||
|
|
||||||
def _update_presence(self, stanza):
|
def _update_presence(self, stanza):
|
||||||
if not isinstance(stanza, Presence):
|
if not isinstance(stanza, Presence):
|
||||||
@ -74,41 +91,35 @@ class XEP_0153(BasePlugin):
|
|||||||
stanza['vcard_temp_update']['photo'] = current_hash
|
stanza['vcard_temp_update']['photo'] = current_hash
|
||||||
return stanza
|
return stanza
|
||||||
|
|
||||||
def _reset_hash(self, jid=None):
|
def _reset_hash(self, jid, node, ifrom, args):
|
||||||
if jid is None:
|
|
||||||
jid = self.xmpp.boundjid
|
|
||||||
|
|
||||||
own_jid = (jid.bare == self.xmpp.boundjid.bare)
|
own_jid = (jid.bare == self.xmpp.boundjid.bare)
|
||||||
if self.xmpp.is_component:
|
if self.xmpp.is_component:
|
||||||
own_jid = (jid.domain == self.xmpp.boundjid.domain)
|
own_jid = (jid.domain == self.xmpp.boundjid.domain)
|
||||||
|
|
||||||
if jid is not None:
|
|
||||||
jid = jid.bare
|
|
||||||
self.api['set_hash'](jid, args=None)
|
self.api['set_hash'](jid, args=None)
|
||||||
if own_jid:
|
if own_jid:
|
||||||
self.xmpp.roster[jid].send_last_presence()
|
self.xmpp.roster[jid].send_last_presence()
|
||||||
|
|
||||||
iq = self.xmpp['xep_0054'].get_vcard(
|
iq = self.xmpp['xep_0054'].get_vcard(jid=jid.bare, ifrom=ifrom)
|
||||||
jid=jid,
|
|
||||||
ifrom=self.xmpp.boundjid)
|
|
||||||
data = iq['vcard_temp']['PHOTO']['BINVAL']
|
data = iq['vcard_temp']['PHOTO']['BINVAL']
|
||||||
if not data:
|
if not data:
|
||||||
new_hash = ''
|
new_hash = ''
|
||||||
else:
|
else:
|
||||||
new_hash = hashlib.sha1(data).hexdigest()
|
new_hash = hashlib.sha1(data).hexdigest()
|
||||||
|
|
||||||
self.api['set_hash'](jid, args=new_hash)
|
self.api['set_hash'](jid, args=new_hash)
|
||||||
if own_jid:
|
|
||||||
self.xmpp.roster[jid].send_last_presence()
|
|
||||||
|
|
||||||
def _recv_presence(self, pres):
|
def _recv_presence(self, pres):
|
||||||
if not pres.match('presence/vcard_temp_update'):
|
if not pres.match('presence/vcard_temp_update'):
|
||||||
self.api['set_hash'](pres['from'], args=None)
|
self.api['set_hash'](pres['from'], args=None)
|
||||||
return
|
return
|
||||||
|
|
||||||
data = pres['vcard_temp_update']['photo']
|
data = pres['vcard_temp_update']['photo']
|
||||||
if data is None:
|
if data is None:
|
||||||
return
|
return
|
||||||
elif data == '' or data != self.api['get_hash'](pres['to']):
|
elif data == '' or data != self.api['get_hash'](pres['to']):
|
||||||
self._reset_hash(pres['from'])
|
self.api['reset_hash'](pres['from'], ifrom=pres['to'])
|
||||||
self.xmpp.event('vcard_avatar_update', pres)
|
self.xmpp.event('vcard_avatar_update', pres)
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
|
@ -514,8 +514,9 @@ class ElementBase(object):
|
|||||||
:param string attrib: The :attr:`plugin_attrib` value of the
|
:param string attrib: The :attr:`plugin_attrib` value of the
|
||||||
plugin to enable.
|
plugin to enable.
|
||||||
"""
|
"""
|
||||||
if lang is None:
|
default_lang = self.get_lang()
|
||||||
lang = self.get_lang()
|
if not lang:
|
||||||
|
lang = default_lang
|
||||||
|
|
||||||
plugin_class = self.plugin_attrib_map[attrib]
|
plugin_class = self.plugin_attrib_map[attrib]
|
||||||
|
|
||||||
@ -528,7 +529,7 @@ class ElementBase(object):
|
|||||||
existing_xml = self.xml.find(plugin_class.tag_name())
|
existing_xml = self.xml.find(plugin_class.tag_name())
|
||||||
|
|
||||||
if existing_xml is not None:
|
if existing_xml is not None:
|
||||||
if existing_xml.attrib.get('{%s}lang' % XML_NS, '') != lang:
|
if existing_xml.attrib.get('{%s}lang' % XML_NS, default_lang) != lang:
|
||||||
existing_xml = None
|
existing_xml = None
|
||||||
|
|
||||||
plugin = plugin_class(parent=self, xml=existing_xml)
|
plugin = plugin_class(parent=self, xml=existing_xml)
|
||||||
@ -536,7 +537,8 @@ class ElementBase(object):
|
|||||||
if plugin.is_extension:
|
if plugin.is_extension:
|
||||||
self.plugins[(attrib, None)] = plugin
|
self.plugins[(attrib, None)] = plugin
|
||||||
else:
|
else:
|
||||||
plugin['lang'] = lang
|
if lang != default_lang:
|
||||||
|
plugin['lang'] = lang
|
||||||
self.plugins[(attrib, lang)] = plugin
|
self.plugins[(attrib, lang)] = plugin
|
||||||
|
|
||||||
if plugin_class in self.plugin_iterables:
|
if plugin_class in self.plugin_iterables:
|
||||||
@ -576,7 +578,7 @@ class ElementBase(object):
|
|||||||
for plugin, stanza in self.plugins.items():
|
for plugin, stanza in self.plugins.items():
|
||||||
lang = stanza['lang']
|
lang = stanza['lang']
|
||||||
if lang:
|
if lang:
|
||||||
values['%s|%s' % (plugin, lang)] = stanza.values
|
values['%s|%s' % (plugin[0], lang)] = stanza.values
|
||||||
else:
|
else:
|
||||||
values[plugin[0]] = stanza.values
|
values[plugin[0]] = stanza.values
|
||||||
if self.iterables:
|
if self.iterables:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user