Merge pull request #59 from rigid/develop

support yowsup wa16 protocol and axolotol changes
This commit is contained in:
Steffen Vogel 2016-10-07 21:06:48 -04:00 committed by GitHub
commit 9e4ce9032f
13 changed files with 210 additions and 194 deletions

View file

@ -1,3 +1,6 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import protocol_pb2 import protocol_pb2
import socket import socket
import struct import struct
@ -11,7 +14,7 @@ import resource
def WRAP(MESSAGE, TYPE): def WRAP(MESSAGE, TYPE):
wrap = protocol_pb2.WrapperMessage() wrap = protocol_pb2.WrapperMessage()
wrap.type = TYPE wrap.type = TYPE
wrap.payload = MESSAGE wrap.payload = bytes(MESSAGE)
return wrap.SerializeToString() return wrap.SerializeToString()
class SpectrumBackend: class SpectrumBackend:
@ -24,7 +27,7 @@ class SpectrumBackend:
def __init__(self): def __init__(self):
self.m_pingReceived = False self.m_pingReceived = False
self.m_data = "" self.m_data = bytes("")
self.m_init_res = 0 self.m_init_res = 0
self.logger = logging.getLogger(self.__class__.__name__) self.logger = logging.getLogger(self.__class__.__name__)
@ -67,7 +70,7 @@ class SpectrumBackend:
vcard.id = ID vcard.id = ID
vcard.fullname = fullName vcard.fullname = fullName
vcard.nickname = nickname vcard.nickname = nickname
vcard.photo = photo vcard.photo = bytes(photo)
message = WRAP(vcard.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_VCARD) message = WRAP(vcard.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_VCARD)
self.send(message) self.send(message)
@ -220,7 +223,7 @@ class SpectrumBackend:
def handleFTData(self, ftID, data): def handleFTData(self, ftID, data):
d = protocol_pb2.FileTransferData() d = protocol_pb2.FileTransferData()
d.ftid = ftID d.ftid = ftID
d.data = data d.data = bytes(data)
message = WRAP(d.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_FT_DATA); message = WRAP(d.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_FT_DATA);
self.send(message) self.send(message)

View file

@ -1,3 +1,6 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import asyncore, socket import asyncore, socket
import logging import logging
import sys import sys
@ -12,7 +15,7 @@ class IOChannel(asyncore.dispatcher):
self.callback = callback self.callback = callback
self.closeCallback = closeCallback self.closeCallback = closeCallback
self.buffer = "" self.buffer = bytes("")
def sendData(self, data): def sendData(self, data):
self.buffer += data self.buffer += data

3
bot.py
View file

@ -1,3 +1,6 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
__author__ = "Steffen Vogel" __author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel" __copyright__ = "Copyright 2015, Steffen Vogel"
__license__ = "GPLv3" __license__ = "GPLv3"

View file

