plugin XEP-0054: nick handling + don't remove data on avatar set
- new getNick and setNick allow to manipulate nickname
- setAvatar was removing any data previously in vCard, this is not the case anymore
This commit is contained in:
parent
a4b122094c
commit
7ab6802b2c
1 changed files with 90 additions and 31 deletions
|
|
@ -23,7 +23,7 @@ from sat.core.constants import Const as C
|
|||
from sat.core.log import getLogger
|
||||
log = getLogger(__name__)
|
||||
from twisted.internet import threads, defer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.protocols.jabber import jid, error
|
||||
from twisted.words.xish import domish
|
||||
from twisted.python.failure import Failure
|
||||
|
||||
|
|
@ -187,9 +187,9 @@ class XEP_0054(object):
|
|||
def getCache(self, client, entity_jid, name):
|
||||
"""return cached value for jid
|
||||
|
||||
@param entity_jid: target contact
|
||||
@param name: name of the value ('nick' or 'avatar')
|
||||
@return: wanted value or None"""
|
||||
@param entity_jid(jid.JID): target contact
|
||||
@param name(unicode): name of the value ('nick' or 'avatar')
|
||||
@return(unicode, None): wanted value or None"""
|
||||
entity_jid = self.getBareOrFull(client, entity_jid)
|
||||
try:
|
||||
data = self.host.memory.getEntityData(entity_jid, [name], client.profile)
|
||||
|
|
@ -289,15 +289,10 @@ class XEP_0054(object):
|
|||
|
||||
defer.returnValue(vcard_dict)
|
||||
|
||||
def _vCardCb(self, iq_elt, to_jid, client):
|
||||
def _vCardCb(self, vcard_elt, to_jid, client):
|
||||
"""Called after the first get IQ"""
|
||||
log.debug(_("VCard found"))
|
||||
|
||||
try:
|
||||
vcard_elt = iq_elt.elements(NS_VCARD, "vCard").next()
|
||||
except StopIteration:
|
||||
log.warning(u"Can't find vCard element in answer for jid {jid}", jid=to_jid.full())
|
||||
return
|
||||
iq_elt = vcard_elt.parent
|
||||
try:
|
||||
from_jid = jid.JID(iq_elt["from"])
|
||||
except KeyError:
|
||||
|
|
@ -310,11 +305,13 @@ class XEP_0054(object):
|
|||
log.warning(u"Can't get vCard for {jid}: {failure}".format(jid=to_jid.full, failure=failure_))
|
||||
self.updateCache(client, to_jid, "avatar", None)
|
||||
|
||||
def getCard(self, client, entity_jid):
|
||||
"""Ask server for VCard
|
||||
def _getVcardElt(self, iq_elt):
|
||||
return iq_elt.elements(NS_VCARD, "vCard").next()
|
||||
|
||||
@param entity_jid(jid.JID): jid from which we want the VCard
|
||||
@result: id to retrieve the profile
|
||||
def getCardRaw(self, client, entity_jid):
|
||||
"""get raw vCard XML
|
||||
|
||||
params are as in [getCard]
|
||||
"""
|
||||
entity_jid = self.getBareOrFull(client, entity_jid)
|
||||
log.debug(u"Asking for {}'s VCard".format(entity_jid.full()))
|
||||
|
|
@ -322,7 +319,18 @@ class XEP_0054(object):
|
|||
reg_request["from"] = client.jid.full()
|
||||
reg_request["to"] = entity_jid.full()
|
||||
reg_request.addElement('vCard', NS_VCARD)
|
||||
d = reg_request.send(entity_jid.full()).addCallbacks(self._vCardCb, self._vCardEb, callbackArgs=[entity_jid, client], errbackArgs=[entity_jid, client])
|
||||
d = reg_request.send(entity_jid.full())
|
||||
d.addCallback(self._getVcardElt)
|
||||
return d
|
||||
|
||||
def getCard(self, client, entity_jid):
|
||||
"""Ask server for VCard
|
||||
|
||||
@param entity_jid(jid.JID): jid from which we want the VCard
|
||||
@result: id to retrieve the profile
|
||||
"""
|
||||
d = self.getCardRaw(client, entity_jid)
|
||||
d.addCallbacks(self._vCardCb, self._vCardEb, callbackArgs=[entity_jid, client], errbackArgs=[entity_jid, client])
|
||||
return d
|
||||
|
||||
def _getCardCb(self, dummy, client, entity):
|
||||
|
|
@ -382,7 +390,47 @@ class XEP_0054(object):
|
|||
d.addCallback(lambda dummy: full_path)
|
||||
return d
|
||||
|
||||
def _buildSetAvatar(self, client, vcard_set, file_path):
|
||||
@defer.inlineCallbacks
|
||||
def getNick(self, client, entity):
|
||||
"""get nick from cache, or check vCard
|
||||
|
||||
@param entity(jid.JID): entity to get nick from
|
||||
@return(unicode, None): nick or None if not found
|
||||
"""
|
||||
nick = self.getCache(client, entity, u'nick')
|
||||
if nick is not None:
|
||||
defer.returnValue(nick)
|
||||
yield self.getCard(client, entity)
|
||||
defer.returnValue(self.getCache(client, entity, u'nick'))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def setNick(self, client, nick):
|
||||
"""update our vCard and set a nickname
|
||||
|
||||
@param nick(unicode): new nickname to use
|
||||
"""
|
||||
jid_ = client.jid.userhostJID()
|
||||
try:
|
||||
vcard_elt = yield self.getCardRaw(client, jid_)
|
||||
except error.StanzaError as e:
|
||||
if e.condition == 'item-not-found':
|
||||
vcard_elt = domish.Element((NS_VCARD, 'vCard'))
|
||||
else:
|
||||
raise e
|
||||
try:
|
||||
nickname_elt = next(vcard_elt.elements(NS_VCARD, u'NICKNAME'))
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
vcard_elt.children.remove(nickname_elt)
|
||||
|
||||
nickname_elt = vcard_elt.addElement((NS_VCARD, u'NICKNAME'), content=nick)
|
||||
iq_elt = client.IQ()
|
||||
vcard_elt = iq_elt.addChild(vcard_elt)
|
||||
yield iq_elt.send()
|
||||
self.updateCache(client, jid_, u'nick', unicode(nick))
|
||||
|
||||
def _buildSetAvatar(self, client, vcard_elt, file_path):
|
||||
# XXX: this method is executed in a separate thread
|
||||
try:
|
||||
img = Image.open(file_path)
|
||||
|
|
@ -405,7 +453,6 @@ class XEP_0054(object):
|
|||
img_buf = StringIO()
|
||||
img.save(img_buf, 'PNG')
|
||||
|
||||
vcard_elt = vcard_set.addElement('vCard', NS_VCARD)
|
||||
photo_elt = vcard_elt.addElement('PHOTO')
|
||||
photo_elt.addElement('TYPE', content='image/png')
|
||||
photo_elt.addElement('BINVAL', content=b64encode(img_buf.getvalue()))
|
||||
|
|
@ -417,31 +464,43 @@ class XEP_0054(object):
|
|||
MAX_AGE
|
||||
) as f:
|
||||
f.write(img_buf.getvalue())
|
||||
return vcard_set, image_hash
|
||||
return image_hash
|
||||
|
||||
def _setAvatar(self, file_path, profile_key=C.PROF_KEY_NONE):
|
||||
client = self.host.getClient(profile_key)
|
||||
return self.setAvatar(client, file_path)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def setAvatar(self, client, file_path):
|
||||
"""Set avatar of the profile
|
||||
|
||||
@param file_path: path of the image of the avatar
|
||||
"""
|
||||
#TODO: This is a temporary way of setting the avatar, as other VCard information is not managed.
|
||||
# A proper full VCard management should be done (and more generaly a public/private profile)
|
||||
vcard_set = client.IQ()
|
||||
d = threads.deferToThread(self._buildSetAvatar, client, vcard_set, file_path)
|
||||
try:
|
||||
# we first check if a vcard already exists, to keep data
|
||||
vcard_elt = yield self.getCardRaw(client, client.jid.userhostJID())
|
||||
except error.StanzaError as e:
|
||||
if e.condition == 'item-not-found':
|
||||
vcard_elt = domish.Element((NS_VCARD, 'vCard'))
|
||||
else:
|
||||
raise e
|
||||
else:
|
||||
# the vcard exists, we need to remove PHOTO element as we'll make a new one
|
||||
try:
|
||||
photo_elt = next(vcard_elt.elements(NS_VCARD, u'PHOTO'))
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
vcard_elt.children.remove(photo_elt)
|
||||
|
||||
def elementBuilt(result):
|
||||
"""Called once the image is at the right size/format, and the vcard set element is build"""
|
||||
set_avatar_elt, image_hash = result
|
||||
self.updateCache(client, client.jid.userhostJID(), 'avatar', image_hash)
|
||||
return set_avatar_elt.send().addCallback(lambda ignore: client.presence.available()) # FIXME: should send the current presence, not always "available" !
|
||||
iq_elt = client.IQ()
|
||||
iq_elt.addChild(vcard_elt)
|
||||
image_hash = yield threads.deferToThread(self._buildSetAvatar, client, vcard_elt, file_path)
|
||||
# image is now at the right size/format
|
||||
|
||||
d.addCallback(elementBuilt)
|
||||
|
||||
return d
|
||||
self.updateCache(client, client.jid.userhostJID(), 'avatar', image_hash)
|
||||
yield iq_elt.send()
|
||||
client.presence.available() # FIXME: should send the current presence, not always "available" !
|
||||
|
||||
|
||||
class XEP_0054_handler(XMPPHandler):
|
||||
|
|
|
|||
Loading…
Reference in a new issue