241 lines
7.3 KiB
Python
241 lines
7.3 KiB
Python
# use unicode encoding for all literals by default (for python2.x)
|
|
from __future__ import unicode_literals
|
|
|
|
__author__ = "Steffen Vogel"
|
|
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
|
|
__license__ = "GPLv3"
|
|
__maintainer__ = "Steffen Vogel"
|
|
__email__ = "post@steffenvogel.de"
|
|
|
|
"""
|
|
This file is part of transWhat
|
|
|
|
transWhat is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
any later version.
|
|
|
|
transwhat is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
from Spectrum2 import protocol_pb2
|
|
|
|
import logging
|
|
import time
|
|
import utils
|
|
import base64
|
|
|
|
import deferred
|
|
from deferred import call
|
|
|
|
|
|
class Buddy():
|
|
def __init__(self, owner, number, nick, statusMsg, groups, image_hash):
|
|
self.nick = nick
|
|
self.owner = owner
|
|
self.number = "%s" % number
|
|
self.groups = groups
|
|
self.image_hash = image_hash if image_hash is not None else ""
|
|
self.statusMsg = u""
|
|
self.lastseen = 0
|
|
self.presence = 0
|
|
|
|
def update(self, nick, groups, image_hash):
|
|
self.nick = nick
|
|
self.groups = groups
|
|
if image_hash is not None:
|
|
self.image_hash = image_hash
|
|
|
|
def __str__(self):
|
|
# we must return str here
|
|
return str("%s (nick=%s)") % (self.number, self.nick)
|
|
|
|
class BuddyList(dict):
|
|
|
|
def __init__(self, owner, backend, user, session):
|
|
self.owner = owner
|
|
self.backend = backend
|
|
self.session = session
|
|
self.user = user
|
|
self.logger = logging.getLogger(self.__class__.__name__)
|
|
|
|
def _load(self, buddies):
|
|
for buddy in buddies:
|
|
number = buddy.buddyName
|
|
nick = buddy.alias
|
|
statusMsg = buddy.statusMessage
|
|
groups = [g for g in buddy.group]
|
|
image_hash = buddy.iconHash
|
|
self[number] = Buddy(self.owner, number, nick, statusMsg,
|
|
groups, image_hash)
|
|
|
|
self.logger.debug("Update roster")
|
|
|
|
contacts = self.keys()
|
|
contacts.remove('bot')
|
|
|
|
self.session.sendSync(contacts, delta=False, interactive=True,
|
|
success=self.onSync)
|
|
|
|
self.logger.debug("Roster add: %s" % list(contacts))
|
|
|
|
for number in contacts:
|
|
buddy = self[number]
|
|
self.updateSpectrum(buddy)
|
|
|
|
def onSync(self, existing, nonexisting, invalid):
|
|
"""We should only presence subscribe to existing numbers"""
|
|
|
|
for number in existing:
|
|
self.session.subscribePresence(number)
|
|
self.logger.debug("%s is requesting statuses of: %s" % (self.user, existing))
|
|
self.session.requestStatuses(existing, success = self.onStatus)
|
|
|
|
self.logger.debug("Removing nonexisting buddies %s" % nonexisting)
|
|
for number in nonexisting:
|
|
self.remove(number)
|
|
try: del self[number]
|
|
except KeyError: self.logger.warn("non-existing buddy really didn't exist: %s" % number)
|
|
|
|
self.logger.debug("Removing invalid buddies %s" % invalid)
|
|
for number in invalid:
|
|
self.remove(number)
|
|
try: del self[number]
|
|
except KeyError: self.logger.warn("non-existing buddy really didn't exist: %s" % number)
|
|
|
|
|
|
def onStatus(self, contacts):
|
|
self.logger.debug("%s received statuses of: %s" % (self.user, contacts))
|
|
for number, (status, time) in contacts.iteritems():
|
|
try: buddy = self[number]
|
|
except KeyError: self.logger.warn("received status of buddy not in list: %s" % number)
|
|
if status is None:
|
|
buddy.statusMsg = ""
|
|
else:
|
|
buddy.statusMsg = utils.softToUni(status)
|
|
self.updateSpectrum(buddy)
|
|
|
|
|
|
def load(self, buddies):
|
|
if self.session.loggedIn:
|
|
self._load(buddies)
|
|
else:
|
|
self.session.loginQueue.append(lambda: self._load(buddies))
|
|
|
|
def update(self, number, nick, groups, image_hash):
|
|
if number in self:
|
|
buddy = self[number]
|
|
buddy.update(nick, groups, image_hash)
|
|
else:
|
|
buddy = Buddy(self.owner, number, nick, "", groups, image_hash)
|
|
self[number] = buddy
|
|
self.logger.debug("Roster add: %s" % buddy)
|
|
self.session.sendSync([number], delta = True, interactive = True)
|
|
self.session.subscribePresence(number)
|
|
self.session.requestStatuses([number], success = self.onStatus)
|
|
if image_hash == "" or image_hash is None:
|
|
self.requestVCard(number)
|
|
self.updateSpectrum(buddy)
|
|
return buddy
|
|
|
|
def updateSpectrum(self, buddy):
|
|
if buddy.presence == 0:
|
|
status = protocol_pb2.STATUS_NONE
|
|
elif buddy.presence == 'unavailable':
|
|
status = protocol_pb2.STATUS_AWAY
|
|
else:
|
|
status = protocol_pb2.STATUS_ONLINE
|
|
|
|
statusmsg = buddy.statusMsg
|
|
if buddy.lastseen != 0:
|
|
timestamp = time.localtime(buddy.lastseen)
|
|
statusmsg += time.strftime("\n Last seen: %a, %d %b %Y %H:%M:%S", timestamp)
|
|
|
|
iconHash = buddy.image_hash if buddy.image_hash is not None else ""
|
|
|
|
self.logger.debug("Updating buddy %s (%s) in %s, image_hash = %s" %
|
|
(buddy.nick, buddy.number, buddy.groups, iconHash))
|
|
self.logger.debug("Status Message: %s" % statusmsg)
|
|
self.backend.handleBuddyChanged(self.user, buddy.number, buddy.nick,
|
|
buddy.groups, status, statusMessage=statusmsg, iconHash=iconHash)
|
|
|
|
|
|
def remove(self, number):
|
|
try:
|
|
buddy = self[number]
|
|
del self[number]
|
|
self.backend.handleBuddyChanged(self.user, number, "", [],
|
|
protocol_pb2.STATUS_NONE)
|
|
self.backend.handleBuddyRemoved(self.user, number)
|
|
self.session.unsubscribePresence(number)
|
|
# TODO Sync remove
|
|
return buddy
|
|
except KeyError:
|
|
return None
|
|
|
|
def requestVCard(self, buddy, ID=None):
|
|
if "/" in buddy:
|
|
room, nick = buddy.split("/")
|
|
group = self.session.groups[room]
|
|
buddynr = None
|
|
for othernumber, othernick in group.participants.iteritems():
|
|
if othernick == nick:
|
|
buddynr = othernumber
|
|
break
|
|
if buddynr is None:
|
|
return
|
|
else:
|
|
buddynr = buddy
|
|
|
|
|
|
if buddynr == self.user or buddynr == self.user.split('@')[0]:
|
|
buddynr = self.session.legacyName
|
|
|
|
# Get profile picture
|
|
self.logger.debug('Requesting profile picture of %s' % buddynr)
|
|
response = deferred.Deferred()
|
|
# Error probably means image doesn't exist
|
|
error = deferred.Deferred()
|
|
self.session.requestProfilePicture(buddynr, onSuccess=response.run,
|
|
onFailure=error.run)
|
|
response = response.arg(0)
|
|
|
|
pictureData = response.pictureData()
|
|
# Send VCard
|
|
if ID != None:
|
|
call(self.logger.debug, 'Sending VCard (%s) with image id %s: %s' %
|
|
(ID, response.pictureId(), pictureData.then(base64.b64encode)))
|
|
call(self.backend.handleVCard, self.user, ID, buddy, "", "",
|
|
pictureData)
|
|
# If error
|
|
error.when(self.logger.debug, 'Sending VCard (%s) without image' % ID)
|
|
error.when(self.backend.handleVCard, self.user, ID, buddy, "", "", "")
|
|
|
|
# Send image hash
|
|
if not buddynr == self.session.legacyName:
|
|
try:
|
|
obuddy = self[buddynr]
|
|
nick = obuddy.nick
|
|
groups = obuddy.groups
|
|
except KeyError:
|
|
nick = ""
|
|
groups = []
|
|
image_hash = pictureData.then(utils.sha1hash)
|
|
call(self.logger.debug, 'Image hash is %s' % image_hash)
|
|
call(self.update, buddynr, nick, groups, image_hash)
|
|
# No image
|
|
error.when(self.logger.debug, 'No image')
|
|
error.when(self.update, buddynr, nick, groups, '')
|
|
|
|
def refresh(self, number):
|
|
self.session.unsubscribePresence(number)
|
|
self.session.subscribePresence(number)
|
|
self.requestVCard(number)
|
|
self.session.requestStatuses([number], success = self.onStatus)
|