@ -1,3 +1,6 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
__author__ = "Steffen Vogel" __author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel" __copyright__ = "Copyright 2015, Steffen Vogel"
__license__ = "GPLv3" __license__ = "GPLv3"
@ -36,7 +39,7 @@ class Buddy():
def __init__(self, owner, number, nick, statusMsg, groups, image_hash): def __init__(self, owner, number, nick, statusMsg, groups, image_hash):
self.nick = nick self.nick = nick
self.owner = owner self.owner = owner
self.number = number self.number = "%s" % number
self.groups = groups self.groups = groups
self.image_hash = image_hash if image_hash is not None else "" self.image_hash = image_hash if image_hash is not None else ""
self.statusMsg = u"" self.statusMsg = u""
@ -50,7 +53,8 @@ class Buddy():
self.image_hash = image_hash self.image_hash = image_hash
def __str__(self): def __str__(self):
return "%s (nick=%s)" % (self.number, self.nick) # we must return str here
return str("%s (nick=%s)") % (self.number, self.nick)
class BuddyList(dict): class BuddyList(dict):
@ -65,7 +69,7 @@ class BuddyList(dict):
for buddy in buddies: for buddy in buddies:
number = buddy.buddyName number = buddy.buddyName
nick = buddy.alias nick = buddy.alias
statusMsg = buddy.statusMessage.decode('utf-8') statusMsg = buddy.statusMessage
groups = [g for g in buddy.group] groups = [g for g in buddy.group]
image_hash = buddy.iconHash image_hash = buddy.iconHash
self[number] = Buddy(self.owner, number, nick, statusMsg, self[number] = Buddy(self.owner, number, nick, statusMsg,
@ -79,7 +83,7 @@ class BuddyList(dict):
self.session.sendSync(contacts, delta=False, interactive=True, self.session.sendSync(contacts, delta=False, interactive=True,
success=self.onSync) success=self.onSync)
self.logger.debug("Roster add: %s", str(list(contacts))) self.logger.debug("Roster add: %s" % list(contacts))
for number in contacts: for number in contacts:
buddy = self[number] buddy = self[number]
@ -90,23 +94,27 @@ class BuddyList(dict):
for number in existing: for number in existing:
self.session.subscribePresence(number) self.session.subscribePresence(number)
self.logger.debug("%s is requesting statuses of: %s", self.user, existing) self.logger.debug("%s is requesting statuses of: %s" % (self.user, existing))
self.session.requestStatuses(existing, success = self.onStatus) self.session.requestStatuses(existing, success = self.onStatus)
self.logger.debug("Removing nonexisting buddies %s", nonexisting) self.logger.debug("Removing nonexisting buddies %s" % nonexisting)
for number in nonexisting: for number in nonexisting:
self.remove(number) self.remove(number)
del self[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) self.logger.debug("Removing invalid buddies %s" % invalid)
for number in invalid: for number in invalid:
self.remove(number) self.remove(number)
del self[number] try: del self[number]
except KeyError: self.logger.warn("non-existing buddy really didn't exist: %s" % number)
def onStatus(self, contacts): def onStatus(self, contacts):
self.logger.debug("%s received statuses of: %s", self.user, contacts) self.logger.debug("%s received statuses of: %s" % (self.user, contacts))
for number, (status, time) in contacts.iteritems(): for number, (status, time) in contacts.iteritems():
buddy = self[number] try: buddy = self[number]
except KeyError: self.logger.warn("received status of buddy not in list: %s" % number)
if status is None: if status is None:
buddy.statusMsg = "" buddy.statusMsg = ""
else: else:
@ -127,7 +135,7 @@ class BuddyList(dict):
else: else:
buddy = Buddy(self.owner, number, nick, "", groups, image_hash) buddy = Buddy(self.owner, number, nick, "", groups, image_hash)
self[number] = buddy self[number] = buddy
self.logger.debug("Roster add: %s", buddy) self.logger.debug("Roster add: %s" % buddy)
self.session.sendSync([number], delta = True, interactive = True) self.session.sendSync([number], delta = True, interactive = True)
self.session.subscribePresence(number) self.session.subscribePresence(number)
self.session.requestStatuses([number], success = self.onStatus) self.session.requestStatuses([number], success = self.onStatus)
@ -151,9 +159,9 @@ class BuddyList(dict):
iconHash = buddy.image_hash if buddy.image_hash is not None else "" iconHash = buddy.image_hash if buddy.image_hash is not None else ""
self.logger.debug("Updating buddy %s (%s) in %s, image_hash = %s", self.logger.debug("Updating buddy %s (%s) in %s, image_hash = %s" %
buddy.nick, buddy.number, buddy.groups, iconHash) (buddy.nick, buddy.number, buddy.groups, iconHash))
self.logger.debug("Status Message: %s", statusmsg) self.logger.debug("Status Message: %s" % statusmsg)
self.backend.handleBuddyChanged(self.user, buddy.number, buddy.nick, self.backend.handleBuddyChanged(self.user, buddy.number, buddy.nick,
buddy.groups, status, statusMessage=statusmsg, iconHash=iconHash) buddy.groups, status, statusMessage=statusmsg, iconHash=iconHash)
@ -190,7 +198,7 @@ class BuddyList(dict):
buddynr = self.session.legacyName buddynr = self.session.legacyName
# Get profile picture # Get profile picture
self.logger.debug('Requesting profile picture of %s', buddynr) self.logger.debug('Requesting profile picture of %s' % buddynr)
response = deferred.Deferred() response = deferred.Deferred()
# Error probably means image doesn't exist # Error probably means image doesn't exist
error = deferred.Deferred() error = deferred.Deferred()
@ -201,12 +209,12 @@ class BuddyList(dict):
pictureData = response.pictureData() pictureData = response.pictureData()
# Send VCard # Send VCard
if ID != None: if ID != None:
call(self.logger.debug, 'Sending VCard (%s) with image id %s: %s', call(self.logger.debug, 'Sending VCard (%s) with image id %s: %s' %
ID, response.pictureId(), pictureData.then(base64.b64encode)) (ID, response.pictureId(), pictureData.then(base64.b64encode)))
call(self.backend.handleVCard, self.user, ID, buddy, "", "", call(self.backend.handleVCard, self.user, ID, buddy, "", "",
pictureData) pictureData)
# If error # If error
error.when(self.logger.debug, 'Sending VCard (%s) without image', ID) error.when(self.logger.debug, 'Sending VCard (%s) without image' % ID)
error.when(self.backend.handleVCard, self.user, ID, buddy, "", "", "") error.when(self.backend.handleVCard, self.user, ID, buddy, "", "", "")
# Send image hash # Send image hash
@ -219,7 +227,7 @@ class BuddyList(dict):
nick = "" nick = ""
groups = [] groups = []
image_hash = pictureData.then(utils.sha1hash) image_hash = pictureData.then(utils.sha1hash)
call(self.logger.debug, 'Image hash is %s', image_hash) call(self.logger.debug, 'Image hash is %s' % image_hash)
call(self.update, buddynr, nick, groups, image_hash) call(self.update, buddynr, nick, groups, image_hash)
# No image # No image
error.when(self.logger.debug, 'No image') error.when(self.logger.debug, 'No image')

View file

@ -1,3 +1,5 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
from functools import partial from functools import partial
class Deferred(object): class Deferred(object):

View file

@ -1,3 +1,7 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
__author__ = "Steffen Vogel" __author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel" __copyright__ = "Copyright 2015, Steffen Vogel"
__license__ = "GPLv3" __license__ = "GPLv3"

View file

@ -1,3 +1,5 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
from Spectrum2 import protocol_pb2 from Spectrum2 import protocol_pb2
from yowsupwrapper import YowsupApp from yowsupwrapper import YowsupApp
@ -34,13 +36,13 @@ class RegisterSession(YowsupApp):
self.backend.handleMessage(self.user, 'bot', self.backend.handleMessage(self.user, 'bot',
'Country code must be a number') 'Country code must be a number')
else: # Succeded in decoding country code else: # Succeded in decoding country code
country_code = str(country_code) country_code = "%s" % country_code
if country_code != self.number[:len(country_code)]: if country_code != self.number[:len(country_code)]:
self.backend.handleMessage(self.user, self.backend.handleMessage(self.user,
'bot', 'Number does not start with provided country code') 'bot', 'Number does not start with provided country code')
else: else:
self.backend.handleMessage(self.user, 'bot', 'Requesting sms code') self.backend.handleMessage(self.user, 'bot', 'Requesting sms code')
self.logger.debug('Requesting SMS code for %s', self.user) self.logger.debug('Requesting SMS code for %s' % self.user)
self.countryCode = country_code self.countryCode = country_code
self._requestSMSCodeNonBlock() self._requestSMSCodeNonBlock()
elif buddy == 'bot' and self.state == self.WANT_SMS: elif buddy == 'bot' and self.state == self.WANT_SMS:
@ -51,7 +53,7 @@ class RegisterSession(YowsupApp):
self.backend.handleMessage(self.user, self.backend.handleMessage(self.user,
'bot', 'Invalid code. Must be of the form XXX-XXX.') 'bot', 'Invalid code. Must be of the form XXX-XXX.')
else: else:
self.logger.warn('Unauthorised user (%s) attempting to send messages', self.logger.warn('Unauthorised user (%s) attempting to send messages' %
self.user) self.user)
self.backend.handleMessage(self.user, buddy, self.backend.handleMessage(self.user, buddy,
'You are not logged in yet. You can only send messages to bot.') 'You are not logged in yet. You can only send messages to bot.')
@ -104,7 +106,7 @@ class RegisterSession(YowsupApp):
for k, v in result.items(): for k, v in result.items():
if v is None: if v is None:
continue continue
out.append("%s: %s" %(k, v.encode("utf-8") if type(v) is unistr else v)) out.append("%s: %s" % (k, v))
return "\n".join(out) return "\n".join(out)

View file

@ -1,3 +1,6 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
__author__ = "Steffen Vogel" __author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel" __copyright__ = "Copyright 2015, Steffen Vogel"
__license__ = "GPLv3" __license__ = "GPLv3"
@ -54,12 +57,12 @@ class MsgIDs:
class Session(YowsupApp): class Session(YowsupApp):
broadcast_prefix = u'\U0001F4E2 ' broadcast_prefix = '\U0001F4E2 '
def __init__(self, backend, user, legacyName, extra): def __init__(self, backend, user, legacyName, extra):
super(Session, self).__init__() super(Session, self).__init__()
self.logger = logging.getLogger(self.__class__.__name__) self.logger = logging.getLogger(self.__class__.__name__)
self.logger.info("Created: %s", legacyName) self.logger.info("Created: %s" % legacyName)
self.backend = backend self.backend = backend
self.user = user self.user = user
@ -99,12 +102,12 @@ class Session(YowsupApp):
self.logout() self.logout()
def logout(self): def logout(self):
self.logger.info("%s logged out", self.user) self.logger.info("%s logged out" % self.user)
super(Session, self).logout() super(Session, self).logout()
self.loggedIn = False self.loggedIn = False
def login(self, password): def login(self, password):
self.logger.info("%s attempting login", self.user) self.logger.info("%s attempting login" % self.user)
self.password = password self.password = password
self.shouldBeConncted = True self.shouldBeConncted = True
super(Session, self).login(self.legacyName, self.password) super(Session, self).login(self.legacyName, self.password)
@ -126,14 +129,14 @@ class Session(YowsupApp):
rooms.append([self._shortenGroupId(room), group.subject]) rooms.append([self._shortenGroupId(room), group.subject])
text.append(self._shortenGroupId(room) + '@' + self.backend.spectrum_jid + ' :' + group.subject) text.append(self._shortenGroupId(room) + '@' + self.backend.spectrum_jid + ' :' + group.subject)
self.logger.debug("Got rooms: %s", rooms) self.logger.debug("Got rooms: %s" % rooms)
self.backend.handleRoomList(rooms) self.backend.handleRoomList(rooms)
message = "Note, you are a participant of the following groups:\n" +\ message = "Note, you are a participant of the following groups:\n" + \
'\n'.join(text) + '\nIf you do not join them you will lose messages' "\n".join(text) + "\nIf you do not join them you will lose messages"
#self.bot.send(message) #self.bot.send(message)
def _updateGroups(self, response, request): def _updateGroups(self, response, request):
self.logger.debug('Received groups list %s', response) self.logger.debug('Received groups list %s' % response)
groups = response.getGroups() groups = response.getGroups()
for group in groups: for group in groups:
room = group.getId() room = group.getId()
@ -159,8 +162,8 @@ class Session(YowsupApp):
msg = self.groupOfflineQueue[room].pop(0) msg = self.groupOfflineQueue[room].pop(0)
self.backend.handleMessage(self.user, room, msg[1], self.backend.handleMessage(self.user, room, msg[1],
msg[0], "", msg[2]) msg[0], "", msg[2])
self.logger.debug("Send queued group message to: %s %s %s", self.logger.debug("Send queued group message to: %s %s %s" %
msg[0],msg[1], msg[2]) (msg[0],msg[1], msg[2]))
self.gotGroupList = True self.gotGroupList = True
for room, nick in self.joinRoomQueue: for room, nick in self.joinRoomQueue:
self.joinRoom(room, nick) self.joinRoom(room, nick)
@ -173,8 +176,8 @@ class Session(YowsupApp):
return return
room = self._lengthenGroupId(room) room = self._lengthenGroupId(room)
if room in self.groups: if room in self.groups:
self.logger.info("Joining room: %s room=%s, nick=%s", self.logger.info("Joining room: %s room=%s, nick=%s" %
self.legacyName, room, nick) (self.legacyName, room, nick))
group = self.groups[room] group = self.groups[room]
group.joined = True group.joined = True
@ -188,24 +191,24 @@ class Session(YowsupApp):
group.sendParticipantsToSpectrum(self.legacyName) group.sendParticipantsToSpectrum(self.legacyName)
self.backend.handleSubject(self.user, self._shortenGroupId(room), self.backend.handleSubject(self.user, self._shortenGroupId(room),
group.subject, ownerNick) group.subject, ownerNick)
self.logger.debug("Room subject: room=%s, subject=%s", self.logger.debug("Room subject: room=%s, subject=%s" %
room, group.subject) (room, group.subject))
self.backend.handleRoomNicknameChanged( self.backend.handleRoomNicknameChanged(
self.user, self._shortenGroupId(room), group.subject self.user, self._shortenGroupId(room), group.subject
) )
else: else:
self.logger.warn("Room doesn't exist: %s", room) self.logger.warn("Room doesn't exist: %s" % room)
def leaveRoom(self, room): def leaveRoom(self, room):
if room in self.groups: if room in self.groups:
self.logger.info("Leaving room: %s room=%s", self.legacyName, room) self.logger.info("Leaving room: %s room=%s" % (self.legacyName, room))
group = self.groups[room] group = self.groups[room]
group.joined = False group.joined = False
else: else:
self.logger.warn("Room doesn't exist: %s. Unable to leave.", room) self.logger.warn("Room doesn't exist: %s. Unable to leave." % room)
def _lastSeen(self, number, seconds): def _lastSeen(self, number, seconds):
self.logger.debug("Last seen %s at %s seconds" % (number, str(seconds))) self.logger.debug("Last seen %s at %s seconds" % (number, seconds))
if seconds < 60: if seconds < 60:
self.onPresenceAvailable(number) self.onPresenceAvailable(number)
else: else:
@ -215,12 +218,12 @@ class Session(YowsupApp):
if _from.split('@')[0] == buddy: if _from.split('@')[0] == buddy:
self.sendReceipt(_id, _from, 'read', participant) self.sendReceipt(_id, _from, 'read', participant)
self.recvMsgIDs.remove((_id, _from, participant)) self.recvMsgIDs.remove((_id, _from, participant))
self.logger.debug("Send read receipt to %s (ID: %s)", _from, _id) self.logger.debug("Send read receipt to %s (ID: %s)" % (_from, _id))
# Called by superclass # Called by superclass
def onAuthSuccess(self, status, kind, creation, def onAuthSuccess(self, status, kind, creation,
expiration, props, nonce, t): expiration, props, nonce, t):
self.logger.info("Auth success: %s", self.user) self.logger.info("Auth success: %s" % self.user)
self.backend.handleConnected(self.user) self.backend.handleConnected(self.user)
self.backend.handleBuddyChanged(self.user, "bot", self.bot.name, self.backend.handleBuddyChanged(self.user, "bot", self.bot.name,
@ -249,7 +252,7 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onAuthFailed(self, reason): def onAuthFailed(self, reason):
self.logger.info("Auth failed: %s (%s)", self.user, reason) self.logger.info("Auth failed: %s (%s)" % (self.user, reason))
self.backend.handleDisconnected(self.user, 0, reason) self.backend.handleDisconnected(self.user, 0, reason)
self.password = None self.password = None
self.loggedIn = False self.loggedIn = False
@ -261,9 +264,8 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onReceipt(self, _id, _from, timestamp, type, participant, offline, items): def onReceipt(self, _id, _from, timestamp, type, participant, offline, items):
self.logger.debug("received receipt, sending ack: " + self.logger.debug("received receipt, sending ack: %s" %
' '.join(map(str, [_id, _from, timestamp, [ _id, _from, timestamp, type, participant, offline, items ]
type, participant, offline, items]))
) )
try: try:
number = _from.split('@')[0] number = _from.split('@')[0]
@ -272,29 +274,26 @@ class Session(YowsupApp):
if self.msgIDs[_id].cnt == 2: if self.msgIDs[_id].cnt == 2:
del self.msgIDs[_id] del self.msgIDs[_id]
except KeyError: except KeyError:
self.logger.error("Message %s not found. Unable to send ack", _id) self.logger.error("Message %s not found. Unable to send ack" % _id)
# Called by superclass # Called by superclass
def onAck(self, _id, _class, _from, timestamp): def onAck(self, _id, _class, _from, timestamp):
self.logger.debug('received ack ' + self.logger.debug('received ack: %s' % [ _id, _class, _from, timestamp ])
' '.join(map(str, [_id, _class, _from,timestamp,]))
)
# Called by superclass # Called by superclass
def onTextMessage(self, _id, _from, to, notify, timestamp, participant, def onTextMessage(self, _id, _from, to, notify, timestamp, participant,
offline, retry, body): offline, retry, body):
self.logger.debug('received TextMessage' +
' '.join(map(str, [ self.logger.debug('received TextMessage: %s' %
_id, _from, to, notify, timestamp, [_id, _from, to, notify, timestamp,
participant, offline, retry, body participant, offline, retry, body]
]))
) )
buddy = _from.split('@')[0] buddy = _from.split('@')[0]
messageContent = utils.softToUni(body) messageContent = utils.softToUni(body)
self.sendReceipt(_id, _from, None, participant) self.sendReceipt(_id, _from, None, participant)
self.recvMsgIDs.append((_id, _from, participant)) self.recvMsgIDs.append((_id, _from, participant))
self.logger.info("Message received from %s to %s: %s (at ts=%s)", self.logger.info("Message received from %s to %s: %s (at ts=%s)" %
buddy, self.legacyName, messageContent, timestamp) (buddy, self.legacyName, messageContent, timestamp))
if participant is not None: # Group message or broadcast if participant is not None: # Group message or broadcast
partname = participant.split('@')[0] partname = participant.split('@')[0]
if _from.split('@')[1] == 'broadcast': # Broadcast message if _from.split('@')[1] == 'broadcast': # Broadcast message
@ -310,7 +309,7 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onImage(self, image): def onImage(self, image):
self.logger.debug('Received image message %s', str(image)) self.logger.debug('Received image message: %s' % image)
buddy = image._from.split('@')[0] buddy = image._from.split('@')[0]
participant = image.participant participant = image.participant
if image.caption is None: if image.caption is None:
@ -333,7 +332,7 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onAudio(self, audio): def onAudio(self, audio):
self.logger.debug('Received audio message %s', str(audio)) self.logger.debug('Received audio message: %s' % audio)
buddy = audio._from.split('@')[0] buddy = audio._from.split('@')[0]
participant = audio.participant participant = audio.participant
message = audio.url message = audio.url
@ -352,7 +351,7 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onVideo(self, video): def onVideo(self, video):
self.logger.debug('Received video message %s', str(video)) self.logger.debug('Received video message: %s' % video)
buddy = video._from.split('@')[0] buddy = video._from.split('@')[0]
participant = video.participant participant = video.participant
@ -378,8 +377,8 @@ class Session(YowsupApp):
participant = location.participant participant = location.participant
latlong = 'geo:' + latitude + ',' + longitude latlong = 'geo:' + latitude + ',' + longitude
self.logger.debug("Location received from %s: %s, %s", self.logger.debug("Location received from %s: %s, %s" %
buddy, latitude, longitude) (buddy, latitude, longitude))
if participant is not None: # Group message if participant is not None: # Group message
@ -404,10 +403,8 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onVCard(self, _id, _from, name, card_data, to, notify, timestamp, participant): def onVCard(self, _id, _from, name, card_data, to, notify, timestamp, participant):
self.logger.debug('received VCard' + self.logger.debug('received VCard: %s' %
' '.join(map(str, [ [ _id, _from, name, card_data, to, notify, timestamp, participant ]
_id, _from, name, card_data, to, notify, timestamp, participant
]))
) )
message = "Received VCard (not implemented yet)" message = "Received VCard (not implemented yet)"
buddy = _from.split("@")[0] buddy = _from.split("@")[0]
@ -428,14 +425,14 @@ class Session(YowsupApp):
def transferFile(self, buddy, name, data): def transferFile(self, buddy, name, data):
# Not working # Not working
self.logger.debug('transfering file %s', name) self.logger.debug('transfering file: %s' % name)
self.backend.handleFTStart(self.user, buddy, name, len(data)) self.backend.handleFTStart(self.user, buddy, name, len(data))
self.backend.handleFTData(0, data) self.backend.handleFTData(0, data)
self.backend.handleFTFinish(self.user, buddy, name, len(data), 0) self.backend.handleFTFinish(self.user, buddy, name, len(data), 0)
# Called by superclass # Called by superclass
def onContactTyping(self, buddy): def onContactTyping(self, buddy):
self.logger.info("Started typing: %s", buddy) self.logger.info("Started typing: %s" % buddy)
if buddy != 'bot': if buddy != 'bot':
self.sendPresence(True) self.sendPresence(True)
self.backend.handleBuddyTyping(self.user, buddy) self.backend.handleBuddyTyping(self.user, buddy)
@ -445,7 +442,7 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onContactPaused(self, buddy): def onContactPaused(self, buddy):
self.logger.info("Paused typing: %s", buddy) self.logger.info("Paused typing: %s" % buddy)
if buddy != 'bot': if buddy != 'bot':
self.backend.handleBuddyTyped(self.user, buddy) self.backend.handleBuddyTyped(self.user, buddy)
self.timer = Timer(3, self.backend.handleBuddyStoppedTyping, self.timer = Timer(3, self.backend.handleBuddyStoppedTyping,
@ -453,20 +450,20 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onAddedToGroup(self, group): def onAddedToGroup(self, group):
self.logger.debug("Added to group: %s", group) self.logger.debug("Added to group: %s" % group)
room = group.getGroupId() room = group.getGroupId()
owner = group.getCreatorJid(full = False) owner = group.getCreatorJid(full = False)
subjectOwner = group.getSubjectOwnerJid(full = False) subjectOwner = group.getSubjectOwnerJid(full = False)
subject = utils.softToUni(group.getSubject()) subject = utils.softToUni(group.getSubject())
self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user)
self.groups[room].addParticipants(group.getParticipants, self.buddies, self.legacyName) self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName)
self.bot.send("You have been added to group: %s@%s (%s)" self.bot.send("You have been added to group: %s@%s (%s)"
% (self._shortenGroupId(room), subject, self.backend.spectrum_jid)) % (self._shortenGroupId(room), subject, self.backend.spectrum_jid))
# Called by superclass # Called by superclass
def onParticipantsAddedToGroup(self, group): def onParticipantsAddedToGroup(self, group):
self.logger.debug("Participants added to group: %s", group) self.logger.debug("Participants added to group: %s" % group)
room = group.getGroupId().split('@')[0] room = group.getGroupId().split('@')[0]
self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName) self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName)
self.groups[room].sendParticipantsToSpectrum(self.legacyName) self.groups[room].sendParticipantsToSpectrum(self.legacyName)
@ -474,12 +471,13 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onSubjectChanged(self, room, subject, subjectOwner, timestamp): def onSubjectChanged(self, room, subject, subjectOwner, timestamp):
self.logger.debug( self.logger.debug(
"onSubjectChange(rrom=%s, subject=%s, subjectOwner=%s, timestamp=%s)", "onSubjectChange(rrom=%s, subject=%s, subjectOwner=%s, timestamp=%s)" %
room, subject, subjectOwner, timestamp) (room, subject, subjectOwner, timestamp)
)
try: try:
group = self.groups[room] group = self.groups[room]
except KeyError: except KeyError:
self.logger.error("Subject of non-existant group (%s) changed", group) self.logger.error("Subject of non-existant group (%s) changed" % group)
else: else:
group.subject = subject group.subject = subject
group.subjectOwner = subjectOwner group.subjectOwner = subjectOwner
@ -491,46 +489,46 @@ class Session(YowsupApp):
# Called by superclass # Called by superclass
def onParticipantsRemovedFromGroup(self, room, participants): def onParticipantsRemovedFromGroup(self, room, participants):
self.logger.debug("Participants removed from group: %s, %s", self.logger.debug("Participants removed from group: %s, %s" %
room, participants) (room, participants))
self.groups[room].removeParticipants(participants) self.groups[room].removeParticipants(participants)
# Called by superclass # Called by superclass
def onContactStatusChanged(self, number, status): def onContactStatusChanged(self, number, status):
self.logger.debug("%s changed their status to %s", number, status) self.logger.debug("%s changed their status to %s" % (number, status))
try: try:
buddy = self.buddies[number] buddy = self.buddies[number]
buddy.statusMsg = status buddy.statusMsg = status
self.buddies.updateSpectrum(buddy) self.buddies.updateSpectrum(buddy)
except KeyError: except KeyError:
self.logger.debug("%s not in buddy list", number) self.logger.debug("%s not in buddy list" % number)
# Called by superclass # Called by superclass
def onContactPictureChanged(self, number): def onContactPictureChanged(self, number):
self.logger.debug("%s changed their profile picture", number) self.logger.debug("%s changed their profile picture" % number)
self.buddies.requestVCard(number) self.buddies.requestVCard(number)
# Called by superclass # Called by superclass
def onContactAdded(self, number, nick): def onContactAdded(self, number, nick):
self.logger.debug("Adding new contact %s (%s)", nick, number) self.logger.debug("Adding new contact %s (%s)" % (nick, number))
self.updateBuddy(number, nick, []) self.updateBuddy(number, nick, [])
# Called by superclass # Called by superclass
def onContactRemoved(self, number): def onContactRemoved(self, number):
self.logger.debug("Removing contact %s", number) self.logger.debug("Removing contact %s" % number)
self.removeBuddy(number) self.removeBuddy(number)
def onContactUpdated(self, oldnumber, newnumber): def onContactUpdated(self, oldnumber, newnumber):
self.logger.debug("Contact has changed number from %s to %s", self.logger.debug("Contact has changed number from %s to %s" %
oldnumber, newnumber) (oldnumber, newnumber))
if newnumber in self.buddies: if newnumber in self.buddies:
self.logger.warn("Contact %s exists, just updating", newnumber) self.logger.warn("Contact %s exists, just updating" % newnumber)
self.buddies.refresh(newnumber) self.buddies.refresh(newnumber)
try: try:
buddy = self.buddies[oldnumber] buddy = self.buddies[oldnumber]
except KeyError: except KeyError:
self.logger.warn("Old contact (%s) not found. Adding new contact (%s)", self.logger.warn("Old contact (%s) not found. Adding new contact (%s)" %
oldnumber, newnumber) (oldnumber, newnumber))
nick = "" nick = ""
else: else:
self.removeBuddy(buddy.number) self.removeBuddy(buddy.number)
@ -538,17 +536,17 @@ class Session(YowsupApp):
self.updateBuddy(newnumber, nick, []) self.updateBuddy(newnumber, nick, [])
def onPresenceReceived(self, _type, name, jid, lastseen): def onPresenceReceived(self, _type, name, jid, lastseen):
self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) self.logger.info("Presence received: %s %s %s %s" % (_type, name, jid, lastseen))
buddy = jid.split("@")[0] buddy = jid.split("@")[0]
try: try:
buddy = self.buddies[buddy] buddy = self.buddies[buddy]
except KeyError: except KeyError:
# Sometimes whatsapp send our own presence # Sometimes whatsapp send our own presence
if buddy != self.legacyName: if buddy != self.legacyName:
self.logger.error("Buddy not found: %s", buddy) self.logger.error("Buddy not found: %s" % buddy)
return return
if (lastseen == str(buddy.lastseen)) and (_type == buddy.presence): if (lastseen == buddy.lastseen) and (_type == buddy.presence):
return return
if ((lastseen != "deny") and (lastseen != None) and (lastseen != "none")): if ((lastseen != "deny") and (lastseen != None) and (lastseen != "none")):
@ -566,17 +564,17 @@ class Session(YowsupApp):
def onPresenceAvailable(self, buddy): def onPresenceAvailable(self, buddy):
self.logger.info("Is available: %s", buddy) self.logger.info("Is available: %s" % buddy)
self.buddies.updateSpectrum(buddy) self.buddies.updateSpectrum(buddy)
def onPresenceUnavailable(self, buddy): def onPresenceUnavailable(self, buddy):
self.logger.info("Is unavailable: %s", buddy) self.logger.info("Is unavailable: %s" % buddy)
self.buddies.updateSpectrum(buddy) self.buddies.updateSpectrum(buddy)
# spectrum RequestMethods # spectrum RequestMethods
def sendTypingStarted(self, buddy): def sendTypingStarted(self, buddy):
if buddy != "bot": if buddy != "bot":
self.logger.info("Started typing: %s to %s", self.legacyName, buddy) self.logger.info("Started typing: %s to %s" % (self.legacyName, buddy))
self.sendTyping(buddy, True) self.sendTyping(buddy, True)
self.sendReadReceipts(buddy) self.sendReadReceipts(buddy)
# If he is typing he is present # If he is typing he is present
@ -586,7 +584,7 @@ class Session(YowsupApp):
def sendTypingStopped(self, buddy): def sendTypingStopped(self, buddy):
if buddy != "bot": if buddy != "bot":
self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy) self.logger.info("Stopped typing: %s to %s" % (self.legacyName, buddy))
self.sendTyping(buddy, False) self.sendTyping(buddy, False)
self.sendReadReceipts(buddy) self.sendReadReceipts(buddy)
@ -602,7 +600,7 @@ class Session(YowsupApp):
# Success # Success
path = success.arg(0) path = success.arg(0)
call(self.logger.info, "Success: Image downloaded to %s", path) call(self.logger.info, "Success: Image downloaded to %s" % path)
pathWithExt = path.then(lambda p: p + "." + imgType) pathWithExt = path.then(lambda p: p + "." + imgType)
call(os.rename, path, pathWithExt) call(os.rename, path, pathWithExt)
pathJpg = path.then(lambda p: p + ".jpg") pathJpg = path.then(lambda p: p + ".jpg")
@ -610,7 +608,7 @@ class Session(YowsupApp):
im = call(Image.open, pathWithExt) im = call(Image.open, pathWithExt)
call(im.save, pathJpg) call(im.save, pathJpg)
call(os.remove, pathWithExt) call(os.remove, pathWithExt)
call(self.logger.info, "Sending image to %s", to) call(self.logger.info, "Sending image to %s" % to)
waId = deferred.Deferred() waId = deferred.Deferred()
call(super(Session, self).sendImage, to, pathJpg, onSuccess = waId.run) call(super(Session, self).sendImage, to, pathJpg, onSuccess = waId.run)
call(self.setWaId, ID, waId) call(self.setWaId, ID, waId)
@ -626,10 +624,9 @@ class Session(YowsupApp):
self.msgIDs[waId] = MsgIDs(XmppId, waId) self.msgIDs[waId] = MsgIDs(XmppId, waId)
def sendMessageToWA(self, sender, message, ID, xhtml=""): def sendMessageToWA(self, sender, message, ID, xhtml=""):
self.logger.info("Message sent from %s to %s: %s (xhtml=%s)", self.logger.info("Message sent from %s to %s: %s (xhtml=%s)" %
self.legacyName, sender, message, xhtml) (self.legacyName, sender, message, xhtml))
message = message.encode("utf-8")
self.sendReadReceipts(sender) self.sendReadReceipts(sender)
if sender == "bot": if sender == "bot":
@ -644,27 +641,27 @@ class Session(YowsupApp):
number = othernumber number = othernumber
break break
if number is not None: if number is not None:
self.logger.debug("Private message sent from %s to %s", self.legacyName, number) self.logger.debug("Private message sent from %s to %s" % (self.legacyName, number))
waId = self.sendTextMessage(number + '@s.whatsapp.net', message) waId = self.sendTextMessage(number + '@s.whatsapp.net', message)
self.msgIDs[waId] = MsgIDs( ID, waId) self.msgIDs[waId] = MsgIDs( ID, waId)
else: else:
self.logger.error("Attempted to send private message to non-existent user") self.logger.error("Attempted to send private message to non-existent user")
self.logger.debug("%s to %s in %s", self.legacyName, nick, room) self.logger.debug("%s to %s in %s" % (self.legacyName, nick, room))
else: else:
room = sender room = sender
if message[0] == '\\' and message[:1] != '\\\\': if message[0] == '\\' and message[:1] != '\\\\':
self.logger.debug("Executing command %s in %s", message, room) self.logger.debug("Executing command %s in %s" % (message, room))
self.executeCommand(message, room) self.executeCommand(message, room)
else: else:
try: try:
group = self.groups[self._lengthenGroupId(room)] group = self.groups[self._lengthenGroupId(room)]
self.logger.debug("Group Message from %s to %s Groups: %s", self.logger.debug("Group Message from %s to %s Groups: %s" %
group.nick , group , self.groups) (group.nick , group , self.groups))
self.backend.handleMessage( self.backend.handleMessage(
self.user, room, message.decode('utf-8'), group.nick, xhtml=xhtml self.user, room, message, group.nick, xhtml=xhtml
) )
except KeyError: except KeyError:
self.logger.error('Group not found: %s', room) self.logger.error('Group not found: %s' % room)
if (".jpg" in message.lower()) or (".webp" in message.lower()): if (".jpg" in message.lower()) or (".webp" in message.lower()):
self.sendImage(message, ID, room + '@g.us') self.sendImage(message, ID, room + '@g.us')
@ -695,12 +692,12 @@ class Session(YowsupApp):
waId = self.sendTextMessage(sender + '@s.whatsapp.net', message) waId = self.sendTextMessage(sender + '@s.whatsapp.net', message)
self.msgIDs[waId] = MsgIDs( ID, waId) self.msgIDs[waId] = MsgIDs( ID, waId)
self.logger.info("WA Message send to %s with ID %s", buddy, waId) self.logger.info("WA Message send to %s with ID %s" % (buddy, waId))
#self.sendTextMessage(sender + '@s.whatsapp.net', message) #self.sendTextMessage(sender + '@s.whatsapp.net', message)
def executeCommand(self, command, room): def executeCommand(self, command, room):
if command == '\\leave': if command == '\\leave':
self.logger.debug("Leaving room %s", room) self.logger.debug("Leaving room %s" % room)
# Leave group on whatsapp side # Leave group on whatsapp side
self.leaveGroup(room) self.leaveGroup(room)
# Delete Room on spectrum side # Delete Room on spectrum side
@ -713,7 +710,7 @@ class Session(YowsupApp):
def onSuccess(buddy, lastseen): def onSuccess(buddy, lastseen):
timestamp = time.localtime(time.localtime()-lastseen) timestamp = time.localtime(time.localtime()-lastseen)
timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp) timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp)
self.sendMessageToXMPP(buddy, "%s (%s) %s" % (timestring, utils.ago(lastseen),str(lastseen))) self.sendMessageToXMPP(buddy, "%s (%s) %s" % (timestring, utils.ago(lastseen), lastseen))
def onError(errorIqEntity, originalIqEntity): def onError(errorIqEntity, originalIqEntity):
self.sendMessageToXMPP(errorIqEntity.getFrom(), "LastSeen Error") self.sendMessageToXMPP(errorIqEntity.getFrom(), "LastSeen Error")
@ -725,7 +722,7 @@ class Session(YowsupApp):
latitude,longitude = message.split(':')[1].split(',') latitude,longitude = message.split(':')[1].split(',')
waId = self.sendLocation(buddy, float(latitude), float(longitude)) waId = self.sendLocation(buddy, float(latitude), float(longitude))
self.msgIDs[waId] = MsgIDs( ID, waId) self.msgIDs[waId] = MsgIDs( ID, waId)
self.logger.info("WA Location Message send to %s with ID %s", buddy, waId) self.logger.info("WA Location Message send to %s with ID %s" % (buddy, waId))
@ -734,22 +731,22 @@ class Session(YowsupApp):
timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp)) timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))
if self.initialized == False: if self.initialized == False:
self.logger.debug("Message queued from %s to %s: %s", self.logger.debug("Message queued from %s to %s: %s" %
buddy, self.legacyName, messageContent) (buddy, self.legacyName, messageContent))
self.offlineQueue.append((buddy, messageContent, timestamp)) self.offlineQueue.append((buddy, messageContent, timestamp))
else: else:
self.logger.debug("Message sent from %s to %s: %s", buddy, self.logger.debug("Message sent from %s to %s: %s" % (
self.legacyName, messageContent) buddy, self.legacyName, messageContent))
self.backend.handleMessage(self.user, buddy, messageContent, "", self.backend.handleMessage(self.user, buddy, messageContent, "",
"", timestamp) "", timestamp)
def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = u"", defaultname = u""): def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = "", defaultname = ""):
if timestamp: if timestamp:
timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp)) timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))
if self.initialized == False: if self.initialized == False:
self.logger.debug("Group message queued from %s to %s: %s", self.logger.debug("Group message queued from %s to %s: %s" %
number, room, messageContent) (number, room, messageContent))
if room not in self.groupOfflineQueue: if room not in self.groupOfflineQueue:
self.groupOfflineQueue[room] = [ ] self.groupOfflineQueue[room] = [ ]
@ -758,8 +755,8 @@ class Session(YowsupApp):
(number, messageContent, timestamp) (number, messageContent, timestamp)
) )
else: else:
self.logger.debug("Group message sent from %s to %s: %s", self.logger.debug("Group message sent from %s to %s: %s" %
number, room, messageContent) (number, room, messageContent))
try: try:
group = self.groups[room] group = self.groups[room]
# Update nickname # Update nickname
@ -787,7 +784,7 @@ class Session(YowsupApp):
def changeStatus(self, status): def changeStatus(self, status):
if status != self.status: if status != self.status:
self.logger.info("Status changed: %s", status) self.logger.info("Status changed: %s" % status)
self.status = status self.status = status
if status == protocol_pb2.STATUS_ONLINE \ if status == protocol_pb2.STATUS_ONLINE \
@ -799,8 +796,8 @@ class Session(YowsupApp):
def changeStatusMessage(self, statusMessage): def changeStatusMessage(self, statusMessage):
if (statusMessage != self.statusMessage) or (self.initialized == False): if (statusMessage != self.statusMessage) or (self.initialized == False):
self.statusMessage = statusMessage self.statusMessage = statusMessage
self.setStatus(statusMessage.encode('utf-8')) self.setStatus(statusMessage)
self.logger.info("Status message changed: %s", statusMessage) self.logger.info("Status message changed: %s" % statusMessage)
#if self.initialized == False: #if self.initialized == False:
# self.sendOfflineMessages() # self.sendOfflineMessages()
@ -824,7 +821,7 @@ class Session(YowsupApp):
def removeBuddy(self, buddy): def removeBuddy(self, buddy):
if buddy != "bot": if buddy != "bot":
self.logger.info("Buddy removed: %s", buddy) self.logger.info("Buddy removed: %s" % buddy)
self.buddies.remove(buddy) self.buddies.remove(buddy)
def requestVCard(self, buddy, ID=None): def requestVCard(self, buddy, ID=None):
@ -853,7 +850,7 @@ class Session(YowsupApp):
# Not used # Not used
def onLocationReceived(self, messageId, jid, name, preview, latitude, longitude, receiptRequested, isBroadcast): def onLocationReceived(self, messageId, jid, name, preview, latitude, longitude, receiptRequested, isBroadcast):
buddy = jid.split("@")[0] buddy = jid.split("@")[0]
self.logger.info("Location received from %s: %s, %s", buddy, latitude, longitude) self.logger.info("Location received from %s: %s, %s" % (buddy, latitude, longitude))
url = "http://maps.google.de?%s" % urllib.urlencode({ "q": "%s %s" % (latitude, longitude) }) url = "http://maps.google.de?%s" % urllib.urlencode({ "q": "%s %s" % (latitude, longitude) })
self.sendMessageToXMPP(buddy, utils.shorten(url)) self.sendMessageToXMPP(buddy, utils.shorten(url))
@ -872,7 +869,7 @@ class Session(YowsupApp):
room = gjid.split("@")[0] room = gjid.split("@")[0]
buddy = jid.split("@")[0] buddy = jid.split("@")[0]
self.logger.info("Removed %s from room %s", buddy, room) self.logger.info("Removed %s from room %s" % (buddy, room))
self.backend.handleParticipantChanged(self.user, buddy, room, protocol_pb2.PARTICIPANT_FLAG_NONE, protocol_pb2.STATUS_NONE) # TODO self.backend.handleParticipantChanged(self.user, buddy, room, protocol_pb2.PARTICIPANT_FLAG_NONE, protocol_pb2.STATUS_NONE) # TODO
if receiptRequested: self.call("notification_ack", (gjid, messageId)) if receiptRequested: self.call("notification_ack", (gjid, messageId))

View file

@ -1,3 +1,6 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import Queue import Queue
import threading import threading

9
transwhat.py Executable file → Normal file
View file

@ -1,5 +1,9 @@
#!/usr/bin/python #!/usr/bin/python
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
__author__ = "Steffen Vogel" __author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel" __copyright__ = "Copyright 2015, Steffen Vogel"
__license__ = "GPLv3" __license__ = "GPLv3"
@ -23,6 +27,7 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>. along with transWhat. If not, see <http://www.gnu.org/licenses/>.
""" """
import argparse import argparse
import traceback import traceback
import logging import logging
@ -48,7 +53,7 @@ parser.add_argument('--host', type=str, required=True)
parser.add_argument('--port', type=int, required=True) parser.add_argument('--port', type=int, required=True)
parser.add_argument('--service.backend_id', metavar="ID", type=int, required=True) parser.add_argument('--service.backend_id', metavar="ID", type=int, required=True)
parser.add_argument('config', type=str) parser.add_argument('config', type=str)
parser.add_argument('-j', type=str, required=True) parser.add_argument('-j', type=str, metavar="JID", required=True)
args, unknown = parser.parse_known_args() args, unknown = parser.parse_known_args()
@ -59,7 +64,7 @@ if args.log is None:
# Logging # Logging
logging.basicConfig( logging.basicConfig(
filename=args.log, filename = args.log,
format = "%(asctime)-15s %(levelname)s %(name)s: %(message)s", format = "%(asctime)-15s %(levelname)s %(name)s: %(message)s",
level = logging.DEBUG if args.debug else logging.INFO level = logging.DEBUG if args.debug else logging.INFO
) )

View file

@ -1,3 +1,7 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
__author__ = "Steffen Vogel" __author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel" __copyright__ = "Copyright 2015, Steffen Vogel"
__license__ = "GPLv3" __license__ = "GPLv3"
@ -25,6 +29,7 @@ import e4u
import base64 import base64
import hashlib import hashlib
def ago(secs): def ago(secs):
periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"] periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"]
lengths = [60, 60, 24, 7,4.35, 12, 10] lengths = [60, 60, 24, 7,4.35, 12, 10]
@ -43,11 +48,10 @@ def ago(secs):
return "%d %s ago" % (diff, period) return "%d %s ago" % (diff, period)
def softToUni(message): def softToUni(message):
message = message.decode("utf-8")
return e4u.translate(message, reverse=False, **e4u.SOFTBANK_TRANSLATE_PROFILE) return e4u.translate(message, reverse=False, **e4u.SOFTBANK_TRANSLATE_PROFILE)
def decodePassword(password): def decodePassword(password):
return base64.b64decode(bytes(password.encode("utf-8"))) return base64.b64decode(bytes(password))
def sha1hash(data): def sha1hash(data):
return hashlib.sha1(data).hexdigest() return hashlib.sha1(data).hexdigest()

View file

@ -1,3 +1,7 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
__author__ = "Steffen Vogel" __author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel" __copyright__ = "Copyright 2015, Steffen Vogel"
__license__ = "GPLv3" __license__ = "GPLv3"
@ -21,6 +25,7 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>. along with transWhat. If not, see <http://www.gnu.org/licenses/>.
""" """
from Spectrum2.backend import SpectrumBackend from Spectrum2.backend import SpectrumBackend
from Spectrum2 import protocol_pb2 from Spectrum2 import protocol_pb2
@ -29,6 +34,7 @@ from registersession import RegisterSession
import logging import logging
class WhatsAppBackend(SpectrumBackend): class WhatsAppBackend(SpectrumBackend):
def __init__(self, io, spectrum_jid): def __init__(self, io, spectrum_jid):
SpectrumBackend.__init__(self) SpectrumBackend.__init__(self)
@ -43,7 +49,7 @@ class WhatsAppBackend(SpectrumBackend):
# RequestsHandlers # RequestsHandlers
def handleLoginRequest(self, user, legacyName, password, extra): def handleLoginRequest(self, user, legacyName, password, extra):
self.logger.debug("handleLoginRequest(user=%s, legacyName=%s)", user, legacyName) self.logger.debug("handleLoginRequest(user=%s, legacyName=%s)" % (user, legacyName))
# Key word means we should register a new password # Key word means we should register a new password
if password == 'register': if password == 'register':
if user not in self.sessions: if user not in self.sessions:
@ -55,13 +61,14 @@ class WhatsAppBackend(SpectrumBackend):
self.sessions[user].login(password) self.sessions[user].login(password)
def handleLogoutRequest(self, user, legacyName): def handleLogoutRequest(self, user, legacyName):
self.logger.debug("handleLogoutRequest(user=%s, legacyName=%s)", user, legacyName) self.logger.debug("handleLogoutRequest(user=%s, legacyName=%s)" % (user, legacyName))
if user in self.sessions: if user in self.sessions:
self.sessions[user].logout() self.sessions[user].logout()
del self.sessions[user] del self.sessions[user]
def handleMessageSendRequest(self, user, buddy, message, xhtml="", ID=""): def handleMessageSendRequest(self, user, buddy, message, xhtml="", ID=""):
self.logger.debug("handleMessageSendRequest(user=%s, buddy=%s, message=%s, xhtml=%s, ID=%s)", user, buddy, message, xhtml, ID) self.logger.debug("handleMessageSendRequest(user=%s, buddy=%s, message=%s, xhtml=%s, ID=%s)" %
( user, buddy, message, xhtml, ID))
# For some reason spectrum occasionally sends to identical messages to # For some reason spectrum occasionally sends to identical messages to
# a buddy, one to the bare jid and one to the /bot resource. This # a buddy, one to the bare jid and one to the /bot resource. This
# causes duplicate messages. Thus we should not send consecutive # causes duplicate messages. Thus we should not send consecutive
@ -73,63 +80,63 @@ class WhatsAppBackend(SpectrumBackend):
self.lastMsgId[user] = ID self.lastMsgId[user] = ID
def handleJoinRoomRequest(self, user, room, nickname, pasword): def handleJoinRoomRequest(self, user, room, nickname, pasword):
self.logger.debug("handleJoinRoomRequest(user=%s, room=%s, nickname=%s)", user, room, nickname) self.logger.debug("handleJoinRoomRequest(user=%s, room=%s, nickname=%s)" % (user, room, nickname))
self.sessions[user].joinRoom(room, nickname) self.sessions[user].joinRoom(room, nickname)
def handleLeaveRoomRequest(self, user, room): def handleLeaveRoomRequest(self, user, room):
self.logger.debug("handleLeaveRoomRequest(user=%s, room=%s)", user, room) self.logger.debug("handleLeaveRoomRequest(user=%s, room=%s)" % (user, room))
self.sessions[user].leaveRoom(room) self.sessions[user].leaveRoom(room)
def handleStatusChangeRequest(self, user, status, statusMessage): def handleStatusChangeRequest(self, user, status, statusMessage):
self.logger.debug("handleStatusChangeRequest(user=%s, status=%d, statusMessage=%s)", user, status, statusMessage) self.logger.debug("handleStatusChangeRequest(user=%s, status=%d, statusMessage=%s)" % (user, status, statusMessage))
self.sessions[user].changeStatusMessage(statusMessage) self.sessions[user].changeStatusMessage(statusMessage)
self.sessions[user].changeStatus(status) self.sessions[user].changeStatus(status)
def handleBuddies(self, buddies): def handleBuddies(self, buddies):
"""Called when user logs in. Used to initialize roster.""" """Called when user logs in. Used to initialize roster."""
self.logger.debug("handleBuddies(buddies=%s)", buddies) self.logger.debug("handleBuddies(buddies=%s)" % buddies)
buddies = [b for b in buddies.buddy] buddies = [b for b in buddies.buddy]
if len(buddies) > 0: if len(buddies) > 0:
user = buddies[0].userName user = buddies[0].userName
self.sessions[user].loadBuddies(buddies) self.sessions[user].loadBuddies(buddies)
def handleBuddyUpdatedRequest(self, user, buddy, nick, groups): def handleBuddyUpdatedRequest(self, user, buddy, nick, groups):
self.logger.debug("handleBuddyUpdatedRequest(user=%s, buddy=%s, nick=%s, groups=%s)", user, buddy, nick, str(groups)) self.logger.debug("handleBuddyUpdatedRequest(user=%s, buddy=%s, nick=%s, groups=%s)" % (user, buddy, nick, groups))
self.sessions[user].updateBuddy(buddy, nick, groups) self.sessions[user].updateBuddy(buddy, nick, groups)
def handleBuddyRemovedRequest(self, user, buddy, groups): def handleBuddyRemovedRequest(self, user, buddy, groups):
self.logger.debug("handleBuddyRemovedRequest(user=%s, buddy=%s, groups=%s)", user, buddy, str(groups)) self.logger.debug("handleBuddyRemovedRequest(user=%s, buddy=%s, groups=%s)" % (user, buddy, groups))
self.sessions[user].removeBuddy(buddy) self.sessions[user].removeBuddy(buddy)
def handleTypingRequest(self, user, buddy): def handleTypingRequest(self, user, buddy):
self.logger.debug("handleTypingRequest(user=%s, buddy=%s)", user, buddy) self.logger.debug("handleTypingRequest(user=%s, buddy=%s)" % (user, buddy))
self.sessions[user].sendTypingStarted(buddy) self.sessions[user].sendTypingStarted(buddy)
def handleTypedRequest(self, user, buddy): def handleTypedRequest(self, user, buddy):
self.logger.debug("handleTypedRequest(user=%s, buddy=%s)", user, buddy) self.logger.debug("handleTypedRequest(user=%s, buddy=%s)" % (user, buddy))
self.sessions[user].sendTypingStopped(buddy) self.sessions[user].sendTypingStopped(buddy)
def handleStoppedTypingRequest(self, user, buddy): def handleStoppedTypingRequest(self, user, buddy):
self.logger.debug("handleStoppedTypingRequest(user=%s, buddy=%s)", user, buddy) self.logger.debug("handleStoppedTypingRequest(user=%s, buddy=%s)" % (user, buddy))
self.sessions[user].sendTypingStopped(buddy) self.sessions[user].sendTypingStopped(buddy)
def handleVCardRequest(self, user, buddy, ID): def handleVCardRequest(self, user, buddy, ID):
self.logger.debug("handleVCardRequest(user=%s, buddy=%s, ID=%s)", user, buddy, ID) self.logger.debug("handleVCardRequest(user=%s, buddy=%s, ID=%s)" % (user, buddy, ID))
self.sessions[user].requestVCard(buddy, ID) self.sessions[user].requestVCard(buddy, ID)
def handleVCardUpdatedRequest(self, user, photo, nickname): def handleVCardUpdatedRequest(self, user, photo, nickname):
self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, nickname) self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)" % (user, nickname))
self.sessions[user].setProfilePicture(photo) self.sessions[user].setProfilePicture(photo)
def handleBuddyBlockToggled(self, user, buddy, blocked): def handleBuddyBlockToggled(self, user, buddy, blocked):
self.logger.debug("handleBuddyBlockedToggled(user=%s, buddy=%s, blocked=%s)", user, buddy, blocked) self.logger.debug("handleBuddyBlockedToggled(user=%s, buddy=%s, blocked=%s)" % (user, buddy, blocked))
def relogin(self, user, legacyName, password, extra): def relogin(self, user, legacyName, password, extra):
""" """
Used to re-initialize the session object. Used when finished with Used to re-initialize the session object. Used when finished with
registration session and the user needs to login properly registration session and the user needs to login properly
""" """
self.logger.debug("relogin(user=%s, legacyName=%s)", user, legacyName) self.logger.debug("relogin(user=%s, legacyName=%s)" % (user, legacyName))
# Change password in spectrum database # Change password in spectrum database
self.handleQuery('register %s %s %s' % (user, legacyName, password)) self.handleQuery('register %s %s %s' % (user, legacyName, password))
# Key word means we should register a new password # Key word means we should register a new password
@ -144,8 +151,8 @@ class WhatsAppBackend(SpectrumBackend):
pass pass
def handleFTStartRequest(self, user, buddy, fileName, size, ftID): def handleFTStartRequest(self, user, buddy, fileName, size, ftID):
self.logger.debug('File send request %s, for user %s, from %s, size: %s', self.logger.debug('File send request %s, for user %s, from %s, size: %s' %
fileName, user, buddy, size) (fileName, user, buddy, size))
def handleFTFinishRequest(self, user, buddy, fileName, size, ftID): def handleFTFinishRequest(self, user, buddy, fileName, size, ftID):
pass pass
@ -160,7 +167,7 @@ class WhatsAppBackend(SpectrumBackend):
pass pass
def handleMessageAckRequest(self, user, legacyName, ID = 0): def handleMessageAckRequest(self, user, legacyName, ID = 0):
self.logger.info("Meassage ACK request for %s !!",legacyName) self.logger.info("Meassage ACK request for %s !!" % legacyName)
def sendData(self, data): def sendData(self, data):
self.io.sendData(data) self.io.sendData(data)

View file

@ -1,3 +1,6 @@
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import logging import logging
from yowsup import env from yowsup import env
@ -7,7 +10,7 @@ from yowsup.layers import YowLayerEvent, YowParallelLayer
from yowsup.layers.auth import AuthError from yowsup.layers.auth import AuthError
# Layers # Layers
from yowsup.layers.axolotl import YowAxolotlLayer from yowsup.layers.axolotl import AxolotlSendLayer, AxolotlControlLayer, AxolotlReceivelayer
from yowsup.layers.auth import YowCryptLayer, YowAuthenticationProtocolLayer from yowsup.layers.auth import YowCryptLayer, YowAuthenticationProtocolLayer
from yowsup.layers.coder import YowCoderLayer from yowsup.layers.coder import YowCoderLayer
from yowsup.layers.logger import YowLoggerLayer from yowsup.layers.logger import YowLoggerLayer
@ -55,38 +58,9 @@ from functools import partial
#from session import MsgIDs #from session import MsgIDs
# Temporarily work around yowsup padding bugs with new protocol
class UpdatedYowAxolotlLayer(YowAxolotlLayer):
def decodeInt7bit(self, string):
idx = 0
while ord(string[idx]) >= 128:
idx += 1
consumedBytes = idx + 1
value = 0
while idx >= 0:
value <<= 7
value += ord(string[idx]) % 128
idx -= 1
return value, consumedBytes
def unpadV2Plaintext(self, v2plaintext):
end = -ord(v2plaintext[-1]) # length of the left padding
length,consumed = self.decodeInt7bit(v2plaintext[1:])
return v2plaintext[1+consumed:end]
# Temporary env until yowsup updates
class UpdatedS40YowsupEnv(env.S40YowsupEnv):
_VERSION = "2.13.39"
_OS_NAME= "S40"
_OS_VERSION = "14.26"
_DEVICE_NAME = "302"
_MANUFACTURER = "Nokia"
_TOKEN_STRING = "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk{phone}"
_AXOLOTL = True
class YowsupApp(object): class YowsupApp(object):
def __init__(self): def __init__(self):
env.CURRENT_ENV = UpdatedS40YowsupEnv() env.CURRENT_ENV = env.AndroidYowsupEnv()
layers = (YowsupAppLayer, layers = (YowsupAppLayer,
YowParallelLayer((YowAuthenticationProtocolLayer, YowParallelLayer((YowAuthenticationProtocolLayer,
@ -104,7 +78,8 @@ class YowsupApp(object):
YowProfilesProtocolLayer, YowProfilesProtocolLayer,
YowGroupsProtocolLayer, YowGroupsProtocolLayer,
YowPresenceProtocolLayer)), YowPresenceProtocolLayer)),
UpdatedYowAxolotlLayer, AxolotlControlLayer,
YowParallelLayer((AxolotlSendLayer, AxolotlReceivelayer)),
YowCoderLayer, YowCoderLayer,
YowCryptLayer, YowCryptLayer,
YowStanzaRegulator, YowStanzaRegulator,
@ -178,7 +153,7 @@ class YowsupApp(object):
- to: (xxxxxxxxxx@s.whatsapp.net) who to send the message to - to: (xxxxxxxxxx@s.whatsapp.net) who to send the message to
- message: (str) the body of the message - message: (str) the body of the message
""" """
messageEntity = TextMessageProtocolEntity(message, to = to) messageEntity = TextMessageProtocolEntity(message.encode('utf-8'), to = to)
self.sendEntity(messageEntity) self.sendEntity(messageEntity)
return messageEntity.getId() return messageEntity.getId()
@ -264,7 +239,7 @@ class YowsupApp(object):
- phone_number: (str) The cellphone number of the person to - phone_number: (str) The cellphone number of the person to
subscribe to subscribe to
""" """
self.logger.debug("Subscribing to Presence updates from %s", (phone_number)) self.logger.debug("Subscribing to Presence updates from %s" % phone_number)
jid = phone_number + '@s.whatsapp.net' jid = phone_number + '@s.whatsapp.net'
entity = SubscribePresenceProtocolEntity(jid) entity = SubscribePresenceProtocolEntity(jid)
self.sendEntity(entity) self.sendEntity(entity)
@ -394,7 +369,7 @@ class YowsupApp(object):
iq = GetStatusesIqProtocolEntity([c + '@s.whatsapp.net' for c in contacts]) iq = GetStatusesIqProtocolEntity([c + '@s.whatsapp.net' for c in contacts])
def onSuccess(response, request): def onSuccess(response, request):
if success is not None: if success is not None:
self.logger.debug("Received Statuses %s", response) self.logger.debug("Received Statuses %s" % response)
s = {} s = {}
for k, v in response.statuses.iteritems(): for k, v in response.statuses.iteritems():
s[k.split('@')[0]] = v s[k.split('@')[0]] = v
@ -798,7 +773,7 @@ class YowsupAppLayer(YowInterfaceLayer):
""" """
Sends ack automatically Sends ack automatically
""" """
self.logger.debug("Received notification (%s): %s", type(entity), entity) self.logger.debug("Received notification (%s): %s" % (type(entity), entity))
self.toLower(entity.ack()) self.toLower(entity.ack())
if isinstance(entity, CreateGroupsNotificationProtocolEntity): if isinstance(entity, CreateGroupsNotificationProtocolEntity):
self.caller.onAddedToGroup(entity) self.caller.onAddedToGroup(entity)
@ -840,7 +815,7 @@ class YowsupAppLayer(YowInterfaceLayer):
@ProtocolEntityCallback('message') @ProtocolEntityCallback('message')
def onMessageReceived(self, entity): def onMessageReceived(self, entity):
self.logger.debug("Received Message: %s", entity) self.logger.debug("Received Message: %s" % unicode(entity))
if entity.getType() == MessageProtocolEntity.MESSAGE_TYPE_TEXT: if entity.getType() == MessageProtocolEntity.MESSAGE_TYPE_TEXT:
self.caller.onTextMessage( self.caller.onTextMessage(
entity._id, entity._id,