From 9434b1db6f5a3f2fdc9e8ee3737dba27625685ce Mon Sep 17 00:00:00 2001 From: moyamo Date: Mon, 14 Dec 2015 20:14:20 +0200 Subject: [PATCH 01/46] Request contacts' statuses when user logs in --- buddy.py | 36 ++++++++++++++++++++++++++++-------- session.py | 18 ++++++------------ yowsupwrapper.py | 22 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/buddy.py b/buddy.py index 0823163..6c581bf 100644 --- a/buddy.py +++ b/buddy.py @@ -24,6 +24,7 @@ __email__ = "post@steffenvogel.de" from Spectrum2 import protocol_pb2 import logging +import time class Buddy(): @@ -74,6 +75,7 @@ class BuddyList(dict): # new = self.buddies.keys() # contacts = new contacts = self.keys() + contacts.remove('bot') if self.synced == False: self.session.sendSync(contacts, delta = False, interactive = True) @@ -93,11 +95,19 @@ class BuddyList(dict): # for number in contacts: buddy = self[number] - if number != 'bot': - self.backend.handleBuddyChanged(self.user, number, buddy.nick, - buddy.groups, protocol_pb2.STATUS_NONE, - iconHash = buddy.image_hash if buddy.image_hash is not None else "") - self.session.subscribePresence(number) + self.backend.handleBuddyChanged(self.user, number, buddy.nick, + buddy.groups, protocol_pb2.STATUS_NONE, + iconHash = buddy.image_hash if buddy.image_hash is not None else "") + self.session.subscribePresence(number) + self.logger.debug("%s is requesting statuses of: %s", self.user, contacts) + self.session.requestStatuses(contacts, success = self.onStatus) + + def onStatus(self, contacts): + self.logger.debug("%s received statuses of: %s", self.user, contacts) + for number, (status, time) in contacts.iteritems(): + buddy = self[number] + buddy.statusMsg = status + self.updateSpectrum(buddy) def load(self, buddies): @@ -113,21 +123,31 @@ class BuddyList(dict): else: self.session.sendSync([number], delta = True, interactive = True) self.session.subscribePresence(number) + self.session.requestStatuses(contacts, success = self.onStatus) buddy = Buddy(self.owner, number, nick, "", groups, image_hash) self[number] = buddy self.logger.debug("Roster add: %s", buddy) + 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 - self.backend.handleBuddyChanged(self.user, number, buddy.nick, - buddy.groups, status, + + 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) + + self.backend.handleBuddyChanged(self.user, buddy.number, buddy.nick, + buddy.groups, status, statusMessage = statusmsg, iconHash = buddy.image_hash if buddy.image_hash is not None else "") - return buddy def remove(self, number): try: diff --git a/session.py b/session.py index 6dfed66..d481bfe 100644 --- a/session.py +++ b/session.py @@ -30,7 +30,6 @@ from PIL import Image import sys import os -from yowsup.common.tools import TimeTools from yowsup.layers.protocol_media.mediauploader import MediaUploader from yowsup.layers.protocol_media.mediadownloader import MediaDownloader @@ -497,25 +496,20 @@ class Session(YowsupApp): buddy.presence = _type - timestamp = time.localtime(buddy.lastseen) - statusmsg = buddy.statusMsg + time.strftime("\n Last seen: %a, %d %b %Y %H:%M:%S", timestamp) - if _type == "unavailable": - self.onPresenceUnavailable(buddy, statusmsg) + self.onPresenceUnavailable(buddy) else: - self.onPresenceAvailable(buddy, statusmsg) + self.onPresenceAvailable(buddy) - def onPresenceAvailable(self, buddy, statusmsg): + def onPresenceAvailable(self, buddy): self.logger.info("Is available: %s", buddy) - self.backend.handleBuddyChanged(self.user, buddy.number, - buddy.nick, buddy.groups, protocol_pb2.STATUS_ONLINE, statusmsg, buddy.image_hash) + self.buddies.updateSpectrum(buddy) - def onPresenceUnavailable(self, buddy, statusmsg): + def onPresenceUnavailable(self, buddy): self.logger.info("Is unavailable: %s", buddy) - self.backend.handleBuddyChanged(self.user, buddy.number, - buddy.nick, buddy.groups, protocol_pb2.STATUS_AWAY, statusmsg, buddy.image_hash) + self.buddies.updateSpectrum(buddy) # spectrum RequestMethods def sendTypingStarted(self, buddy): diff --git a/yowsupwrapper.py b/yowsupwrapper.py index d7c3a79..9fdb84c 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -65,6 +65,7 @@ class YowsupApp(object): YowProfilesProtocolLayer, YowGroupsProtocolLayer, YowPresenceProtocolLayer)), + YowLoggerLayer, YowAxolotlLayer, YowCoderLayer, YowCryptLayer, @@ -291,6 +292,27 @@ class YowsupApp(object): context = GetSyncIqProtocolEntity.CONTEXT_INTERACTIVE if interactive else GetSyncIqProtocolEntity.CONTEXT_REGISTRATION iq = GetSyncIqProtocolEntity(contacts, mode, context) self.sendIq(iq) + + def requestStatuses(self, contacts, success = None, failure = None): + """ + Request the statuses of a number of users. + + Args: + - contacts: ([str]) the phone numbers of users whose statuses you + wish to request + - success: (func) called when request is successful + - failure: (func) called when request has failed + """ + iq = GetStatusesIqProtocolEntity([c + '@s.whatsapp.net' for c in contacts]) + def onSuccess(response, request): + self.logger.debug("Received Statuses %s", response) + s = {} + for k, v in response.statuses.iteritems(): + s[k.split('@')[0]] = v + success(s) + + self.sendIq(iq, onSuccess = onSuccess, onError = failure) + def requestLastSeen(self, phoneNumber, success = None, failure = None): """ From 40131a06eb1d8ae533de5d30b0873832f8050c30 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 17 Dec 2015 10:06:11 +0200 Subject: [PATCH 02/46] Fix undefined variable in buddy.py --- buddy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buddy.py b/buddy.py index 6c581bf..8e722e3 100644 --- a/buddy.py +++ b/buddy.py @@ -123,7 +123,7 @@ class BuddyList(dict): else: self.session.sendSync([number], delta = True, interactive = True) self.session.subscribePresence(number) - self.session.requestStatuses(contacts, success = self.onStatus) + self.session.requestStatuses([number], success = self.onStatus) buddy = Buddy(self.owner, number, nick, "", groups, image_hash) self[number] = buddy self.logger.debug("Roster add: %s", buddy) From 3c16f39479767d165aac8bf1c0ea050bf3a73cce Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 18 Dec 2015 20:33:32 +0200 Subject: [PATCH 03/46] Send 'muc = true' to spectrum backend --- Spectrum2/backend.py | 14 ++++++++++++-- transwhat.py | 7 ++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Spectrum2/backend.py b/Spectrum2/backend.py index feb2e94..4504bb1 100644 --- a/Spectrum2/backend.py +++ b/Spectrum2/backend.py @@ -222,9 +222,19 @@ class SpectrumBackend: message = WRAP(d.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_FT_DATA); self.send(message) - def handleBackendConfig(self, section, key, value): + def handleBackendConfig(self, data): + """ + data is a dictionary, whose keys are sections and values are a list of + tuples of configuration key and configuration value. + """ c = protocol_pb2.BackendConfig() - c.config = "[%s]\n%s = %s\n" % (section, key, value) + config = [] + for section, rest in data.items(): + config.append('[%s]' % section) + for key, value in rest: + config.append('%s = %s' % (key, value)) + + c.config = '\n'.join(config) message = WRAP(c.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_BACKEND_CONFIG); self.send(message) diff --git a/transwhat.py b/transwhat.py index aa58ad9..935b185 100755 --- a/transwhat.py +++ b/transwhat.py @@ -76,7 +76,12 @@ io = IOChannel(args.host, args.port, handleTransportData, connectionClosed) plugin = WhatsAppBackend(io, args.j) -plugin.handleBackendConfig('features', 'send_buddies_on_login', 1) +plugin.handleBackendConfig({ + 'features': [ + ('send_buddies_on_login', 1), + ('muc', 'true'), + ], +}) while True: try: From 8e9e8a6476bbd76d4433a564ca306e7112674c96 Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 18 Dec 2015 20:56:36 +0200 Subject: [PATCH 04/46] Remove manual sync command. It is done automatically --- bot.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/bot.py b/bot.py index dcd3708..ea54d42 100644 --- a/bot.py +++ b/bot.py @@ -37,7 +37,6 @@ class Bot(): self.commands = { "help": self._help, "prune": self._prune, - "sync": self._sync, "groups": self._groups, "getgroups": self._getgroups } @@ -71,23 +70,10 @@ class Bot(): self.session.backend.handleMessage(self.session.user, self.name, message) # commands - def _sync(self): - user = self.session.legacyName - password = self.session.password - - count = self.session.buddies.sync(user, password) - self.session.updateRoster() - - if count: - self.send("sync complete, %d buddies are using WhatsApp" % count) - else: - self.send("sync failed, sorry something went wrong") - def _help(self): self.send("""following bot commands are available: \\help show this message \\prune clear your buddylist -\\sync sync your imported contacts with WhatsApp following user commands are available: \\lastseen request last online timestamp from buddy From 249e8582cb0a9ff389a401dd030e90aa41d0c4ce Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 18 Dec 2015 21:21:37 +0200 Subject: [PATCH 05/46] Remove YowLoggerLayer --- yowsupwrapper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 9fdb84c..048ba98 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -65,7 +65,6 @@ class YowsupApp(object): YowProfilesProtocolLayer, YowGroupsProtocolLayer, YowPresenceProtocolLayer)), - YowLoggerLayer, YowAxolotlLayer, YowCoderLayer, YowCryptLayer, From c1736e4c478c61fc6307ebf77a2fbe7afe5e58b1 Mon Sep 17 00:00:00 2001 From: moyamo Date: Sat, 26 Dec 2015 21:55:31 +0200 Subject: [PATCH 06/46] Correct Installation instructions (install yowsup2) --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index 29cb6ca..b7a8569 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -74,7 +74,7 @@ Checkout the latest version of transWhat from GitHub: Install required dependencies: - $ pip install --pre e4u protobuf python-dateutil yowsup + $ pip install --pre e4u protobuf python-dateutil yowsup2 - **e4u**: is a simple emoji4unicode python bindings - [**yowsup**](https://github.com/tgalal/yowsup): is a python library that enables you build application which use WhatsApp service. From c40d4776673e3d162bc92c193f9db722c860ba45 Mon Sep 17 00:00:00 2001 From: Mohammed Yaseen Mowzer Date: Sun, 27 Dec 2015 14:24:05 +0200 Subject: [PATCH 07/46] Update INSTALL.md --- INSTALL.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index b7a8569..6c30fb4 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -63,6 +63,9 @@ Create a new file `/etc/spectrum2/transports/whatsapp.cfg` with the following co [logging] config = /etc/spectrum2/logging.cfg backend_config = /etc/spectrum2/backend-logging.cfg + + [database] + type = sqlite3 ## transWhat From 02a27ecac8debf20d63d10e81147e3d785b9e3cb Mon Sep 17 00:00:00 2001 From: moyamo Date: Sun, 27 Dec 2015 23:09:27 +0200 Subject: [PATCH 08/46] Fix encoding errors on status messages --- buddy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/buddy.py b/buddy.py index 8e722e3..a5f7a64 100644 --- a/buddy.py +++ b/buddy.py @@ -34,7 +34,7 @@ class Buddy(): self.number = number self.groups = groups self.image_hash = image_hash if image_hash is not None else "" - self.statusMsg = "" + self.statusMsg = u"" self.lastseen = 0 self.presence = 0 @@ -62,7 +62,7 @@ class BuddyList(dict): for buddy in buddies: number = buddy.buddyName nick = buddy.alias - statusMsg = buddy.statusMessage + statusMsg = buddy.statusMessage.decode('utf-8') groups = [g for g in buddy.group] image_hash = buddy.iconHash self[number] = Buddy(self.owner, number, nick, statusMsg, @@ -106,7 +106,7 @@ class BuddyList(dict): self.logger.debug("%s received statuses of: %s", self.user, contacts) for number, (status, time) in contacts.iteritems(): buddy = self[number] - buddy.statusMsg = status + buddy.statusMsg = status.decode('utf-8') self.updateSpectrum(buddy) From eba8d24a92a02dba576e1a65af86cdc73501a0de Mon Sep 17 00:00:00 2001 From: moyamo Date: Mon, 28 Dec 2015 14:12:47 +0200 Subject: [PATCH 09/46] Rework groupchats to support sending private messages --- group.py | 67 ++++++++++++++++++++++++++++- session.py | 123 +++++++++++++++++------------------------------------ 2 files changed, 104 insertions(+), 86 deletions(-) diff --git a/group.py b/group.py index 8708fe7..993bae4 100644 --- a/group.py +++ b/group.py @@ -21,14 +21,77 @@ __email__ = "post@steffenvogel.de" along with transWhat. If not, see . """ +from Spectrum2 import protocol_pb2 + class Group(): - def __init__(self, id, owner, subject, subjectOwner): + def __init__(self, id, owner, subject, subjectOwner, backend, user): self.id = id self.subject = subject self.subjectOwner = subjectOwner self.owner = owner self.joined = False + self.backend = backend + self.user = user self.nick = "me" - self.participants = [] + # Participants is a number -> nickname dict + self.participants = {} + + def addParticipants(self, participants, buddies, yourNumber): + """ + Adds participants to the group. + + Args: + - participants: (Iterable) phone numbers of participants + - buddies: (dict) Used to get the nicknames of the participants + - yourNumber: The number you are using + """ + for jid in participants: + number = jid.split('@')[0] + try: + nick = buddies[number].nick + except KeyError: + nick = number + if number == yourNumber: + nick = self.nick + if nick == "": + nick = number + self.participants[number] = nick + + def sendParticipantsToSpectrum(self, yourNumber): + for number, nick in self.participants.iteritems(): + if number == self.owner: + flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR + else: + flags = protocol_pb2.PARTICIPANT_FLAG_NONE + if number == yourNumber: + flags = flags | protocol_pb2.PARTICIPANT_FLAG_ME + + self._updateParticipant(number, flags, protocol_pb2.STATUS_ONLINE) + + def removeParticipants(self, participants): + for jid in participants: + number = jid.split('@')[0] + nick = self.participants[number] + flags = protocol_pb2.PARTICIPANT_FLAG_NONE + self._updateParticipant(number, flags, protocol_pb2.STATUS_NONE) + del self.participants[number] + + def changeNick(self, number, new_nick): + if self.participants[number] == new_nick: + return + if number == self.owner: + flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR + else: + flags = protocol_pb2.PARTICIPANT_FLAG_NONE + self._updateParticipant(number, flags, protocol_pb2.STATUS_ONLINE, new_nick) + self.participants[number] = new_nick + + def _updateParticipant(self, number, flags, status, newNick = ""): + nick = self.participants[number] + # Notice the status message is the buddy's number + if self.joined: + self.backend.handleParticipantChanged( + self.user, nick, self.id, flags, + status, number, newname = newNick) diff --git a/session.py b/session.py index d481bfe..5b3dd11 100644 --- a/session.py +++ b/session.py @@ -143,9 +143,10 @@ class Session(YowsupApp): oroom.subjectOwner = subjectOwner oroom.subject = subject else: - self.groups[room] = Group(room, owner, subject, subjectOwner) + self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) # self.joinRoom(self._shortenGroupId(room), self.user.split("@")[0]) - self.groups[room].participants = group.getParticipants().keys() + self.groups[room].addParticipants(group.getParticipants().keys(), + self.buddies, self.legacyName) #self._addParticipantsToRoom(room, group.getParticipants()) @@ -172,13 +173,15 @@ class Session(YowsupApp): self.legacyName, room, nick) group = self.groups[room] + group.joined = True group.nick = nick + group.participants[self.legacyName] = nick try: - ownerNick = self.buddies[group.subjectOwner].nick + ownerNick = group.participants[group.subjectOwner] except KeyError: ownerNick = group.subjectOwner - self._refreshParticipants(room) + group.sendParticipantsToSpectrum(self.legacyName) self.backend.handleSubject(self.user, self._shortenGroupId(room), group.subject, ownerNick) self.logger.debug("Room subject: room=%s, subject=%s", @@ -186,7 +189,6 @@ class Session(YowsupApp): self.backend.handleRoomNicknameChanged( self.user, self._shortenGroupId(room), group.subject ) - group.joined = True else: self.logger.warn("Room doesn't exist: %s", room) @@ -198,29 +200,6 @@ class Session(YowsupApp): else: self.logger.warn("Room doesn't exist: %s. Unable to leave.", room) - def _refreshParticipants(self, room): - group = self.groups[room] - for jid in group.participants: - buddy = jid.split("@")[0] - self.logger.info("Added %s to room %s", buddy, room) - try: - nick = self.buddies[buddy].nick - except KeyError: - nick = buddy - if nick == "": - nick = buddy - - if buddy == group.owner: - flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR - else: - flags = protocol_pb2.PARTICIPANT_FLAG_NONE - if buddy == self.legacyName: - nick = group.nick - flags = flags | protocol_pb2.PARTICIPANT_FLAG_ME - self.backend.handleParticipantChanged( - self.user, nick, self._shortenGroupId(room), flags, - protocol_pb2.STATUS_ONLINE, buddy) - def _lastSeen(self, number, seconds): self.logger.debug("Last seen %s at %s seconds" % (number, str(seconds))) if seconds < 60: @@ -300,17 +279,6 @@ class Session(YowsupApp): buddy, self.legacyName, messageContent, timestamp) if participant is not None: # Group message partname = participant.split('@')[0] - try: - part = self.buddies[partname] - if part.nick == "": - part.nick = notify - self.backend.handleParticipantChanged( - self.user, partname, self._shortenGroupId(buddy), - protocol_pb2.PARTICIPANT_FLAG_NONE, - protocol_pb2.STATUS_ONLINE, "", part.nick - ) # TODO - except KeyError: - self.updateBuddy(partname, notify, []) self.sendGroupMessageToXMPP(buddy, partname, messageContent, timestamp) else: @@ -441,11 +409,8 @@ class Session(YowsupApp): subjectOwner = group.getSubjectOwnerJid(full = False) subject = utils.softToUni(group.getSubject()) - self.groups[room] = Group(room, owner, subject, subjectOwner) - self.groups[room].participants = group.getParticipants().keys() -# self.joinRoom(self._shortenGroupId(room), self.user.split("@")[0]) - - #self._addParticipantsToRoom(room, group.getParticipants()) + self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) + self.groups[room].addParticipants(group.getParticipants, self.buddies, self.legacyName) self.bot.send("You have been added to group: %s@%s (%s)" % (self._shortenGroupId(room), subject, self.backend.spectrum_jid)) @@ -453,29 +418,14 @@ class Session(YowsupApp): def onParticipantsAddedToGroup(self, group): self.logger.debug("Participants added to group: %s", group) room = group.getGroupId().split('@')[0] - self.groups[room].participants.extend(group.getParticipants()) - self._refreshParticipants(room) + self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName) + self.groups[room].sendParticipantsToSpectrum(self.legacyName) # Called by superclass def onParticipantsRemovedFromGroup(self, room, participants): self.logger.debug("Participants removed from group: %s, %s", room, participants) - group = self.groups[room] - for jid in participants: - group.participants.remove(jid) - buddy = jid.split("@")[0] - try: - nick = self.buddies[buddy].nick - except KeyError: - nick = buddy - if nick == "": - nick = buddy - if buddy == self.legacyName: - nick = group.nick - flags = protocol_pb2.PARTICIPANT_FLAG_NONE - self.backend.handleParticipantChanged( - self.user, nick, self._shortenGroupId(room), flags, - protocol_pb2.STATUS_NONE, buddy) + self.groups[room].removeParticipants(participants) def onPresenceReceived(self, _type, name, jid, lastseen): self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) @@ -542,13 +492,19 @@ class Session(YowsupApp): elif "-" in sender: # group msg if "/" in sender: # directed at single user room, nick = sender.split("/") - for buddy, buddy3 in self.buddies.iteritems(): - self.logger.info("Group buddy=%s nick=%s", buddy, - buddy3.nick) - if buddy3.nick == nick: - nick = buddy - waId = self.sendTextMessage(nick + '@s.whatsapp.net', message) - self.msgIDs[waId] = MsgIDs( ID, waId) + group = self.groups[room] + number = None + for othernumber, othernick in group.participants.iteritems(): + if othernick == nick: + number = othernumber + break + if number is not None: + self.logger.debug("Private message sent from %s to %s", self.legacyName, number) + waId = self.sendTextMessage(number + '@s.whatsapp.net', message) + self.msgIDs[waId] = MsgIDs( ID, waId) + else: + self.logger.error("Attempted to send private message to non-existent user") + self.logger.debug("%s to %s in %s", self.legacyName, nick, room) else: room = sender if message[0] == '\\' and message[:1] != '\\\\': @@ -680,35 +636,34 @@ class Session(YowsupApp): self.backend.handleMessage(self.user, buddy, messageContent, "", "", timestamp) - def sendGroupMessageToXMPP(self, room, buddy, messageContent, timestamp = ""): - # self._refreshParticipants(room) - try: - nick = self.buddies[buddy].nick - except KeyError: - nick = buddy - if nick == "": - nick = buddy - + def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = ""): if timestamp: timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp)) if self.initialized == False: self.logger.debug("Group message queued from %s to %s: %s", - buddy, room, messageContent) + number, room, messageContent) if room not in self.groupOfflineQueue: self.groupOfflineQueue[room] = [ ] self.groupOfflineQueue[room].append( - (buddy, messageContent, timestamp) + (number, messageContent, timestamp) ) else: - self.logger.debug("Group message sent from %s (%s) to %s: %s", - buddy, nick, room, messageContent) + self.logger.debug("Group message sent from %s to %s: %s", + number, room, messageContent) try: group = self.groups[room] + # Update nickname + try: + if self.buddies[number].nick != "": + group.changeNick(number, self.buddies[number].nick) + except KeyError: + pass + nick = group.participants[number] if group.joined: - self.backend.handleMessage(self.user,room, messageContent, + self.backend.handleMessage(self.user, room, messageContent, nick, "", timestamp) else: self.bot.send("You have received a message in group: %s@%s" @@ -718,7 +673,7 @@ class Session(YowsupApp): except KeyError: self.logger.warn("Group is not in group list") self.backend.handleMessage(self.user, self._shortenGroupId(room), - messageContent, nick, "", timestamp) + messageContent, number, "", timestamp) def changeStatus(self, status): From 40935682344aa92e6416b3f3f8116e19e1447f26 Mon Sep 17 00:00:00 2001 From: moyamo Date: Tue, 29 Dec 2015 13:50:42 +0200 Subject: [PATCH 10/46] Remove \sync from USAGE.md --- USAGE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/USAGE.md b/USAGE.md index dcea058..67b4c8b 100644 --- a/USAGE.md +++ b/USAGE.md @@ -28,7 +28,6 @@ The bot is one of the contacts every user has in its contact list. It offers you | ------------ | --------------- | | `\help` | show this message | | `\prune` | clear your buddylist | -| `\sync` | sync your imported contacts with WhatsApp | | `\lastseen` | request last online timestamp from buddy | | `\leave` | permanently leave group chat | | `\groups` | print all attended groups | From fae4c648eea5db5060d2e4fed200a7b59d72cd1d Mon Sep 17 00:00:00 2001 From: moyamo Date: Tue, 29 Dec 2015 21:03:14 +0200 Subject: [PATCH 11/46] Use unicode string literals --- Spectrum2/backend.py | 1 - bot.py | 40 ++--- buddy.py | 48 ++--- group.py | 24 +-- reader.py | 20 +-- session.py | 418 +++++++++++++++++++++---------------------- transwhat.py | 38 ++-- utils.py | 22 +-- whatsappbackend.py | 48 ++--- yowsupwrapper.py | 142 +++++++-------- 10 files changed, 400 insertions(+), 401 deletions(-) diff --git a/Spectrum2/backend.py b/Spectrum2/backend.py index 4504bb1..5a91a27 100644 --- a/Spectrum2/backend.py +++ b/Spectrum2/backend.py @@ -262,7 +262,6 @@ class SpectrumBackend: def handleConvMessagePayload(self, data): payload = protocol_pb2.ConversationMessage() - self.logger.error("handleConvMessagePayload") if (payload.ParseFromString(data) == False): #TODO: ERROR return diff --git a/bot.py b/bot.py index ea54d42..ac44fff 100644 --- a/bot.py +++ b/bot.py @@ -1,10 +1,10 @@ -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, Steffen Vogel" -__license__ = "GPLv3" -__maintainer__ = "Steffen Vogel" -__email__ = "post@steffenvogel.de" +__author__ = u"Steffen Vogel" +__copyright__ = u"Copyright 2015, Steffen Vogel" +__license__ = u"GPLv3" +__maintainer__ = u"Steffen Vogel" +__email__ = u"post@steffenvogel.de" -""" +u""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -30,30 +30,30 @@ import os import utils class Bot(): - def __init__(self, session, name = "Bot"): + def __init__(self, session, name = u"Bot"): self.session = session self.name = name self.commands = { - "help": self._help, - "prune": self._prune, - "groups": self._groups, - "getgroups": self._getgroups + u"help": self._help, + u"prune": self._prune, + u"groups": self._groups, + u"getgroups": self._getgroups } def parse(self, message): - args = message.split(" ") + args = message.split(u" ") cmd = args.pop(0) - if cmd[0] == '\\': + if cmd[0] == u'\\': try: self.call(cmd[1:], args) except KeyError: - self.send("invalid command") + self.send(u"invalid command") except TypeError: - self.send("invalid syntax") + self.send(u"invalid syntax") else: - self.send("a valid command starts with a backslash") + self.send(u"a valid command starts with a backslash") def call(self, cmd, args = []): func = self.commands[cmd] @@ -71,7 +71,7 @@ class Bot(): # commands def _help(self): - self.send("""following bot commands are available: + self.send(u"""following bot commands are available: \\help show this message \\prune clear your buddylist @@ -86,7 +86,7 @@ following group commands are available def _prune(self): self.session.buddies.prune() self.session.updateRoster() - self.send("buddy list cleared") + self.send(u"buddy list cleared") def _groups(self): for group in self.session.groups: @@ -96,9 +96,9 @@ following group commands are available except KeyError: nick = buddy - self.send(self.session.groups[group].id + "@" + self.session.backend.spectrum_jid + " " + self.session.groups[group].subject + " Owner: " + nick ) + self.send(self.session.groups[group].id + u"@" + self.session.backend.spectrum_jid + u" " + self.session.groups[group].subject + u" Owner: " + nick ) def _getgroups(self): - #self.session.call("group_getGroups", ("participating",)) + #self.session.call(u"group_getGroups", (u"participating",)) self.session.requestGroupsList(self.session._updateGroups) diff --git a/buddy.py b/buddy.py index a5f7a64..1cdb433 100644 --- a/buddy.py +++ b/buddy.py @@ -1,10 +1,10 @@ -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, Steffen Vogel" -__license__ = "GPLv3" -__maintainer__ = "Steffen Vogel" -__email__ = "post@steffenvogel.de" +__author__ = u"Steffen Vogel" +__copyright__ = u"Copyright 2015, Steffen Vogel" +__license__ = u"GPLv3" +__maintainer__ = u"Steffen Vogel" +__email__ = u"post@steffenvogel.de" -""" +u""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -33,7 +33,7 @@ class Buddy(): self.owner = owner self.number = number 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 u"" self.statusMsg = u"" self.lastseen = 0 self.presence = 0 @@ -46,7 +46,7 @@ class Buddy(): self.image_hash = image_hash def __str__(self): - return "%s (nick=%s)" % (self.number, self.nick) + return u"%s (nick=%s)" % (self.number, self.nick) class BuddyList(dict): @@ -62,20 +62,20 @@ class BuddyList(dict): for buddy in buddies: number = buddy.buddyName nick = buddy.alias - statusMsg = buddy.statusMessage.decode('utf-8') + statusMsg = buddy.statusMessage.decode(u'utf-8') 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") + self.logger.debug(u"Update roster") # old = self.buddies.keys() # self.buddies.load() # new = self.buddies.keys() # contacts = new contacts = self.keys() - contacts.remove('bot') + contacts.remove(u'bot') if self.synced == False: self.session.sendSync(contacts, delta = False, interactive = True) @@ -84,11 +84,11 @@ class BuddyList(dict): # add = set(new) - set(old) # remove = set(old) - set(new) -# self.logger.debug("Roster remove: %s", str(list(remove))) - self.logger.debug("Roster add: %s", str(list(contacts))) +# self.logger.debug(u"Roster remove: %s", str(list(remove))) + self.logger.debug(u"Roster add: %s", str(list(contacts))) # for number in remove: -# self.backend.handleBuddyChanged(self.user, number, "", [], +# self.backend.handleBuddyChanged(self.user, number, u"", [], # protocol_pb2.STATUS_NONE) # self.backend.handleBuddyRemoved(self.user, number) # self.unsubscribePresence(number) @@ -97,16 +97,16 @@ class BuddyList(dict): buddy = self[number] self.backend.handleBuddyChanged(self.user, number, buddy.nick, buddy.groups, protocol_pb2.STATUS_NONE, - iconHash = buddy.image_hash if buddy.image_hash is not None else "") + iconHash = buddy.image_hash if buddy.image_hash is not None else u"") self.session.subscribePresence(number) - self.logger.debug("%s is requesting statuses of: %s", self.user, contacts) + self.logger.debug(u"%s is requesting statuses of: %s", self.user, contacts) self.session.requestStatuses(contacts, success = self.onStatus) def onStatus(self, contacts): - self.logger.debug("%s received statuses of: %s", self.user, contacts) + self.logger.debug(u"%s received statuses of: %s", self.user, contacts) for number, (status, time) in contacts.iteritems(): buddy = self[number] - buddy.statusMsg = status.decode('utf-8') + buddy.statusMsg = status.decode(u'utf-8') self.updateSpectrum(buddy) @@ -124,9 +124,9 @@ class BuddyList(dict): self.session.sendSync([number], delta = True, interactive = True) self.session.subscribePresence(number) self.session.requestStatuses([number], success = self.onStatus) - buddy = Buddy(self.owner, number, nick, "", groups, image_hash) + buddy = Buddy(self.owner, number, nick, u"", groups, image_hash) self[number] = buddy - self.logger.debug("Roster add: %s", buddy) + self.logger.debug(u"Roster add: %s", buddy) self.updateSpectrum(buddy) return buddy @@ -134,7 +134,7 @@ class BuddyList(dict): def updateSpectrum(self, buddy): if buddy.presence == 0: status = protocol_pb2.STATUS_NONE - elif buddy.presence == 'unavailable': + elif buddy.presence == u'unavailable': status = protocol_pb2.STATUS_AWAY else: status = protocol_pb2.STATUS_ONLINE @@ -142,18 +142,18 @@ class BuddyList(dict): 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) + statusmsg += time.strftime(u"\n Last seen: %a, %d %b %Y %H:%M:%S", timestamp) self.backend.handleBuddyChanged(self.user, buddy.number, buddy.nick, buddy.groups, status, statusMessage = statusmsg, - iconHash = buddy.image_hash if buddy.image_hash is not None else "") + iconHash = buddy.image_hash if buddy.image_hash is not None else u"") def remove(self, number): try: buddy = self[number] del self[number] - self.backend.handleBuddyChanged(self.user, number, "", [], + self.backend.handleBuddyChanged(self.user, number, u"", [], protocol_pb2.STATUS_NONE) self.backend.handleBuddyRemoved(self.user, number) self.session.unsubscribePresence(number) diff --git a/group.py b/group.py index 993bae4..4179a95 100644 --- a/group.py +++ b/group.py @@ -1,10 +1,10 @@ -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, Steffen Vogel" -__license__ = "GPLv3" -__maintainer__ = "Steffen Vogel" -__email__ = "post@steffenvogel.de" +__author__ = u"Steffen Vogel" +__copyright__ = u"Copyright 2015, Steffen Vogel" +__license__ = u"GPLv3" +__maintainer__ = u"Steffen Vogel" +__email__ = u"post@steffenvogel.de" -""" +u""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -34,12 +34,12 @@ class Group(): self.backend = backend self.user = user - self.nick = "me" + self.nick = u"me" # Participants is a number -> nickname dict self.participants = {} def addParticipants(self, participants, buddies, yourNumber): - """ + u""" Adds participants to the group. Args: @@ -48,14 +48,14 @@ class Group(): - yourNumber: The number you are using """ for jid in participants: - number = jid.split('@')[0] + number = jid.split(u'@')[0] try: nick = buddies[number].nick except KeyError: nick = number if number == yourNumber: nick = self.nick - if nick == "": + if nick == u"": nick = number self.participants[number] = nick @@ -72,7 +72,7 @@ class Group(): def removeParticipants(self, participants): for jid in participants: - number = jid.split('@')[0] + number = jid.split(u'@')[0] nick = self.participants[number] flags = protocol_pb2.PARTICIPANT_FLAG_NONE self._updateParticipant(number, flags, protocol_pb2.STATUS_NONE) @@ -88,7 +88,7 @@ class Group(): self._updateParticipant(number, flags, protocol_pb2.STATUS_ONLINE, new_nick) self.participants[number] = new_nick - def _updateParticipant(self, number, flags, status, newNick = ""): + def _updateParticipant(self, number, flags, status, newNick = u""): nick = self.participants[number] # Notice the status message is the buddy's number if self.joined: diff --git a/reader.py b/reader.py index 1137973..25dbf59 100644 --- a/reader.py +++ b/reader.py @@ -1,10 +1,10 @@ -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, Steffen Vogel" -__license__ = "GPLv3" -__maintainer__ = "Steffen Vogel" -__email__ = "post@steffenvogel.de" +__author__ = u"Steffen Vogel" +__copyright__ = u"Copyright 2015, Steffen Vogel" +__license__ = u"GPLv3" +__maintainer__ = u"Steffen Vogel" +__email__ = u"post@steffenvogel.de" -""" +u""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -24,19 +24,19 @@ __email__ = "post@steffenvogel.de" import time def get_token(number, timeout = 30): - file = open('tokens') + file = open(u'tokens') file.seek(-1, 2) count = 0 while count < timeout: line = file.readline() - if line in ["", "\n"]: + if line in [u"", u"\n"]: time.sleep(1) count += 1 continue else: - t, n, tk = line[:-1].split("\t") + t, n, tk = line[:-1].split(u"\t") if (n == number): file.close() @@ -45,4 +45,4 @@ def get_token(number, timeout = 30): file.close() -print get_token("4917696978528") +print get_token(u"4917696978528") diff --git a/session.py b/session.py index 5b3dd11..430bd93 100644 --- a/session.py +++ b/session.py @@ -1,10 +1,10 @@ -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, Steffen Vogel" -__license__ = "GPLv3" -__maintainer__ = "Steffen Vogel" -__email__ = "post@steffenvogel.de" +__author__ = u"Steffen Vogel" +__copyright__ = u"Copyright 2015, Steffen Vogel" +__license__ = u"GPLv3" +__maintainer__ = u"Steffen Vogel" +__email__ = u"post@steffenvogel.de" -""" +u""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -56,14 +56,14 @@ class Session(YowsupApp): def __init__(self, backend, user, legacyName, extra): super(Session, self).__init__() self.logger = logging.getLogger(self.__class__.__name__) - self.logger.info("Created: %s", legacyName) + self.logger.info(u"Created: %s", legacyName) self.backend = backend self.user = user self.legacyName = legacyName self.status = protocol_pb2.STATUS_NONE - self.statusMessage = '' + self.statusMessage = u'' self.groups = {} self.gotGroupList = False @@ -86,21 +86,21 @@ class Session(YowsupApp): self.bot = Bot(self) self.imgMsgId = None - self.imgPath = "" + self.imgPath = u"" self.imgBuddy = None - self.imgType = "" + self.imgType = u"" def __del__(self): # handleLogoutRequest self.logout() def logout(self): - self.logger.info("%s logged out", self.user) + self.logger.info(u"%s logged out", self.user) super(Session, self).logout() self.loggedIn = False def login(self, password): - self.logger.info("%s attempting login", self.user) + self.logger.info(u"%s attempting login", self.user) self.password = password self.shouldBeConncted = True super(Session, self).login(self.legacyName, self.password) @@ -108,33 +108,33 @@ class Session(YowsupApp): def _shortenGroupId(self, gid): # FIXME: might have problems if number begins with 0 return gid -# return '-'.join(hex(int(s))[2:] for s in gid.split('-')) +# return u'-'.join(hex(int(s))[2:] for s in gid.split(u'-')) def _lengthenGroupId(self, gid): return gid # FIXME: might have problems if number begins with 0 -# return '-'.join(str(int(s, 16)) for s in gid.split('-')) +# return u'-'.join(str(int(s, 16)) for s in gid.split(u'-')) def updateRoomList(self): rooms = [] text = [] for room, group in self.groups.iteritems(): rooms.append([self._shortenGroupId(room), group.subject]) - text.append(self._shortenGroupId(room) + '@' + self.backend.spectrum_jid + ' :' + group.subject) + text.append(self._shortenGroupId(room) + u'@' + self.backend.spectrum_jid + u' :' + group.subject) - self.logger.debug("Got rooms: %s", rooms) + self.logger.debug(u"Got rooms: %s", rooms) self.backend.handleRoomList(rooms) - message = "Note, you are a participant of the following groups:\n" +\ - '\n'.join(text) + '\nIf you do not join them you will lose messages' + message = u"Note, you are a participant of the following groups:\n" +\ + u'\n'.join(text) + u'\nIf you do not join them you will lose messages' #self.bot.send(message) def _updateGroups(self, response, request): - self.logger.debug('Received groups list %s', response) + self.logger.debug(u'Received groups list %s', response) groups = response.getGroups() for group in groups: room = group.getId() - owner = group.getOwner().split('@')[0] - subjectOwner = group.getSubjectOwner().split('@')[0] + owner = group.getOwner().split(u'@')[0] + subjectOwner = group.getSubjectOwner().split(u'@')[0] subject = utils.softToUni(group.getSubject()) if room in self.groups: @@ -144,7 +144,7 @@ class Session(YowsupApp): oroom.subject = subject else: self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) -# self.joinRoom(self._shortenGroupId(room), self.user.split("@")[0]) +# self.joinRoom(self._shortenGroupId(room), self.user.split(u"@")[0]) self.groups[room].addParticipants(group.getParticipants().keys(), self.buddies, self.legacyName) @@ -154,8 +154,8 @@ class Session(YowsupApp): while self.groupOfflineQueue[room]: msg = self.groupOfflineQueue[room].pop(0) self.backend.handleMessage(self.user, room, msg[1], - msg[0], "", msg[2]) - self.logger.debug("Send queued group message to: %s %s %s", + msg[0], u"", msg[2]) + self.logger.debug(u"Send queued group message to: %s %s %s", msg[0],msg[1], msg[2]) self.gotGroupList = True for room, nick in self.joinRoomQueue: @@ -169,7 +169,7 @@ class Session(YowsupApp): return room = self._lengthenGroupId(room) if room in self.groups: - self.logger.info("Joining room: %s room=%s, nick=%s", + self.logger.info(u"Joining room: %s room=%s, nick=%s", self.legacyName, room, nick) group = self.groups[room] @@ -184,24 +184,24 @@ class Session(YowsupApp): group.sendParticipantsToSpectrum(self.legacyName) self.backend.handleSubject(self.user, self._shortenGroupId(room), group.subject, ownerNick) - self.logger.debug("Room subject: room=%s, subject=%s", + self.logger.debug(u"Room subject: room=%s, subject=%s", room, group.subject) self.backend.handleRoomNicknameChanged( self.user, self._shortenGroupId(room), group.subject ) else: - self.logger.warn("Room doesn't exist: %s", room) + self.logger.warn(u"Room doesn't exist: %s", room) def leaveRoom(self, room): if room in self.groups: - self.logger.info("Leaving room: %s room=%s", self.legacyName, room) + self.logger.info(u"Leaving room: %s room=%s", self.legacyName, room) group = self.groups[room] group.joined = False else: - self.logger.warn("Room doesn't exist: %s. Unable to leave.", room) + self.logger.warn(u"Room doesn't exist: %s. Unable to leave.", room) def _lastSeen(self, number, seconds): - self.logger.debug("Last seen %s at %s seconds" % (number, str(seconds))) + self.logger.debug(u"Last seen %s at %s seconds" % (number, str(seconds))) if seconds < 60: self.onPresenceAvailable(number) else: @@ -210,43 +210,43 @@ class Session(YowsupApp): # Called by superclass def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t): - self.logger.info("Auth success: %s", self.user) + self.logger.info(u"Auth success: %s", self.user) self.backend.handleConnected(self.user) - self.backend.handleBuddyChanged(self.user, "bot", self.bot.name, - ["Admin"], protocol_pb2.STATUS_ONLINE) + self.backend.handleBuddyChanged(self.user, u"bot", self.bot.name, + [u"Admin"], protocol_pb2.STATUS_ONLINE) if self.initialized == False: self.sendOfflineMessages() - #self.bot.call("welcome") + #self.bot.call(u"welcome") self.initialized = True self.sendPresence(True) for func in self.loginQueue: func() - self.logger.debug('Requesting groups list') + self.logger.debug(u'Requesting groups list') self.requestGroupsList(self._updateGroups) self.loggedIn = True # Called by superclass def onAuthFailed(self, reason): - self.logger.info("Auth failed: %s (%s)", self.user, reason) + self.logger.info(u"Auth failed: %s (%s)", self.user, reason) self.backend.handleDisconnected(self.user, 0, reason) self.password = None self.loggedIn = False # Called by superclass def onDisconnect(self): - self.logger.debug('Disconnected') - self.backend.handleDisconnected(self.user, 0, 'Disconnected for unknown reasons') + self.logger.debug(u'Disconnected') + self.backend.handleDisconnected(self.user, 0, u'Disconnected for unknown reasons') # Called by superclass def onReceipt(self, _id, _from, timestamp, type, participant, offline, items): - self.logger.debug("received receipt, sending ack: " + - ' '.join(map(str, [_id, _from, timestamp, + self.logger.debug(u"received receipt, sending ack: " + + u' '.join(map(str, [_id, _from, timestamp, type, participant, offline, items])) ) try: - buddy = self.buddies[_from.split('@')[0]] + buddy = self.buddies[_from.split(u'@')[0]] #self.backend.handleBuddyChanged(self.user, buddy.number.number, # buddy.nick, buddy.groups, protocol_pb2.STATUS_ONLINE) self.backend.handleMessageAck(self.user, buddy.number, self.msgIDs[_id].xmppId) @@ -259,47 +259,47 @@ class Session(YowsupApp): # Called by superclass def onAck(self, _id, _class, _from, timestamp): - self.logger.debug('received ack ' + - ' '.join(map(str, [_id, _class, _from,timestamp,])) + self.logger.debug(u'received ack ' + + u' '.join(map(str, [_id, _class, _from,timestamp,])) ) # Called by superclass def onTextMessage(self, _id, _from, to, notify, timestamp, participant, offline, retry, body): - self.logger.debug('received TextMessage' + - ' '.join(map(str, [ + self.logger.debug(u'received TextMessage' + + u' '.join(map(str, [ _id, _from, to, notify, timestamp, participant, offline, retry, body ])) ) - buddy = _from.split('@')[0] + buddy = _from.split(u'@')[0] messageContent = utils.softToUni(body) self.sendReceipt(_id, _from, None, participant) - self.logger.info("Message received from %s to %s: %s (at ts=%s)", + self.logger.info(u"Message received from %s to %s: %s (at ts=%s)", buddy, self.legacyName, messageContent, timestamp) if participant is not None: # Group message - partname = participant.split('@')[0] + partname = participant.split(u'@')[0] self.sendGroupMessageToXMPP(buddy, partname, messageContent, timestamp) else: self.sendMessageToXMPP(buddy, messageContent, timestamp) - # isBroadcast always returns false, I'm not sure how to get a broadcast + # isBroadcast always returns false, Iu'm not sure how to get a broadcast # message. #if messageEntity.isBroadcast(): - # self.logger.info("Broadcast received from %s to %s: %s (at ts=%s)",\ + # self.logger.info(u"Broadcast received from %s to %s: %s (at ts=%s)",\ # buddy, self.legacyName, messageContent, timestamp) - # messageContent = "[Broadcast] " + messageContent + # messageContent = u"[Broadcast] " + messageContent # Called by superclass def onImage(self, image): - self.logger.debug('Received image message %s', str(image)) - buddy = image._from.split('@')[0] + self.logger.debug('Received image message %su', str(image)) + buddy = image._from.split('@u')[0] participant = image.participant if image.caption is None: - image.caption = '' - message = image.url + ' ' + image.caption + image.caption = 'u' + message = image.url + ' u' + image.caption if participant is not None: # Group message - partname = participant.split('@')[0] + partname = participant.split('@u')[0] self.sendGroupMessageToXMPP(buddy, partname, message, image.timestamp) else: @@ -308,12 +308,12 @@ class Session(YowsupApp): # Called by superclass def onAudio(self, audio): - self.logger.debug('Received audio message %s', str(audio)) - buddy = audio._from.split('@')[0] + self.logger.debug('Received audio message %su', str(audio)) + buddy = audio._from.split('@u')[0] participant = audio.participant message = audio.url if participant is not None: # Group message - partname = participant.split('@')[0] + partname = participant.split('@u')[0] self.sendGroupMessageToXMPP(buddy, partname, message, audio.timestamp) else: @@ -322,13 +322,13 @@ class Session(YowsupApp): # Called by superclass def onVideo(self, video): - self.logger.debug('Received video message %s', str(video)) - buddy = video._from.split('@')[0] + self.logger.debug('Received video message %su', str(video)) + buddy = video._from.split('@u')[0] participant = video.participant message = video.url if participant is not None: # Group message - partname = participant.split('@')[0] + partname = participant.split('@u')[0] self.sendGroupMessageToXMPP(buddy, partname, message, video.timestamp) else: @@ -336,57 +336,57 @@ class Session(YowsupApp): self.sendReceipt(video._id, video._from, None, video.participant) def onLocation(self, location): - buddy = location._from.split('@')[0] + buddy = location._from.split('@u')[0] latitude = location.getLatitude() longitude = location.getLongitude() url = location.getLocationUrl() participant = location.participant - self.logger.debug("Location received from %s: %s, %s", + self.logger.debug(u"Location received from %s: %s, %s", buddy, latitude, longitude) if participant is not None: # Group message - partname = participant.split('@')[0] + partname = participant.split('@u')[0] self.sendGroupMessageToXMPP(buddy, partname, url, location.timestamp) - self.sendGroupMessageToXMPP(buddy, partname, 'geo:' + latitude + ',' + longitude, + self.sendGroupMessageToXMPP(buddy, partname, 'geo:u' + latitude + ',u' + longitude, location.timestamp) else: self.sendMessageToXMPP(buddy, url, location.timestamp) - self.sendMessageToXMPP(buddy, 'geo:' + latitude + ',' + longitude, + self.sendMessageToXMPP(buddy, 'geo:u' + latitude + ',u' + longitude, location.timestamp) self.sendReceipt(location._id, location._from, None, location.participant, location.timestamp) # Called by superclass def onVCard(self, _id, _from, name, card_data, to, notify, timestamp, participant): - self.logger.debug('received VCard' + - ' '.join(map(str, [ + self.logger.debug('received VCardu' + + ' u'.join(map(str, [ _id, _from, name, card_data, to, notify, timestamp, participant ])) ) - buddy = _from.split("@")[0] + buddy = _from.split(u"@")[0] if participant is not None: # Group message - partname = participant.split('@')[0] - self.sendGroupMessageToXMPP(buddy, partname, "Received VCard (not implemented yet)", timestamp) + partname = participant.split('@u')[0] + self.sendGroupMessageToXMPP(buddy, partname, u"Received VCard (not implemented yet)", timestamp) else: - self.sendMessageToXMPP(buddy, "Received VCard (not implemented yet)") + self.sendMessageToXMPP(buddy, u"Received VCard (not implemented yet)") # self.sendMessageToXMPP(buddy, card_data) #self.transferFile(buddy, str(name), card_data) self.sendReceipt(_id, _from, None, participant) def transferFile(self, buddy, name, data): # Not working - self.logger.debug('transfering file %s', name) + self.logger.debug('transfering file %su', name) self.backend.handleFTStart(self.user, buddy, name, len(data)) self.backend.handleFTData(0, data) self.backend.handleFTFinish(self.user, buddy, name, len(data), 0) # Called by superclass def onContactTyping(self, buddy): - self.logger.info("Started typing: %s", buddy) - if buddy != 'bot': + self.logger.info(u"Started typing: %s", buddy) + if buddy != 'botu': self.sendPresence(True) self.backend.handleBuddyTyping(self.user, buddy) @@ -395,15 +395,15 @@ class Session(YowsupApp): # Called by superclass def onContactPaused(self, buddy): - self.logger.info("Paused typing: %s", buddy) - if buddy != 'bot': + self.logger.info(u"Paused typing: %s", buddy) + if buddy != 'botu': self.backend.handleBuddyTyped(self.user, buddy) self.timer = Timer(3, self.backend.handleBuddyStoppedTyping, (self.user, buddy)).start() # Called by superclass def onAddedToGroup(self, group): - self.logger.debug("Added to group: %s", group) + self.logger.debug(u"Added to group: %s", group) room = group.getGroupId() owner = group.getCreatorJid(full = False) subjectOwner = group.getSubjectOwnerJid(full = False) @@ -411,42 +411,42 @@ class Session(YowsupApp): self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) 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(u"You have been added to group: %s@%s (%s)" % (self._shortenGroupId(room), subject, self.backend.spectrum_jid)) # Called by superclass def onParticipantsAddedToGroup(self, group): - self.logger.debug("Participants added to group: %s", group) - room = group.getGroupId().split('@')[0] + self.logger.debug(u"Participants added to group: %s", group) + room = group.getGroupId().split('@u')[0] self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName) self.groups[room].sendParticipantsToSpectrum(self.legacyName) # Called by superclass def onParticipantsRemovedFromGroup(self, room, participants): - self.logger.debug("Participants removed from group: %s, %s", + self.logger.debug(u"Participants removed from group: %s, %s", room, participants) self.groups[room].removeParticipants(participants) def onPresenceReceived(self, _type, name, jid, lastseen): - self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) - buddy = jid.split("@")[0] + self.logger.info(u"Presence received: %s %s %s %s", _type, name, jid, lastseen) + buddy = jid.split(u"@")[0] try: buddy = self.buddies[buddy] except KeyError: - self.logger.error("Buddy not found: %s", buddy) + self.logger.error(u"Buddy not found: %s", buddy) return if (lastseen == str(buddy.lastseen)) and (_type == buddy.presence): return - if ((lastseen != "deny") and (lastseen != None) and (lastseen != "none")): + if ((lastseen != u"deny") and (lastseen != None) and (lastseen != u"none")): buddy.lastseen = int(lastseen) if (_type == None): buddy.lastseen = time.time() buddy.presence = _type - if _type == "unavailable": + if _type == u"unavailable": self.onPresenceUnavailable(buddy) else: self.onPresenceAvailable(buddy) @@ -454,17 +454,17 @@ class Session(YowsupApp): def onPresenceAvailable(self, buddy): - self.logger.info("Is available: %s", buddy) + self.logger.info(u"Is available: %s", buddy) self.buddies.updateSpectrum(buddy) def onPresenceUnavailable(self, buddy): - self.logger.info("Is unavailable: %s", buddy) + self.logger.info(u"Is unavailable: %s", buddy) self.buddies.updateSpectrum(buddy) # spectrum RequestMethods def sendTypingStarted(self, buddy): - if buddy != "bot": - self.logger.info("Started typing: %s to %s", self.legacyName, buddy) + if buddy != u"bot": + self.logger.info(u"Started typing: %s to %s", self.legacyName, buddy) self.sendTyping(buddy, True) # If he is typing he is present # I really don't know where else to put this. @@ -472,26 +472,26 @@ class Session(YowsupApp): self.sendPresence(True) def sendTypingStopped(self, buddy): - if buddy != "bot": - self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy) + if buddy != u"bot": + self.logger.info(u"Stopped typing: %s to %s", self.legacyName, buddy) self.sendTyping(buddy, False) def sendMessageToWA(self, sender, message, ID): - self.logger.info("Message sent from %s to %s: %s", + self.logger.info(u"Message sent from %s to %s: %s", self.legacyName, sender, message) - message = message.encode("utf-8") + message = message.encode(u"utf-8") # FIXME: Fragile, should pass this in to onDlerror self.dlerror_message = message self.dlerror_sender = sender self.dlerror_ID = ID # End Fragile - if sender == "bot": + if sender == u"bot": self.bot.parse(message) - elif "-" in sender: # group msg - if "/" in sender: # directed at single user - room, nick = sender.split("/") + elif u"-" in sender: # group msg + if u"/" in sender: # directed at single user + room, nick = sender.split(u"/") group = self.groups[room] number = None for othernumber, othernick in group.participants.iteritems(): @@ -499,99 +499,99 @@ class Session(YowsupApp): number = othernumber break if number is not None: - self.logger.debug("Private message sent from %s to %s", self.legacyName, number) - waId = self.sendTextMessage(number + '@s.whatsapp.net', message) + self.logger.debug(u"Private message sent from %s to %s", self.legacyName, number) + waId = self.sendTextMessage(number + u'@s.whatsapp.net', message) self.msgIDs[waId] = MsgIDs( ID, waId) else: - 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.error(u"Attempted to send private message to non-existent user") + self.logger.debug(u"%s to %s in %s", self.legacyName, nick, room) else: room = sender - if message[0] == '\\' and message[:1] != '\\\\': - self.logger.debug("Executing command %s in %s", message, room) + if message[0] == u'\\' and message[:1] != u'\\\\': + self.logger.debug(u"Executing command %s in %s", message, room) self.executeCommand(message, room) else: try: group = self.groups[self._lengthenGroupId(room)] - self.logger.debug("Group Message from %s to %s Groups: %s", + self.logger.debug(u"Group Message from %s to %s Groups: %s", group.nick , group , self.groups) self.backend.handleMessage( - self.user, room, message.decode('utf-8'), group.nick + self.user, room, message.decode(u'utf-8'), group.nick ) except KeyError: - self.logger.error('Group not found: %s', room) + self.logger.error(u'Group not found: %s', room) - if (".jpg" in message.lower()) or (".webp" in message.lower()): - if (".jpg" in message.lower()): - self.imgType = "jpg" - if (".webp" in message.lower()): - self.imgType = "webp" + if (u".jpg" in message.lower()) or (u".webp" in message.lower()): + if (u".jpg" in message.lower()): + self.imgType = u"jpg" + if (u".webp" in message.lower()): + self.imgType = u"webp" self.imgMsgId = ID - self.imgBuddy = room + "@g.us" + self.imgBuddy = room + u"@g.us" downloader = MediaDownloader(self.onDlsuccess, self.onDlerror) downloader.download(message) #self.imgMsgId = ID - #self.imgBuddy = room + "@g.us" - elif "geo:" in message.lower(): - self._sendLocation(room + "@g.us", message, ID) + #self.imgBuddy = room + u"@g.us" + elif u"geo:" in message.lower(): + self._sendLocation(room + u"@g.us", message, ID) else: - self.sendTextMessage(self._lengthenGroupId(room) + '@g.us', message) + self.sendTextMessage(self._lengthenGroupId(room) + u'@g.us', message) else: # private msg buddy = sender -# if message == "\\lastseen": -# self.call("presence_request", buddy = (buddy + "@s.whatsapp.net",)) +# if message == u"\\lastseen": +# self.call(u"presence_request", buddy = (buddy + u"@s.whatsapp.net",)) # else: - if message.split(" ").pop(0) == "\\lastseen": + if message.split(u" ").pop(0) == u"\\lastseen": self.presenceRequested.append(buddy) - #self.call("presence_request", (buddy + "@s.whatsapp.net",)) + #self.call(u"presence_request", (buddy + u"@s.whatsapp.net",)) self._requestLastSeen(buddy) - elif message.split(" ").pop(0) == "\\gpp": - self.logger.info("Get Profile Picture! ") - self.sendMessageToXMPP(buddy, "Fetching Profile Picture") - #self.call("contact_getProfilePicture", (buddy + "@s.whatsapp.net",)) + elif message.split(u" ").pop(0) == u"\\gpp": + self.logger.info(u"Get Profile Picture! ") + self.sendMessageToXMPP(buddy, u"Fetching Profile Picture") + #self.call(u"contact_getProfilePicture", (buddy + u"@s.whatsapp.net",)) self.requestVCard(buddy) else: - if (".jpg" in message.lower()) or (".webp" in message.lower()): - #waId = self.call("message_imageSend", (buddy + "@s.whatsapp.net", message, None, 0, None)) - #waId = self.call("message_send", (buddy + "@s.whatsapp.net", message)) - if (".jpg" in message.lower()): - self.imgType = "jpg" - if (".webp" in message.lower()): - self.imgType = "webp" + if (u".jpg" in message.lower()) or (u".webp" in message.lower()): + #waId = self.call(u"message_imageSend", (buddy + u"@s.whatsapp.net", message, None, 0, None)) + #waId = self.call(u"message_send", (buddy + u"@s.whatsapp.net", message)) + if (u".jpg" in message.lower()): + self.imgType = u"jpg" + if (u".webp" in message.lower()): + self.imgType = u"webp" self.imgMsgId = ID - self.imgBuddy = buddy + "@s.whatsapp.net" + self.imgBuddy = buddy + u"@s.whatsapp.net" downloader = MediaDownloader(self.onDlsuccess, self.onDlerror) downloader.download(message) #self.imgMsgId = ID - #self.imgBuddy = buddy + "@s.whatsapp.net" - elif "geo:" in message.lower(): - self._sendLocation(buddy + "@s.whatsapp.net", message, ID) + #self.imgBuddy = buddy + u"@s.whatsapp.net" + elif u"geo:" in message.lower(): + self._sendLocation(buddy + u"@s.whatsapp.net", message, ID) else: - waId = self.sendTextMessage(sender + '@s.whatsapp.net', message) + waId = self.sendTextMessage(sender + u'@s.whatsapp.net', message) self.msgIDs[waId] = MsgIDs( ID, waId) - self.logger.info("WA Message send to %s with ID %s", buddy, waId) - #self.sendTextMessage(sender + '@s.whatsapp.net', message) + self.logger.info(u"WA Message send to %s with ID %s", buddy, waId) + #self.sendTextMessage(sender + u'@s.whatsapp.net', message) def executeCommand(self, command, room): - if command == '\\leave': - self.logger.debug("Leaving room %s", room) + if command == u'\\leave': + self.logger.debug(u"Leaving room %s", room) # Leave group on whatsapp side self.leaveGroup(room) # Delete Room on spectrum side group = self.groups[room] for jid in group.participants: - buddy = jid.split("@")[0] + buddy = jid.split(u"@")[0] try: nick = self.buddies[buddy].nick except KeyError: nick = buddy - if nick == "": + if nick == u"": nick = buddy if buddy == self.legacyName: nick = group.nick @@ -605,43 +605,43 @@ class Session(YowsupApp): def onSuccess(buddy, lastseen): timestamp = time.localtime(time.localtime()-lastseen) - timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp) - self.sendMessageToXMPP(buddy, "%s (%s) %s" % (timestring, utils.ago(lastseen),str(lastseen))) + timestring = time.strftime(u"%a, %d %b %Y %H:%M:%S", timestamp) + self.sendMessageToXMPP(buddy, u"%s (%s) %s" % (timestring, utils.ago(lastseen),str(lastseen))) def onError(errorIqEntity, originalIqEntity): - self.sendMessageToXMPP(errorIqEntity.getFrom(), "LastSeen Error") + self.sendMessageToXMPP(errorIqEntity.getFrom(), u"LastSeen Error") self.requestLastSeen(buddy, onSuccess, onError) def _sendLocation(self, buddy, message, ID): - #with open('/opt/transwhat/map.jpg', 'rb') as imageFile: + #with open(u'/opt/transwhat/map.jpg', u'rb') as imageFile: # raw = base64.b64encode(imageFile.read()) - latitude,longitude = message.split(':')[1].split(',') + latitude,longitude = message.split(u':')[1].split(u',') waId = self.sendLocation(buddy, float(latitude), float(longitude)) self.msgIDs[waId] = MsgIDs( ID, waId) - self.logger.info("WA Location Message send to %s with ID %s", buddy, waId) + self.logger.info(u"WA Location Message send to %s with ID %s", buddy, waId) - def sendMessageToXMPP(self, buddy, messageContent, timestamp = "", nickname = ""): + def sendMessageToXMPP(self, buddy, messageContent, timestamp = u"", nickname = u""): if timestamp: - timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp)) + timestamp = time.strftime(u"%Y%m%dT%H%M%S", time.gmtime(timestamp)) if self.initialized == False: - self.logger.debug("Message queued from %s to %s: %s", + self.logger.debug(u"Message queued from %s to %s: %s", buddy, self.legacyName, messageContent) self.offlineQueue.append((buddy, messageContent, timestamp)) else: - self.logger.debug("Message sent from %s to %s: %s", buddy, + self.logger.debug(u"Message sent from %s to %s: %s", buddy, self.legacyName, messageContent) - self.backend.handleMessage(self.user, buddy, messageContent, "", - "", timestamp) + self.backend.handleMessage(self.user, buddy, messageContent, u"", + u"", timestamp) - def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = ""): + def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = u""): if timestamp: - timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp)) + timestamp = time.strftime(u"%Y%m%dT%H%M%S", time.gmtime(timestamp)) if self.initialized == False: - self.logger.debug("Group message queued from %s to %s: %s", + self.logger.debug(u"Group message queued from %s to %s: %s", number, room, messageContent) if room not in self.groupOfflineQueue: @@ -651,34 +651,34 @@ class Session(YowsupApp): (number, messageContent, timestamp) ) else: - self.logger.debug("Group message sent from %s to %s: %s", + self.logger.debug(u"Group message sent from %s to %s: %s", number, room, messageContent) try: group = self.groups[room] # Update nickname try: - if self.buddies[number].nick != "": + if self.buddies[number].nick != u"": group.changeNick(number, self.buddies[number].nick) except KeyError: pass nick = group.participants[number] if group.joined: self.backend.handleMessage(self.user, room, messageContent, - nick, "", timestamp) + nick, u"", timestamp) else: - self.bot.send("You have received a message in group: %s@%s" + self.bot.send(u"You have received a message in group: %s@%s" % (room, self.backend.spectrum_jid)) - self.bot.send("Join the group in order to reply") - self.bot.send("%s: %s" % (nick, messageContent)) + self.bot.send(u"Join the group in order to reply") + self.bot.send(u"%s: %s" % (nick, messageContent)) except KeyError: - self.logger.warn("Group is not in group list") + self.logger.warn(u"Group is not in group list") self.backend.handleMessage(self.user, self._shortenGroupId(room), - messageContent, number, "", timestamp) + messageContent, number, u"", timestamp) def changeStatus(self, status): if status != self.status: - self.logger.info("Status changed: %s", status) + self.logger.info(u"Status changed: %s", status) self.status = status if status == protocol_pb2.STATUS_ONLINE \ @@ -690,19 +690,19 @@ class Session(YowsupApp): def changeStatusMessage(self, statusMessage): if (statusMessage != self.statusMessage) or (self.initialized == False): self.statusMessage = statusMessage - self.setStatus(statusMessage.encode('utf-8')) - self.logger.info("Status message changed: %s", statusMessage) + self.setStatus(statusMessage.encode(u'utf-8')) + self.logger.info(u"Status message changed: %s", statusMessage) #if self.initialized == False: # self.sendOfflineMessages() - # self.bot.call("welcome") + # self.bot.call(u"welcome") # self.initialized = True def sendOfflineMessages(self): # Flush Queues while self.offlineQueue: msg = self.offlineQueue.pop(0) - self.backend.handleMessage(self.user, msg[0], msg[1], "", "", msg[2]) + self.backend.handleMessage(self.user, msg[0], msg[1], u"", u"", msg[2]) # Called when user logs in to initialize the roster def loadBuddies(self, buddies): @@ -710,56 +710,56 @@ class Session(YowsupApp): # also for adding a new buddy def updateBuddy(self, buddy, nick, groups, image_hash = None): - if buddy != "bot": + if buddy != u"bot": self.buddies.update(buddy, nick, groups, image_hash) def removeBuddy(self, buddy): - if buddy != "bot": - self.logger.info("Buddy removed: %s", buddy) + if buddy != u"bot": + self.logger.info(u"Buddy removed: %s", buddy) self.buddies.remove(buddy) def requestVCard(self, buddy, ID=None): def onSuccess(response, request): - self.logger.debug('Sending VCard (%s) with image id %s', + self.logger.debug(u'Sending VCard (%s) with image id %s', ID, response.pictureId) image_hash = utils.sha1hash(response.pictureData) - self.logger.debug('Image hash is %s', image_hash) + self.logger.debug(u'Image hash is %s', image_hash) if ID != None: - self.backend.handleVCard(self.user, ID, buddy, "", "", response.pictureData) + self.backend.handleVCard(self.user, ID, buddy, u"", u"", response.pictureData) obuddy = self.buddies[buddy] self.updateBuddy(buddy, obuddy.nick, obuddy.groups, image_hash) - self.logger.debug('Requesting profile picture of %s', buddy) + self.logger.debug(u'Requesting profile picture of %s', buddy) self.requestProfilePicture(buddy, onSuccess = onSuccess) def onDlsuccess(self, path): - self.logger.info("Success: Image downloaded to %s", path) - os.rename(path, path+"."+self.imgType) - if self.imgType != "jpg": - im = Image.open(path+"."+self.imgType) - im.save(path+".jpg") - self.imgPath = path+".jpg" + self.logger.info(u"Success: Image downloaded to %s", path) + os.rename(path, path+u"."+self.imgType) + if self.imgType != u"jpg": + im = Image.open(path+u"."+self.imgType) + im.save(path+u".jpg") + self.imgPath = path+u".jpg" statinfo = os.stat(self.imgPath) name=os.path.basename(self.imgPath) - self.logger.info("Buddy %s",self.imgBuddy) + self.logger.info(u"Buddy %s",self.imgBuddy) self.image_send(self.imgBuddy, self.imgPath) - #self.logger.info("Sending picture %s of size %s with name %s",self.imgPath, statinfo.st_size, name) - #mtype = "image" + #self.logger.info(u"Sending picture %s of size %s with name %s",self.imgPath, statinfo.st_size, name) + #mtype = u"image" #sha1 = hashlib.sha256() - #fp = open(self.imgPath, 'rb') + #fp = open(self.imgPath, u'rb') #try: # sha1.update(fp.read()) # hsh = base64.b64encode(sha1.digest()) - # self.call("media_requestUpload", (hsh, mtype, os.path.getsize(self.imgPath))) + # self.call(u"media_requestUpload", (hsh, mtype, os.path.getsize(self.imgPath))) #finally: # fp.close() def onDlerror(self): - self.logger.info("Download Error. Sending message as is.") - waId = self.sendTextMessage(self.dlerror_sender + '@s.whatsapp.net', self.dlerror_message) + self.logger.info(u"Download Error. Sending message as is.") + waId = self.sendTextMessage(self.dlerror_sender + u'@s.whatsapp.net', self.dlerror_message) self.msgIDs[waId] = MsgIDs(self.dlerror_ID, waId) @@ -777,7 +777,7 @@ class Session(YowsupApp): def createThumb(self, size=100, raw=False): img = Image.open(self.imgPath) width, height = img.size - img_thumbnail = self.imgPath + '_thumbnail' + img_thumbnail = self.imgPath + u'_thumbnail' if width > height: nheight = float(height) / width * size @@ -787,47 +787,47 @@ class Session(YowsupApp): nheight = size img.thumbnail((nwidth, nheight), Image.ANTIALIAS) - img.save(img_thumbnail, 'JPEG') + img.save(img_thumbnail, u'JPEG') - with open(img_thumbnail, 'rb') as imageFile: + with open(img_thumbnail, u'rb') as imageFile: raw = base64.b64encode(imageFile.read()) return raw # Not used def onLocationReceived(self, messageId, jid, name, preview, latitude, longitude, receiptRequested, isBroadcast): - buddy = jid.split("@")[0] - self.logger.info("Location received from %s: %s, %s", buddy, latitude, longitude) + buddy = jid.split(u"@")[0] + self.logger.info(u"Location received from %s: %s, %s", buddy, latitude, longitude) - url = "http://maps.google.de?%s" % urllib.urlencode({ "q": "%s %s" % (latitude, longitude) }) + url = u"http://maps.google.de?%s" % urllib.urlencode({ u"q": u"%s %s" % (latitude, longitude) }) self.sendMessageToXMPP(buddy, utils.shorten(url)) - if receiptRequested: self.call("message_ack", (jid, messageId)) + if receiptRequested: self.call(u"message_ack", (jid, messageId)) def onGroupSubjectReceived(self, messageId, gjid, jid, subject, timestamp, receiptRequested): - room = gjid.split("@")[0] - buddy = jid.split("@")[0] + room = gjid.split(u"@")[0] + buddy = jid.split(u"@")[0] self.backend.handleSubject(self.user, room, subject, buddy) - if receiptRequested: self.call("subject_ack", (gjid, messageId)) + if receiptRequested: self.call(u"subject_ack", (gjid, messageId)) # Yowsup Notifications def onGroupParticipantRemoved(self, gjid, jid, author, timestamp, messageId, receiptRequested): - room = gjid.split("@")[0] - buddy = jid.split("@")[0] + room = gjid.split(u"@")[0] + buddy = jid.split(u"@")[0] - self.logger.info("Removed %s from room %s", buddy, room) + self.logger.info(u"Removed %s from room %s", buddy, room) 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(u"notification_ack", (gjid, messageId)) def onContactProfilePictureUpdated(self, jid, timestamp, messageId, pictureId, receiptRequested): # TODO - if receiptRequested: self.call("notification_ack", (jid, messageId)) + if receiptRequested: self.call(u"notification_ack", (jid, messageId)) def onGroupPictureUpdated(self, jid, author, timestamp, messageId, pictureId, receiptRequested): # TODO - if receiptRequested: self.call("notification_ack", (jid, messageId)) + if receiptRequested: self.call(u"notification_ack", (jid, messageId)) diff --git a/transwhat.py b/transwhat.py index 935b185..cb07119 100755 --- a/transwhat.py +++ b/transwhat.py @@ -1,12 +1,12 @@ #!/usr/bin/python -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, Steffen Vogel" -__license__ = "GPLv3" -__maintainer__ = "Steffen Vogel" -__email__ = "post@steffenvogel.de" +__author__ = u"Steffen Vogel" +__copyright__ = u"Copyright 2015, Steffen Vogel" +__license__ = u"GPLv3" +__maintainer__ = u"Steffen Vogel" +__email__ = u"post@steffenvogel.de" -""" +u""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -42,21 +42,21 @@ from yowsup.stacks import YowStack # Arguments parser = argparse.ArgumentParser() -parser.add_argument('--debug', action='store_true') -parser.add_argument('--host', type=str, 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('config', type=str) -parser.add_argument('-j', type=str, required=True) +parser.add_argument(u'--debug', action=u'store_true') +parser.add_argument(u'--host', type=str, required=True) +parser.add_argument(u'--port', type=int, required=True) +parser.add_argument(u'--service.backend_id', metavar=u"ID", type=int, required=True) +parser.add_argument(u'config', type=str) +parser.add_argument(u'-j', type=str, required=True) args, unknown = parser.parse_known_args() -YowConstants.PATH_STORAGE='/var/lib/spectrum2/' + args.j -loggingfile = '/var/log/spectrum2/' + args.j + '/backends/backend.log' +YowConstants.PATH_STORAGE=u'/var/lib/spectrum2/' + args.j +loggingfile = u'/var/log/spectrum2/' + args.j + u'/backends/backend.log' # Logging logging.basicConfig( \ filename=loggingfile,\ - format = "%(asctime)-15s %(levelname)s %(name)s: %(message)s", \ + format = u"%(asctime)-15s %(levelname)s %(name)s: %(message)s", \ level = logging.DEBUG if args.debug else logging.INFO \ ) @@ -77,9 +77,9 @@ io = IOChannel(args.host, args.port, handleTransportData, connectionClosed) plugin = WhatsAppBackend(io, args.j) plugin.handleBackendConfig({ - 'features': [ - ('send_buddies_on_login', 1), - ('muc', 'true'), + u'features': [ + (u'send_buddies_on_login', 1), + (u'muc', u'true'), ], }) @@ -87,7 +87,7 @@ while True: try: asyncore.loop(timeout=1.0, count=10, use_poll = True) try: - callback = YowStack._YowStack__detachedQueue.get(False) #doesn't block + callback = YowStack._YowStack__detachedQueue.get(False) #doesnu't block callback() except Queue.Empty: pass diff --git a/utils.py b/utils.py index f6d120c..e53d7fe 100644 --- a/utils.py +++ b/utils.py @@ -1,10 +1,10 @@ -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, Steffen Vogel" -__license__ = "GPLv3" -__maintainer__ = "Steffen Vogel" -__email__ = "post@steffenvogel.de" +__author__ = u"Steffen Vogel" +__copyright__ = u"Copyright 2015, Steffen Vogel" +__license__ = u"GPLv3" +__maintainer__ = u"Steffen Vogel" +__email__ = u"post@steffenvogel.de" -""" +u""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ import base64 import hashlib def ago(secs): - periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"] + periods = [u"second", u"minute", u"hour", u"day", u"week", u"month", u"year", u"decade"] lengths = [60, 60, 24, 7,4.35, 12, 10] j = 0 @@ -38,16 +38,16 @@ def ago(secs): j += 1 period = periods[j] - if diff > 1: period += "s" + if diff > 1: period += u"s" - return "%d %s ago" % (diff, period) + return u"%d %s ago" % (diff, period) def softToUni(message): - message = message.decode("utf-8") + message = message.decode(u"utf-8") return e4u.translate(message, reverse=False, **e4u.SOFTBANK_TRANSLATE_PROFILE) def decodePassword(password): - return base64.b64decode(bytes(password.encode("utf-8"))) + return base64.b64decode(bytes(password.encode(u"utf-8"))) def sha1hash(data): return hashlib.sha1(data).hexdigest() diff --git a/whatsappbackend.py b/whatsappbackend.py index a2ddc9e..7ef2c13 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -1,10 +1,10 @@ -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, Steffen Vogel" -__license__ = "GPLv3" -__maintainer__ = "Steffen Vogel" -__email__ = "post@steffenvogel.de" +__author__ = u"Steffen Vogel" +__copyright__ = u"Copyright 2015, Steffen Vogel" +__license__ = u"GPLv3" +__maintainer__ = u"Steffen Vogel" +__email__ = u"post@steffenvogel.de" -""" +u""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -38,11 +38,11 @@ class WhatsAppBackend(SpectrumBackend): # Used to prevent duplicate messages self.lastMessage = {} - self.logger.debug("Backend started") + self.logger.debug(u"Backend started") # RequestsHandlers def handleLoginRequest(self, user, legacyName, password, extra): - self.logger.debug("handleLoginRequest(user=%s, legacyName=%s)", user, legacyName) + self.logger.debug(u"handleLoginRequest(user=%s, legacyName=%s)", user, legacyName) if user not in self.sessions: self.sessions[user] = Session(self, user, legacyName, extra) @@ -52,13 +52,13 @@ class WhatsAppBackend(SpectrumBackend): self.sessions[user].login(password) def handleLogoutRequest(self, user, legacyName): - self.logger.debug("handleLogoutRequest(user=%s, legacyName=%s)", user, legacyName) + self.logger.debug(u"handleLogoutRequest(user=%s, legacyName=%s)", user, legacyName) if user in self.sessions: self.sessions[user].logout() del self.sessions[user] - def handleMessageSendRequest(self, user, buddy, message, xhtml = "", ID = 0): - self.logger.debug("handleMessageSendRequest(user=%s, buddy=%s, message=%s, xhtml = %s)", user, buddy, message, xhtml) + def handleMessageSendRequest(self, user, buddy, message, xhtml = u"", ID = 0): + self.logger.debug(u"handleMessageSendRequest(user=%s, buddy=%s, message=%s, xhtml = %s)", user, buddy, message, xhtml) # For some reason spectrum occasionally sends to identical messages to # a buddy, one to the bare jid and one to /bot. This causes duplicate # messages. Since it is unlikely a user wants to send the same message @@ -73,48 +73,48 @@ class WhatsAppBackend(SpectrumBackend): usersMessage[buddy] = message def handleJoinRoomRequest(self, user, room, nickname, pasword): - self.logger.debug("handleJoinRoomRequest(user=%s, room=%s, nickname=%s)", user, room, nickname) + self.logger.debug(u"handleJoinRoomRequest(user=%s, room=%s, nickname=%s)", user, room, nickname) self.sessions[user].joinRoom(room, nickname) def handleLeaveRoomRequest(self, user, room): - self.logger.debug("handleLeaveRoomRequest(user=%s, room=%s)", user, room) + self.logger.debug(u"handleLeaveRoomRequest(user=%s, room=%s)", user, room) self.sessions[user].leaveRoom(room) def handleStatusChangeRequest(self, user, status, statusMessage): - self.logger.debug("handleStatusChangeRequest(user=%s, status=%d, statusMessage=%s)", user, status, statusMessage) + self.logger.debug(u"handleStatusChangeRequest(user=%s, status=%d, statusMessage=%s)", user, status, statusMessage) self.sessions[user].changeStatusMessage(statusMessage) self.sessions[user].changeStatus(status) def handleBuddies(self, buddies): - """Called when user logs in. Used to initialize roster.""" - self.logger.debug("handleBuddies(buddies=%s)", buddies) + u"""Called when user logs in. Used to initialize roster.""" + self.logger.debug(u"handleBuddies(buddies=%s)", buddies) buddies = [b for b in buddies.buddy] if len(buddies) > 0: user = buddies[0].userName self.sessions[user].loadBuddies(buddies) 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(u"handleBuddyUpdatedRequest(user=%s, buddy=%s, nick=%s, groups=%s)", user, buddy, nick, str(groups)) self.sessions[user].updateBuddy(buddy, nick, groups) def handleBuddyRemovedRequest(self, user, buddy, groups): - self.logger.debug("handleBuddyRemovedRequest(user=%s, buddy=%s, groups=%s)", user, buddy, str(groups)) + self.logger.debug(u"handleBuddyRemovedRequest(user=%s, buddy=%s, groups=%s)", user, buddy, str(groups)) self.sessions[user].removeBuddy(buddy) def handleTypingRequest(self, user, buddy): - self.logger.debug("handleTypingRequest(user=%s, buddy=%s)", user, buddy) + self.logger.debug(u"handleTypingRequest(user=%s, buddy=%s)", user, buddy) self.sessions[user].sendTypingStarted(buddy) def handleTypedRequest(self, user, buddy): - self.logger.debug("handleTypedRequest(user=%s, buddy=%s)", user, buddy) + self.logger.debug(u"handleTypedRequest(user=%s, buddy=%s)", user, buddy) self.sessions[user].sendTypingStopped(buddy) def handleStoppedTypingRequest(self, user, buddy): - self.logger.debug("handleStoppedTypingRequest(user=%s, buddy=%s)", user, buddy) + self.logger.debug(u"handleStoppedTypingRequest(user=%s, buddy=%s)", user, buddy) self.sessions[user].sendTypingStopped(buddy) def handleVCardRequest(self, user, buddy, ID): - self.logger.debug("handleVCardRequest(user=%s, buddy=%s, ID=%s)", user, buddy, ID) + self.logger.debug(u"handleVCardRequest(user=%s, buddy=%s, ID=%s)", user, buddy, ID) self.sessions[user].requestVCard(buddy, ID) @@ -129,7 +129,7 @@ class WhatsAppBackend(SpectrumBackend): pass 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(u'File send request %s, for user %s, from %s, size: %s', fileName, user, buddy, size) def handleFTFinishRequest(self, user, buddy, fileName, size, ftID): @@ -145,7 +145,7 @@ class WhatsAppBackend(SpectrumBackend): pass def handleMessageAckRequest(self, user, legacyName, ID = 0): - self.logger.info("Meassage ACK request for %s !!",legacyName) + self.logger.info(u"Meassage ACK request for %s !!",legacyName) def sendData(self, data): self.io.sendData(data) diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 048ba98..df08b98 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -78,7 +78,7 @@ class YowsupApp(object): ) def login(self, username, password): - """Login to yowsup + u"""Login to yowsup Should result in onAuthSuccess or onAuthFailure to be called. @@ -102,20 +102,20 @@ class YowsupApp(object): self.stack.broadcastEvent( YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT)) except TypeError as e: # Occurs when password is not correctly formated - self.onAuthFailure('password not base64 encoded') + self.onAuthFailure(u'password not base64 encoded') # try: # self.stack.loop(timeout=0.5, discrete=0.5) # except AuthError as e: # For some reason Yowsup throws an exception -# self.onAuthFailure("%s" % e) +# self.onAuthFailure(u"%s" % e) def logout(self): - """ + u""" Logout from whatsapp """ self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT)) def sendReceipt(self, _id, _from, read, participant): - """ + u""" Send a receipt (delivered: double-tick, read: blue-ticks) Args: @@ -128,7 +128,7 @@ class YowsupApp(object): self.sendEntity(receipt) def sendTextMessage(self, to, message): - """ + u""" Sends a text message Args: @@ -140,7 +140,7 @@ class YowsupApp(object): return messageEntity.getId() def sendLocation(self, to, latitude, longitude): - messageEntity = LocationMediaMessageProtocolEntity(latitude,longitude, None, None, "raw", to = to) + messageEntity = LocationMediaMessageProtocolEntity(latitude,longitude, None, None, u"raw", to = to) self.sendEntity(messageEntity) return messageEntity.getId() @@ -170,14 +170,14 @@ class YowsupApp(object): mediaUploader.start() def onRequestUploadError(self, jid, path, errorRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity): - self.logger.error("Request upload for file %s for %s failed" % (path, jid)) + self.logger.error(u"Request upload for file %s for %s failed" % (path, jid)) def onUploadError(self, filePath, jid, url): - #logger.error("Upload file %s to %s for %s failed!" % (filePath, url, jid)) - self.logger.error("Upload Error!") + #logger.error(u"Upload file %s to %s for %s failed!" % (filePath, url, jid)) + self.logger.error(u"Upload Error!") def onUploadProgress(self, filePath, jid, url, progress): - #sys.stdout.write("%s => %s, %d%% \r" % (os.path.basename(filePath), jid, progress)) + #sys.stdout.write(u"%s => %s, %d%% \r" % (os.path.basename(filePath), jid, progress)) #sys.stdout.flush() pass @@ -197,7 +197,7 @@ class YowsupApp(object): def sendPresence(self, available): - """ + u""" Send presence to whatsapp Args: @@ -209,42 +209,42 @@ class YowsupApp(object): self.sendEntity(UnavailablePresenceProtocolEntity()) def subscribePresence(self, phone_number): - """ + u""" Subscribe to presence updates from phone_number Args: - phone_number: (str) The cellphone number of the person to subscribe to """ - self.logger.debug("Subscribing to Presence updates from %s", (phone_number)) - jid = phone_number + '@s.whatsapp.net' + self.logger.debug(u"Subscribing to Presence updates from %s", (phone_number)) + jid = phone_number + u'@s.whatsapp.net' entity = SubscribePresenceProtocolEntity(jid) self.sendEntity(entity) def unsubscribePresence(self, phone_number): - """ + u""" Unsubscribe to presence updates from phone_number Args: - phone_number: (str) The cellphone number of the person to unsubscribe from """ - jid = phone_number + '@s.whatsapp.net' + jid = phone_number + u'@s.whatsapp.net' entity = UnsubscribePresenceProtocolEntity(jid) self.sendEntity(entity) def leaveGroup(self, group): - """ + u""" Permanently leave a WhatsApp group Args: - group: (str) the group id (e.g. 27831788123-144024456) """ - entity = LeaveGroupsIqProtocolEntity([group + '@g.us']) + entity = LeaveGroupsIqProtocolEntity([group + u'@g.us']) self.sendEntity(entity) def setStatus(self, statusText): - """ + u""" Send status to whatsapp Args: @@ -254,14 +254,14 @@ class YowsupApp(object): self.sendIq(iq) def sendTyping(self, phoneNumber, typing): - """ + u""" Notify buddy using phoneNumber that you are typing to him Args: - phoneNumber: (str) cellphone number of the buddy you are typing to. - typing: (bool) True if you are typing, False if you are not """ - jid = phoneNumber + '@s.whatsapp.net' + jid = phoneNumber + u'@s.whatsapp.net' if typing: state = OutgoingChatstateProtocolEntity( ChatstateProtocolEntity.STATE_TYPING, jid @@ -273,7 +273,7 @@ class YowsupApp(object): self.sendEntity(state) def sendSync(self, contacts, delta = False, interactive = True): - """ + u""" You need to sync new contacts before you interact with them, failure to do so could result in a temporary ban. @@ -293,7 +293,7 @@ class YowsupApp(object): self.sendIq(iq) def requestStatuses(self, contacts, success = None, failure = None): - """ + u""" Request the statuses of a number of users. Args: @@ -302,19 +302,19 @@ class YowsupApp(object): - success: (func) called when request is successful - failure: (func) called when request has failed """ - iq = GetStatusesIqProtocolEntity([c + '@s.whatsapp.net' for c in contacts]) + iq = GetStatusesIqProtocolEntity([c + u'@s.whatsapp.net' for c in contacts]) def onSuccess(response, request): - self.logger.debug("Received Statuses %s", response) + self.logger.debug(u"Received Statuses %s", response) s = {} for k, v in response.statuses.iteritems(): - s[k.split('@')[0]] = v + s[k.split(u'@')[0]] = v success(s) self.sendIq(iq, onSuccess = onSuccess, onError = failure) def requestLastSeen(self, phoneNumber, success = None, failure = None): - """ + u""" Requests when user was last seen. Args: - phone_number: (str) the phone number of the user @@ -323,22 +323,22 @@ class YowsupApp(object): since last seen. - failure: (func) called when request has failed """ - iq = LastseenIqProtocolEntity(phoneNumber + '@s.whatsapp.net') + iq = LastseenIqProtocolEntity(phoneNumber + u'@s.whatsapp.net') self.sendIq(iq, onSuccess = partial(self._lastSeenSuccess, success), onError = failure) def _lastSeenSuccess(self, success, response, request): - success(response._from.split('@')[0], response.seconds) + success(response._from.split(u'@')[0], response.seconds) def requestProfilePicture(self, phoneNumber, onSuccess = None, onFailure = None): - """ + u""" Requests profile picture of whatsapp user Args: - phoneNumber: (str) the phone number of the user - onSuccess: (func) called when request is successfully processed. - onFailure: (func) called when request has failed """ - iq = GetPictureIqProtocolEntity(phoneNumber + '@s.whatsapp.net') + iq = GetPictureIqProtocolEntity(phoneNumber + u'@s.whatsapp.net') self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def requestGroupsList(self, onSuccess = None, onFailure = None): @@ -346,7 +346,7 @@ class YowsupApp(object): self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def requestGroupInfo(self, group, onSuccess = None, onFailure = None): - """ + u""" Request info on a specific group (includes participants, subject, owner etc.) Args: @@ -354,11 +354,11 @@ class YowsupApp(object): - onSuccess: (func) called when request is successfully processed. - onFailure: (func) called when request is has failed """ - iq = InfoGroupsIqProtocolEntity(group + '@g.us') + iq = InfoGroupsIqProtocolEntity(group + u'@g.us') self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t): - """ + u""" Called when login is successful. Args: @@ -373,7 +373,7 @@ class YowsupApp(object): pass def onAuthFailure(self, reason): - """ + u""" Called when login is a failure Args: @@ -382,7 +382,7 @@ class YowsupApp(object): pass def onReceipt(self, _id, _from, timestamp, type, participant, offline, items): - """ + u""" Called when a receipt is received (double tick or blue tick) Args @@ -398,7 +398,7 @@ class YowsupApp(object): pass def onAck(self, _id,_class, _from, timestamp): - """ + u""" Called when Ack is received Args: @@ -410,7 +410,7 @@ class YowsupApp(object): pass def onPresenceReceived(self, _type, name, _from, last): - """ + u""" Called when presence (e.g. available, unavailable) is received from whatsapp @@ -423,12 +423,12 @@ class YowsupApp(object): pass def onDisconnect(self): - """ + u""" Called when disconnected from whatsapp """ def onContactTyping(self, number): - """ + u""" Called when contact starts to type Args: @@ -437,7 +437,7 @@ class YowsupApp(object): pass def onContactPaused(self, number): - """ + u""" Called when contact stops typing Args: @@ -446,7 +446,7 @@ class YowsupApp(object): pass def onTextMessage(self, _id, _from, to, notify, timestamp, participant, offline, retry, body): - """ + u""" Called when text message is received Args: @@ -463,7 +463,7 @@ class YowsupApp(object): pass def onImage(self, entity): - """ + u""" Called when image message is received Args: @@ -472,7 +472,7 @@ class YowsupApp(object): pass def onAudio(self, entity): - """ + u""" Called when audio message is received Args: @@ -482,7 +482,7 @@ class YowsupApp(object): def onVideo(self, entity): - """ + u""" Called when video message is received Args: @@ -491,7 +491,7 @@ class YowsupApp(object): pass def onLocation(self, entity): - """ + u""" Called when location message is received Args: @@ -500,7 +500,7 @@ class YowsupApp(object): pass def onVCard(self, _id, _from, name, card_data, to, notify, timestamp, participant): - """ + u""" Called when VCard message is received Args: @@ -516,15 +516,15 @@ class YowsupApp(object): pass def onAddedToGroup(self, entity): - """Called when the user has been added to a new group""" + u"""Called when the user has been added to a new group""" pass def onParticipantsAddedToGroup(self, entity): - """Called when participants have been added to a group""" + u"""Called when participants have been added to a group""" pass def onParticipantsRemovedFromGroup(self, group, participants): - """Called when participants have been removed from a group + u"""Called when participants have been removed from a group Args: - group: (str) id of the group (e.g. 27831788123-144024456) @@ -533,7 +533,7 @@ class YowsupApp(object): pass def sendEntity(self, entity): - """Sends an entity down the stack (as if YowsupAppLayer called toLower)""" + u"""Sends an entity down the stack (as if YowsupAppLayer called toLower)""" self.stack.broadcastEvent(YowLayerEvent(YowsupAppLayer.TO_LOWER_EVENT, entity = entity )) @@ -551,9 +551,9 @@ class YowsupApp(object): from yowsup.layers.interface import YowInterfaceLayer, ProtocolEntityCallback class YowsupAppLayer(YowInterfaceLayer): - EVENT_START = 'transwhat.event.YowsupAppLayer.start' - TO_LOWER_EVENT = 'transwhat.event.YowsupAppLayer.toLower' - SEND_IQ = 'transwhat.event.YowsupAppLayer.sendIq' + EVENT_START = u'transwhat.event.YowsupAppLayer.start' + TO_LOWER_EVENT = u'transwhat.event.YowsupAppLayer.toLower' + SEND_IQ = u'transwhat.event.YowsupAppLayer.sendIq' def onEvent(self, layerEvent): # We cannot pass instance varaibles in through init, so we use an event @@ -561,24 +561,24 @@ class YowsupAppLayer(YowInterfaceLayer): # Return False if you want the event to propogate down the stack # return True otherwise if layerEvent.getName() == YowsupAppLayer.EVENT_START: - self.caller = layerEvent.getArg('caller') + self.caller = layerEvent.getArg(u'caller') self.logger = logging.getLogger(self.__class__.__name__) return True elif layerEvent.getName() == YowNetworkLayer.EVENT_STATE_DISCONNECTED: self.caller.onDisconnect() return True elif layerEvent.getName() == YowsupAppLayer.TO_LOWER_EVENT: - self.toLower(layerEvent.getArg('entity')) + self.toLower(layerEvent.getArg(u'entity')) return True elif layerEvent.getName() == YowsupAppLayer.SEND_IQ: - iq = layerEvent.getArg('iq') - success = layerEvent.getArg('success') - failure = layerEvent.getArg('failure') + iq = layerEvent.getArg(u'iq') + success = layerEvent.getArg(u'success') + failure = layerEvent.getArg(u'failure') self._sendIq(iq, success, failure) return True return False - @ProtocolEntityCallback('success') + @ProtocolEntityCallback(u'success') def onAuthSuccess(self, entity): # entity is SuccessProtocolEntity status = entity.status @@ -587,21 +587,21 @@ class YowsupAppLayer(YowInterfaceLayer): expiration = entity.expiration props = entity.props nonce = entity.nonce - t = entity.t # I don't know what this is + t = entity.t # I donu't know what this is self.caller.onAuthSuccess(status, kind, creation, expiration, props, nonce, t) - @ProtocolEntityCallback('failure') + @ProtocolEntityCallback('failureu') def onAuthFailure(self, entity): # entity is FailureProtocolEntity reason = entity.reason self.caller.onAuthFailure(reason) - @ProtocolEntityCallback('receipt') + @ProtocolEntityCallback('receiptu') def onReceipt(self, entity): """Sends ack automatically""" # entity is IncomingReceiptProtocolEntity ack = OutgoingAckProtocolEntity(entity.getId(), - 'receipt', entity.getType(), entity.getFrom()) + 'receiptu', entity.getType(), entity.getFrom()) self.toLower(ack) _id = entity._id _from = entity._from @@ -612,7 +612,7 @@ class YowsupAppLayer(YowInterfaceLayer): items = entity.items self.caller.onReceipt(_id, _from, timestamp, type, participant, offline, items) - @ProtocolEntityCallback('ack') + @ProtocolEntityCallback('acku') def onAck(self, entity): # entity is IncomingAckProtocolEntity self.caller.onAck( @@ -622,7 +622,7 @@ class YowsupAppLayer(YowInterfaceLayer): entity.timestamp ) - @ProtocolEntityCallback('notification') + @ProtocolEntityCallback('notificationu') def onNotification(self, entity): """ Sends ack automatically @@ -635,11 +635,11 @@ class YowsupAppLayer(YowInterfaceLayer): self.caller.onParticipantsAddedToGroup(entity) elif isinstance(entity, RemoveGroupsNotificationProtocolEntity): self.caller.onParticipantsRemovedFromGroup( - entity.getGroupId().split('@')[0], + entity.getGroupId().split('@u')[0], entity.getParticipants().keys() ) - @ProtocolEntityCallback('message') + @ProtocolEntityCallback('messageu') def onMessageReceived(self, entity): self.logger.debug("Received Message: %s", entity) if entity.getType() == MessageProtocolEntity.MESSAGE_TYPE_TEXT: @@ -677,7 +677,7 @@ class YowsupAppLayer(YowInterfaceLayer): elif isinstance(entity, LocationMediaMessageProtocolEntity): self.caller.onLocation(entity) - @ProtocolEntityCallback('presence') + @ProtocolEntityCallback('presenceu') def onPresenceReceived(self, presence): _type = presence.getType() name = presence.getName() @@ -685,7 +685,7 @@ class YowsupAppLayer(YowInterfaceLayer): last = presence.getLast() self.caller.onPresenceReceived(_type, name, _from, last) - @ProtocolEntityCallback('chatstate') + @ProtocolEntityCallback('chatstateu') def onChatstate(self, chatstate): number = chatstate._from.split('@')[0] if chatstate.getState() == ChatstateProtocolEntity.STATE_TYPING: From bbf5a086970f6cf0a5e3c09043aebde183f8d49a Mon Sep 17 00:00:00 2001 From: moyamo Date: Wed, 30 Dec 2015 09:46:39 +0200 Subject: [PATCH 12/46] Update nickname in groups if nickname is unset. --- session.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/session.py b/session.py index 430bd93..ff30227 100644 --- a/session.py +++ b/session.py @@ -280,7 +280,7 @@ class Session(YowsupApp): if participant is not None: # Group message partname = participant.split(u'@')[0] self.sendGroupMessageToXMPP(buddy, partname, messageContent, - timestamp) + timestamp, participant) else: self.sendMessageToXMPP(buddy, messageContent, timestamp) # isBroadcast always returns false, Iu'm not sure how to get a broadcast @@ -636,7 +636,7 @@ class Session(YowsupApp): self.backend.handleMessage(self.user, buddy, messageContent, u"", u"", timestamp) - def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = u""): + def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = u"", defaultname = u""): if timestamp: timestamp = time.strftime(u"%Y%m%dT%H%M%S", time.gmtime(timestamp)) @@ -657,6 +657,8 @@ class Session(YowsupApp): group = self.groups[room] # Update nickname try: + if defaultname != u"" and group.participants[number].nick == number: + group.changeNick(number, defaultname) if self.buddies[number].nick != u"": group.changeNick(number, self.buddies[number].nick) except KeyError: From 3781dcfc1cc5aa7c565592b0a49a95553c245c38 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 31 Dec 2015 18:48:15 +0200 Subject: [PATCH 13/46] Handle case when buddy has no status message --- buddy.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/buddy.py b/buddy.py index 1cdb433..2aa004c 100644 --- a/buddy.py +++ b/buddy.py @@ -25,6 +25,7 @@ from Spectrum2 import protocol_pb2 import logging import time +import utils class Buddy(): @@ -106,7 +107,10 @@ class BuddyList(dict): self.logger.debug(u"%s received statuses of: %s", self.user, contacts) for number, (status, time) in contacts.iteritems(): buddy = self[number] - buddy.statusMsg = status.decode(u'utf-8') + if status is None: + buddy.statusMessage = "" + else: + buddy.statusMsg = utils.softToUni(status) self.updateSpectrum(buddy) From bed8214a1124f462beeb5c4001259f047d5929eb Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 31 Dec 2015 19:55:45 +0200 Subject: [PATCH 14/46] Use base64 sha1hash instead of base16 --- buddy.py | 2 +- utils.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/buddy.py b/buddy.py index 2aa004c..256038f 100644 --- a/buddy.py +++ b/buddy.py @@ -108,7 +108,7 @@ class BuddyList(dict): for number, (status, time) in contacts.iteritems(): buddy = self[number] if status is None: - buddy.statusMessage = "" + buddy.statusMsg = "" else: buddy.statusMsg = utils.softToUni(status) self.updateSpectrum(buddy) diff --git a/utils.py b/utils.py index e53d7fe..b325922 100644 --- a/utils.py +++ b/utils.py @@ -24,6 +24,7 @@ u""" import e4u import base64 import hashlib +import base64 def ago(secs): periods = [u"second", u"minute", u"hour", u"day", u"week", u"month", u"year", u"decade"] @@ -50,4 +51,4 @@ def decodePassword(password): return base64.b64decode(bytes(password.encode(u"utf-8"))) def sha1hash(data): - return hashlib.sha1(data).hexdigest() + return base64.b64encode(hashlib.sha1(data).digest()) From 22cebef457678afaa87a22b636de588ce806f2bf Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 31 Dec 2015 21:43:10 +0200 Subject: [PATCH 15/46] Revert upto c40d4776673e3d162bc92c193f9db722c860ba45 Hard to find connection bugs are occuring. Reverting changes for now --- Spectrum2/backend.py | 1 + USAGE.md | 1 + bot.py | 40 ++-- buddy.py | 54 ++--- group.py | 81 +------ reader.py | 20 +- session.py | 529 +++++++++++++++++++++++-------------------- transwhat.py | 38 ++-- utils.py | 25 +- whatsappbackend.py | 48 ++-- yowsupwrapper.py | 142 ++++++------ 11 files changed, 478 insertions(+), 501 deletions(-) diff --git a/Spectrum2/backend.py b/Spectrum2/backend.py index 5a91a27..4504bb1 100644 --- a/Spectrum2/backend.py +++ b/Spectrum2/backend.py @@ -262,6 +262,7 @@ class SpectrumBackend: def handleConvMessagePayload(self, data): payload = protocol_pb2.ConversationMessage() + self.logger.error("handleConvMessagePayload") if (payload.ParseFromString(data) == False): #TODO: ERROR return diff --git a/USAGE.md b/USAGE.md index 67b4c8b..dcea058 100644 --- a/USAGE.md +++ b/USAGE.md @@ -28,6 +28,7 @@ The bot is one of the contacts every user has in its contact list. It offers you | ------------ | --------------- | | `\help` | show this message | | `\prune` | clear your buddylist | +| `\sync` | sync your imported contacts with WhatsApp | | `\lastseen` | request last online timestamp from buddy | | `\leave` | permanently leave group chat | | `\groups` | print all attended groups | diff --git a/bot.py b/bot.py index ac44fff..ea54d42 100644 --- a/bot.py +++ b/bot.py @@ -1,10 +1,10 @@ -__author__ = u"Steffen Vogel" -__copyright__ = u"Copyright 2015, Steffen Vogel" -__license__ = u"GPLv3" -__maintainer__ = u"Steffen Vogel" -__email__ = u"post@steffenvogel.de" +__author__ = "Steffen Vogel" +__copyright__ = "Copyright 2015, Steffen Vogel" +__license__ = "GPLv3" +__maintainer__ = "Steffen Vogel" +__email__ = "post@steffenvogel.de" -u""" +""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -30,30 +30,30 @@ import os import utils class Bot(): - def __init__(self, session, name = u"Bot"): + def __init__(self, session, name = "Bot"): self.session = session self.name = name self.commands = { - u"help": self._help, - u"prune": self._prune, - u"groups": self._groups, - u"getgroups": self._getgroups + "help": self._help, + "prune": self._prune, + "groups": self._groups, + "getgroups": self._getgroups } def parse(self, message): - args = message.split(u" ") + args = message.split(" ") cmd = args.pop(0) - if cmd[0] == u'\\': + if cmd[0] == '\\': try: self.call(cmd[1:], args) except KeyError: - self.send(u"invalid command") + self.send("invalid command") except TypeError: - self.send(u"invalid syntax") + self.send("invalid syntax") else: - self.send(u"a valid command starts with a backslash") + self.send("a valid command starts with a backslash") def call(self, cmd, args = []): func = self.commands[cmd] @@ -71,7 +71,7 @@ class Bot(): # commands def _help(self): - self.send(u"""following bot commands are available: + self.send("""following bot commands are available: \\help show this message \\prune clear your buddylist @@ -86,7 +86,7 @@ following group commands are available def _prune(self): self.session.buddies.prune() self.session.updateRoster() - self.send(u"buddy list cleared") + self.send("buddy list cleared") def _groups(self): for group in self.session.groups: @@ -96,9 +96,9 @@ following group commands are available except KeyError: nick = buddy - self.send(self.session.groups[group].id + u"@" + self.session.backend.spectrum_jid + u" " + self.session.groups[group].subject + u" Owner: " + nick ) + self.send(self.session.groups[group].id + "@" + self.session.backend.spectrum_jid + " " + self.session.groups[group].subject + " Owner: " + nick ) def _getgroups(self): - #self.session.call(u"group_getGroups", (u"participating",)) + #self.session.call("group_getGroups", ("participating",)) self.session.requestGroupsList(self.session._updateGroups) diff --git a/buddy.py b/buddy.py index 256038f..8e722e3 100644 --- a/buddy.py +++ b/buddy.py @@ -1,10 +1,10 @@ -__author__ = u"Steffen Vogel" -__copyright__ = u"Copyright 2015, Steffen Vogel" -__license__ = u"GPLv3" -__maintainer__ = u"Steffen Vogel" -__email__ = u"post@steffenvogel.de" +__author__ = "Steffen Vogel" +__copyright__ = "Copyright 2015, Steffen Vogel" +__license__ = "GPLv3" +__maintainer__ = "Steffen Vogel" +__email__ = "post@steffenvogel.de" -u""" +""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -25,7 +25,6 @@ from Spectrum2 import protocol_pb2 import logging import time -import utils class Buddy(): @@ -34,8 +33,8 @@ class Buddy(): self.owner = owner self.number = number self.groups = groups - self.image_hash = image_hash if image_hash is not None else u"" - self.statusMsg = u"" + self.image_hash = image_hash if image_hash is not None else "" + self.statusMsg = "" self.lastseen = 0 self.presence = 0 @@ -47,7 +46,7 @@ class Buddy(): self.image_hash = image_hash def __str__(self): - return u"%s (nick=%s)" % (self.number, self.nick) + return "%s (nick=%s)" % (self.number, self.nick) class BuddyList(dict): @@ -63,20 +62,20 @@ class BuddyList(dict): for buddy in buddies: number = buddy.buddyName nick = buddy.alias - statusMsg = buddy.statusMessage.decode(u'utf-8') + 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(u"Update roster") + self.logger.debug("Update roster") # old = self.buddies.keys() # self.buddies.load() # new = self.buddies.keys() # contacts = new contacts = self.keys() - contacts.remove(u'bot') + contacts.remove('bot') if self.synced == False: self.session.sendSync(contacts, delta = False, interactive = True) @@ -85,11 +84,11 @@ class BuddyList(dict): # add = set(new) - set(old) # remove = set(old) - set(new) -# self.logger.debug(u"Roster remove: %s", str(list(remove))) - self.logger.debug(u"Roster add: %s", str(list(contacts))) +# self.logger.debug("Roster remove: %s", str(list(remove))) + self.logger.debug("Roster add: %s", str(list(contacts))) # for number in remove: -# self.backend.handleBuddyChanged(self.user, number, u"", [], +# self.backend.handleBuddyChanged(self.user, number, "", [], # protocol_pb2.STATUS_NONE) # self.backend.handleBuddyRemoved(self.user, number) # self.unsubscribePresence(number) @@ -98,19 +97,16 @@ class BuddyList(dict): buddy = self[number] self.backend.handleBuddyChanged(self.user, number, buddy.nick, buddy.groups, protocol_pb2.STATUS_NONE, - iconHash = buddy.image_hash if buddy.image_hash is not None else u"") + iconHash = buddy.image_hash if buddy.image_hash is not None else "") self.session.subscribePresence(number) - self.logger.debug(u"%s is requesting statuses of: %s", self.user, contacts) + self.logger.debug("%s is requesting statuses of: %s", self.user, contacts) self.session.requestStatuses(contacts, success = self.onStatus) def onStatus(self, contacts): - self.logger.debug(u"%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(): buddy = self[number] - if status is None: - buddy.statusMsg = "" - else: - buddy.statusMsg = utils.softToUni(status) + buddy.statusMsg = status self.updateSpectrum(buddy) @@ -128,9 +124,9 @@ class BuddyList(dict): self.session.sendSync([number], delta = True, interactive = True) self.session.subscribePresence(number) self.session.requestStatuses([number], success = self.onStatus) - buddy = Buddy(self.owner, number, nick, u"", groups, image_hash) + buddy = Buddy(self.owner, number, nick, "", groups, image_hash) self[number] = buddy - self.logger.debug(u"Roster add: %s", buddy) + self.logger.debug("Roster add: %s", buddy) self.updateSpectrum(buddy) return buddy @@ -138,7 +134,7 @@ class BuddyList(dict): def updateSpectrum(self, buddy): if buddy.presence == 0: status = protocol_pb2.STATUS_NONE - elif buddy.presence == u'unavailable': + elif buddy.presence == 'unavailable': status = protocol_pb2.STATUS_AWAY else: status = protocol_pb2.STATUS_ONLINE @@ -146,18 +142,18 @@ class BuddyList(dict): statusmsg = buddy.statusMsg if buddy.lastseen != 0: timestamp = time.localtime(buddy.lastseen) - statusmsg += time.strftime(u"\n Last seen: %a, %d %b %Y %H:%M:%S", timestamp) + statusmsg += time.strftime("\n Last seen: %a, %d %b %Y %H:%M:%S", timestamp) self.backend.handleBuddyChanged(self.user, buddy.number, buddy.nick, buddy.groups, status, statusMessage = statusmsg, - iconHash = buddy.image_hash if buddy.image_hash is not None else u"") + iconHash = buddy.image_hash if buddy.image_hash is not None else "") def remove(self, number): try: buddy = self[number] del self[number] - self.backend.handleBuddyChanged(self.user, number, u"", [], + self.backend.handleBuddyChanged(self.user, number, "", [], protocol_pb2.STATUS_NONE) self.backend.handleBuddyRemoved(self.user, number) self.session.unsubscribePresence(number) diff --git a/group.py b/group.py index 4179a95..8708fe7 100644 --- a/group.py +++ b/group.py @@ -1,10 +1,10 @@ -__author__ = u"Steffen Vogel" -__copyright__ = u"Copyright 2015, Steffen Vogel" -__license__ = u"GPLv3" -__maintainer__ = u"Steffen Vogel" -__email__ = u"post@steffenvogel.de" +__author__ = "Steffen Vogel" +__copyright__ = "Copyright 2015, Steffen Vogel" +__license__ = "GPLv3" +__maintainer__ = "Steffen Vogel" +__email__ = "post@steffenvogel.de" -u""" +""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -21,77 +21,14 @@ u""" along with transWhat. If not, see . """ -from Spectrum2 import protocol_pb2 - class Group(): - def __init__(self, id, owner, subject, subjectOwner, backend, user): + def __init__(self, id, owner, subject, subjectOwner): self.id = id self.subject = subject self.subjectOwner = subjectOwner self.owner = owner self.joined = False - self.backend = backend - self.user = user - self.nick = u"me" - # Participants is a number -> nickname dict - self.participants = {} - - def addParticipants(self, participants, buddies, yourNumber): - u""" - Adds participants to the group. - - Args: - - participants: (Iterable) phone numbers of participants - - buddies: (dict) Used to get the nicknames of the participants - - yourNumber: The number you are using - """ - for jid in participants: - number = jid.split(u'@')[0] - try: - nick = buddies[number].nick - except KeyError: - nick = number - if number == yourNumber: - nick = self.nick - if nick == u"": - nick = number - self.participants[number] = nick - - def sendParticipantsToSpectrum(self, yourNumber): - for number, nick in self.participants.iteritems(): - if number == self.owner: - flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR - else: - flags = protocol_pb2.PARTICIPANT_FLAG_NONE - if number == yourNumber: - flags = flags | protocol_pb2.PARTICIPANT_FLAG_ME - - self._updateParticipant(number, flags, protocol_pb2.STATUS_ONLINE) - - def removeParticipants(self, participants): - for jid in participants: - number = jid.split(u'@')[0] - nick = self.participants[number] - flags = protocol_pb2.PARTICIPANT_FLAG_NONE - self._updateParticipant(number, flags, protocol_pb2.STATUS_NONE) - del self.participants[number] - - def changeNick(self, number, new_nick): - if self.participants[number] == new_nick: - return - if number == self.owner: - flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR - else: - flags = protocol_pb2.PARTICIPANT_FLAG_NONE - self._updateParticipant(number, flags, protocol_pb2.STATUS_ONLINE, new_nick) - self.participants[number] = new_nick - - def _updateParticipant(self, number, flags, status, newNick = u""): - nick = self.participants[number] - # Notice the status message is the buddy's number - if self.joined: - self.backend.handleParticipantChanged( - self.user, nick, self.id, flags, - status, number, newname = newNick) + self.nick = "me" + self.participants = [] diff --git a/reader.py b/reader.py index 25dbf59..1137973 100644 --- a/reader.py +++ b/reader.py @@ -1,10 +1,10 @@ -__author__ = u"Steffen Vogel" -__copyright__ = u"Copyright 2015, Steffen Vogel" -__license__ = u"GPLv3" -__maintainer__ = u"Steffen Vogel" -__email__ = u"post@steffenvogel.de" +__author__ = "Steffen Vogel" +__copyright__ = "Copyright 2015, Steffen Vogel" +__license__ = "GPLv3" +__maintainer__ = "Steffen Vogel" +__email__ = "post@steffenvogel.de" -u""" +""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -24,19 +24,19 @@ u""" import time def get_token(number, timeout = 30): - file = open(u'tokens') + file = open('tokens') file.seek(-1, 2) count = 0 while count < timeout: line = file.readline() - if line in [u"", u"\n"]: + if line in ["", "\n"]: time.sleep(1) count += 1 continue else: - t, n, tk = line[:-1].split(u"\t") + t, n, tk = line[:-1].split("\t") if (n == number): file.close() @@ -45,4 +45,4 @@ def get_token(number, timeout = 30): file.close() -print get_token(u"4917696978528") +print get_token("4917696978528") diff --git a/session.py b/session.py index ff30227..d481bfe 100644 --- a/session.py +++ b/session.py @@ -1,10 +1,10 @@ -__author__ = u"Steffen Vogel" -__copyright__ = u"Copyright 2015, Steffen Vogel" -__license__ = u"GPLv3" -__maintainer__ = u"Steffen Vogel" -__email__ = u"post@steffenvogel.de" +__author__ = "Steffen Vogel" +__copyright__ = "Copyright 2015, Steffen Vogel" +__license__ = "GPLv3" +__maintainer__ = "Steffen Vogel" +__email__ = "post@steffenvogel.de" -u""" +""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -56,14 +56,14 @@ class Session(YowsupApp): def __init__(self, backend, user, legacyName, extra): super(Session, self).__init__() self.logger = logging.getLogger(self.__class__.__name__) - self.logger.info(u"Created: %s", legacyName) + self.logger.info("Created: %s", legacyName) self.backend = backend self.user = user self.legacyName = legacyName self.status = protocol_pb2.STATUS_NONE - self.statusMessage = u'' + self.statusMessage = '' self.groups = {} self.gotGroupList = False @@ -86,21 +86,21 @@ class Session(YowsupApp): self.bot = Bot(self) self.imgMsgId = None - self.imgPath = u"" + self.imgPath = "" self.imgBuddy = None - self.imgType = u"" + self.imgType = "" def __del__(self): # handleLogoutRequest self.logout() def logout(self): - self.logger.info(u"%s logged out", self.user) + self.logger.info("%s logged out", self.user) super(Session, self).logout() self.loggedIn = False def login(self, password): - self.logger.info(u"%s attempting login", self.user) + self.logger.info("%s attempting login", self.user) self.password = password self.shouldBeConncted = True super(Session, self).login(self.legacyName, self.password) @@ -108,33 +108,33 @@ class Session(YowsupApp): def _shortenGroupId(self, gid): # FIXME: might have problems if number begins with 0 return gid -# return u'-'.join(hex(int(s))[2:] for s in gid.split(u'-')) +# return '-'.join(hex(int(s))[2:] for s in gid.split('-')) def _lengthenGroupId(self, gid): return gid # FIXME: might have problems if number begins with 0 -# return u'-'.join(str(int(s, 16)) for s in gid.split(u'-')) +# return '-'.join(str(int(s, 16)) for s in gid.split('-')) def updateRoomList(self): rooms = [] text = [] for room, group in self.groups.iteritems(): rooms.append([self._shortenGroupId(room), group.subject]) - text.append(self._shortenGroupId(room) + u'@' + self.backend.spectrum_jid + u' :' + group.subject) + text.append(self._shortenGroupId(room) + '@' + self.backend.spectrum_jid + ' :' + group.subject) - self.logger.debug(u"Got rooms: %s", rooms) + self.logger.debug("Got rooms: %s", rooms) self.backend.handleRoomList(rooms) - message = u"Note, you are a participant of the following groups:\n" +\ - u'\n'.join(text) + u'\nIf you do not join them you will lose messages' + message = "Note, you are a participant of the following groups:\n" +\ + '\n'.join(text) + '\nIf you do not join them you will lose messages' #self.bot.send(message) def _updateGroups(self, response, request): - self.logger.debug(u'Received groups list %s', response) + self.logger.debug('Received groups list %s', response) groups = response.getGroups() for group in groups: room = group.getId() - owner = group.getOwner().split(u'@')[0] - subjectOwner = group.getSubjectOwner().split(u'@')[0] + owner = group.getOwner().split('@')[0] + subjectOwner = group.getSubjectOwner().split('@')[0] subject = utils.softToUni(group.getSubject()) if room in self.groups: @@ -143,10 +143,9 @@ class Session(YowsupApp): oroom.subjectOwner = subjectOwner oroom.subject = subject else: - self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) -# self.joinRoom(self._shortenGroupId(room), self.user.split(u"@")[0]) - self.groups[room].addParticipants(group.getParticipants().keys(), - self.buddies, self.legacyName) + self.groups[room] = Group(room, owner, subject, subjectOwner) +# self.joinRoom(self._shortenGroupId(room), self.user.split("@")[0]) + self.groups[room].participants = group.getParticipants().keys() #self._addParticipantsToRoom(room, group.getParticipants()) @@ -154,8 +153,8 @@ class Session(YowsupApp): while self.groupOfflineQueue[room]: msg = self.groupOfflineQueue[room].pop(0) self.backend.handleMessage(self.user, room, msg[1], - msg[0], u"", msg[2]) - self.logger.debug(u"Send queued group message to: %s %s %s", + msg[0], "", msg[2]) + self.logger.debug("Send queued group message to: %s %s %s", msg[0],msg[1], msg[2]) self.gotGroupList = True for room, nick in self.joinRoomQueue: @@ -169,39 +168,61 @@ class Session(YowsupApp): return room = self._lengthenGroupId(room) if room in self.groups: - self.logger.info(u"Joining room: %s room=%s, nick=%s", + self.logger.info("Joining room: %s room=%s, nick=%s", self.legacyName, room, nick) group = self.groups[room] - group.joined = True group.nick = nick - group.participants[self.legacyName] = nick try: - ownerNick = group.participants[group.subjectOwner] + ownerNick = self.buddies[group.subjectOwner].nick except KeyError: ownerNick = group.subjectOwner - group.sendParticipantsToSpectrum(self.legacyName) + self._refreshParticipants(room) self.backend.handleSubject(self.user, self._shortenGroupId(room), group.subject, ownerNick) - self.logger.debug(u"Room subject: room=%s, subject=%s", + self.logger.debug("Room subject: room=%s, subject=%s", room, group.subject) self.backend.handleRoomNicknameChanged( self.user, self._shortenGroupId(room), group.subject ) + group.joined = True else: - self.logger.warn(u"Room doesn't exist: %s", room) + self.logger.warn("Room doesn't exist: %s", room) def leaveRoom(self, room): if room in self.groups: - self.logger.info(u"Leaving room: %s room=%s", self.legacyName, room) + self.logger.info("Leaving room: %s room=%s", self.legacyName, room) group = self.groups[room] group.joined = False else: - self.logger.warn(u"Room doesn't exist: %s. Unable to leave.", room) + self.logger.warn("Room doesn't exist: %s. Unable to leave.", room) + + def _refreshParticipants(self, room): + group = self.groups[room] + for jid in group.participants: + buddy = jid.split("@")[0] + self.logger.info("Added %s to room %s", buddy, room) + try: + nick = self.buddies[buddy].nick + except KeyError: + nick = buddy + if nick == "": + nick = buddy + + if buddy == group.owner: + flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR + else: + flags = protocol_pb2.PARTICIPANT_FLAG_NONE + if buddy == self.legacyName: + nick = group.nick + flags = flags | protocol_pb2.PARTICIPANT_FLAG_ME + self.backend.handleParticipantChanged( + self.user, nick, self._shortenGroupId(room), flags, + protocol_pb2.STATUS_ONLINE, buddy) def _lastSeen(self, number, seconds): - self.logger.debug(u"Last seen %s at %s seconds" % (number, str(seconds))) + self.logger.debug("Last seen %s at %s seconds" % (number, str(seconds))) if seconds < 60: self.onPresenceAvailable(number) else: @@ -210,43 +231,43 @@ class Session(YowsupApp): # Called by superclass def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t): - self.logger.info(u"Auth success: %s", self.user) + self.logger.info("Auth success: %s", self.user) self.backend.handleConnected(self.user) - self.backend.handleBuddyChanged(self.user, u"bot", self.bot.name, - [u"Admin"], protocol_pb2.STATUS_ONLINE) + self.backend.handleBuddyChanged(self.user, "bot", self.bot.name, + ["Admin"], protocol_pb2.STATUS_ONLINE) if self.initialized == False: self.sendOfflineMessages() - #self.bot.call(u"welcome") + #self.bot.call("welcome") self.initialized = True self.sendPresence(True) for func in self.loginQueue: func() - self.logger.debug(u'Requesting groups list') + self.logger.debug('Requesting groups list') self.requestGroupsList(self._updateGroups) self.loggedIn = True # Called by superclass def onAuthFailed(self, reason): - self.logger.info(u"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.password = None self.loggedIn = False # Called by superclass def onDisconnect(self): - self.logger.debug(u'Disconnected') - self.backend.handleDisconnected(self.user, 0, u'Disconnected for unknown reasons') + self.logger.debug('Disconnected') + self.backend.handleDisconnected(self.user, 0, 'Disconnected for unknown reasons') # Called by superclass def onReceipt(self, _id, _from, timestamp, type, participant, offline, items): - self.logger.debug(u"received receipt, sending ack: " + - u' '.join(map(str, [_id, _from, timestamp, + self.logger.debug("received receipt, sending ack: " + + ' '.join(map(str, [_id, _from, timestamp, type, participant, offline, items])) ) try: - buddy = self.buddies[_from.split(u'@')[0]] + buddy = self.buddies[_from.split('@')[0]] #self.backend.handleBuddyChanged(self.user, buddy.number.number, # buddy.nick, buddy.groups, protocol_pb2.STATUS_ONLINE) self.backend.handleMessageAck(self.user, buddy.number, self.msgIDs[_id].xmppId) @@ -259,47 +280,58 @@ class Session(YowsupApp): # Called by superclass def onAck(self, _id, _class, _from, timestamp): - self.logger.debug(u'received ack ' + - u' '.join(map(str, [_id, _class, _from,timestamp,])) + self.logger.debug('received ack ' + + ' '.join(map(str, [_id, _class, _from,timestamp,])) ) # Called by superclass def onTextMessage(self, _id, _from, to, notify, timestamp, participant, offline, retry, body): - self.logger.debug(u'received TextMessage' + - u' '.join(map(str, [ + self.logger.debug('received TextMessage' + + ' '.join(map(str, [ _id, _from, to, notify, timestamp, participant, offline, retry, body ])) ) - buddy = _from.split(u'@')[0] + buddy = _from.split('@')[0] messageContent = utils.softToUni(body) self.sendReceipt(_id, _from, None, participant) - self.logger.info(u"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) if participant is not None: # Group message - partname = participant.split(u'@')[0] + partname = participant.split('@')[0] + try: + part = self.buddies[partname] + if part.nick == "": + part.nick = notify + self.backend.handleParticipantChanged( + self.user, partname, self._shortenGroupId(buddy), + protocol_pb2.PARTICIPANT_FLAG_NONE, + protocol_pb2.STATUS_ONLINE, "", part.nick + ) # TODO + except KeyError: + self.updateBuddy(partname, notify, []) self.sendGroupMessageToXMPP(buddy, partname, messageContent, - timestamp, participant) + timestamp) else: self.sendMessageToXMPP(buddy, messageContent, timestamp) - # isBroadcast always returns false, Iu'm not sure how to get a broadcast + # isBroadcast always returns false, I'm not sure how to get a broadcast # message. #if messageEntity.isBroadcast(): - # self.logger.info(u"Broadcast received from %s to %s: %s (at ts=%s)",\ + # self.logger.info("Broadcast received from %s to %s: %s (at ts=%s)",\ # buddy, self.legacyName, messageContent, timestamp) - # messageContent = u"[Broadcast] " + messageContent + # messageContent = "[Broadcast] " + messageContent # Called by superclass def onImage(self, image): - self.logger.debug('Received image message %su', str(image)) - buddy = image._from.split('@u')[0] + self.logger.debug('Received image message %s', str(image)) + buddy = image._from.split('@')[0] participant = image.participant if image.caption is None: - image.caption = 'u' - message = image.url + ' u' + image.caption + image.caption = '' + message = image.url + ' ' + image.caption if participant is not None: # Group message - partname = participant.split('@u')[0] + partname = participant.split('@')[0] self.sendGroupMessageToXMPP(buddy, partname, message, image.timestamp) else: @@ -308,12 +340,12 @@ class Session(YowsupApp): # Called by superclass def onAudio(self, audio): - self.logger.debug('Received audio message %su', str(audio)) - buddy = audio._from.split('@u')[0] + self.logger.debug('Received audio message %s', str(audio)) + buddy = audio._from.split('@')[0] participant = audio.participant message = audio.url if participant is not None: # Group message - partname = participant.split('@u')[0] + partname = participant.split('@')[0] self.sendGroupMessageToXMPP(buddy, partname, message, audio.timestamp) else: @@ -322,13 +354,13 @@ class Session(YowsupApp): # Called by superclass def onVideo(self, video): - self.logger.debug('Received video message %su', str(video)) - buddy = video._from.split('@u')[0] + self.logger.debug('Received video message %s', str(video)) + buddy = video._from.split('@')[0] participant = video.participant message = video.url if participant is not None: # Group message - partname = participant.split('@u')[0] + partname = participant.split('@')[0] self.sendGroupMessageToXMPP(buddy, partname, message, video.timestamp) else: @@ -336,57 +368,57 @@ class Session(YowsupApp): self.sendReceipt(video._id, video._from, None, video.participant) def onLocation(self, location): - buddy = location._from.split('@u')[0] + buddy = location._from.split('@')[0] latitude = location.getLatitude() longitude = location.getLongitude() url = location.getLocationUrl() participant = location.participant - self.logger.debug(u"Location received from %s: %s, %s", + self.logger.debug("Location received from %s: %s, %s", buddy, latitude, longitude) if participant is not None: # Group message - partname = participant.split('@u')[0] + partname = participant.split('@')[0] self.sendGroupMessageToXMPP(buddy, partname, url, location.timestamp) - self.sendGroupMessageToXMPP(buddy, partname, 'geo:u' + latitude + ',u' + longitude, + self.sendGroupMessageToXMPP(buddy, partname, 'geo:' + latitude + ',' + longitude, location.timestamp) else: self.sendMessageToXMPP(buddy, url, location.timestamp) - self.sendMessageToXMPP(buddy, 'geo:u' + latitude + ',u' + longitude, + self.sendMessageToXMPP(buddy, 'geo:' + latitude + ',' + longitude, location.timestamp) self.sendReceipt(location._id, location._from, None, location.participant, location.timestamp) # Called by superclass def onVCard(self, _id, _from, name, card_data, to, notify, timestamp, participant): - self.logger.debug('received VCardu' + - ' u'.join(map(str, [ + self.logger.debug('received VCard' + + ' '.join(map(str, [ _id, _from, name, card_data, to, notify, timestamp, participant ])) ) - buddy = _from.split(u"@")[0] + buddy = _from.split("@")[0] if participant is not None: # Group message - partname = participant.split('@u')[0] - self.sendGroupMessageToXMPP(buddy, partname, u"Received VCard (not implemented yet)", timestamp) + partname = participant.split('@')[0] + self.sendGroupMessageToXMPP(buddy, partname, "Received VCard (not implemented yet)", timestamp) else: - self.sendMessageToXMPP(buddy, u"Received VCard (not implemented yet)") + self.sendMessageToXMPP(buddy, "Received VCard (not implemented yet)") # self.sendMessageToXMPP(buddy, card_data) #self.transferFile(buddy, str(name), card_data) self.sendReceipt(_id, _from, None, participant) def transferFile(self, buddy, name, data): # Not working - self.logger.debug('transfering file %su', name) + self.logger.debug('transfering file %s', name) self.backend.handleFTStart(self.user, buddy, name, len(data)) self.backend.handleFTData(0, data) self.backend.handleFTFinish(self.user, buddy, name, len(data), 0) # Called by superclass def onContactTyping(self, buddy): - self.logger.info(u"Started typing: %s", buddy) - if buddy != 'botu': + self.logger.info("Started typing: %s", buddy) + if buddy != 'bot': self.sendPresence(True) self.backend.handleBuddyTyping(self.user, buddy) @@ -395,58 +427,76 @@ class Session(YowsupApp): # Called by superclass def onContactPaused(self, buddy): - self.logger.info(u"Paused typing: %s", buddy) - if buddy != 'botu': + self.logger.info("Paused typing: %s", buddy) + if buddy != 'bot': self.backend.handleBuddyTyped(self.user, buddy) self.timer = Timer(3, self.backend.handleBuddyStoppedTyping, (self.user, buddy)).start() # Called by superclass def onAddedToGroup(self, group): - self.logger.debug(u"Added to group: %s", group) + self.logger.debug("Added to group: %s", group) room = group.getGroupId() owner = group.getCreatorJid(full = False) subjectOwner = group.getSubjectOwnerJid(full = False) subject = utils.softToUni(group.getSubject()) - self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) - self.groups[room].addParticipants(group.getParticipants, self.buddies, self.legacyName) - self.bot.send(u"You have been added to group: %s@%s (%s)" + self.groups[room] = Group(room, owner, subject, subjectOwner) + self.groups[room].participants = group.getParticipants().keys() +# self.joinRoom(self._shortenGroupId(room), self.user.split("@")[0]) + + #self._addParticipantsToRoom(room, group.getParticipants()) + self.bot.send("You have been added to group: %s@%s (%s)" % (self._shortenGroupId(room), subject, self.backend.spectrum_jid)) # Called by superclass def onParticipantsAddedToGroup(self, group): - self.logger.debug(u"Participants added to group: %s", group) - room = group.getGroupId().split('@u')[0] - self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName) - self.groups[room].sendParticipantsToSpectrum(self.legacyName) + self.logger.debug("Participants added to group: %s", group) + room = group.getGroupId().split('@')[0] + self.groups[room].participants.extend(group.getParticipants()) + self._refreshParticipants(room) # Called by superclass def onParticipantsRemovedFromGroup(self, room, participants): - self.logger.debug(u"Participants removed from group: %s, %s", + self.logger.debug("Participants removed from group: %s, %s", room, participants) - self.groups[room].removeParticipants(participants) + group = self.groups[room] + for jid in participants: + group.participants.remove(jid) + buddy = jid.split("@")[0] + try: + nick = self.buddies[buddy].nick + except KeyError: + nick = buddy + if nick == "": + nick = buddy + if buddy == self.legacyName: + nick = group.nick + flags = protocol_pb2.PARTICIPANT_FLAG_NONE + self.backend.handleParticipantChanged( + self.user, nick, self._shortenGroupId(room), flags, + protocol_pb2.STATUS_NONE, buddy) def onPresenceReceived(self, _type, name, jid, lastseen): - self.logger.info(u"Presence received: %s %s %s %s", _type, name, jid, lastseen) - buddy = jid.split(u"@")[0] + self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) + buddy = jid.split("@")[0] try: buddy = self.buddies[buddy] except KeyError: - self.logger.error(u"Buddy not found: %s", buddy) + self.logger.error("Buddy not found: %s", buddy) return if (lastseen == str(buddy.lastseen)) and (_type == buddy.presence): return - if ((lastseen != u"deny") and (lastseen != None) and (lastseen != u"none")): + if ((lastseen != "deny") and (lastseen != None) and (lastseen != "none")): buddy.lastseen = int(lastseen) if (_type == None): buddy.lastseen = time.time() buddy.presence = _type - if _type == u"unavailable": + if _type == "unavailable": self.onPresenceUnavailable(buddy) else: self.onPresenceAvailable(buddy) @@ -454,17 +504,17 @@ class Session(YowsupApp): def onPresenceAvailable(self, buddy): - self.logger.info(u"Is available: %s", buddy) + self.logger.info("Is available: %s", buddy) self.buddies.updateSpectrum(buddy) def onPresenceUnavailable(self, buddy): - self.logger.info(u"Is unavailable: %s", buddy) + self.logger.info("Is unavailable: %s", buddy) self.buddies.updateSpectrum(buddy) # spectrum RequestMethods def sendTypingStarted(self, buddy): - if buddy != u"bot": - self.logger.info(u"Started typing: %s to %s", self.legacyName, buddy) + if buddy != "bot": + self.logger.info("Started typing: %s to %s", self.legacyName, buddy) self.sendTyping(buddy, True) # If he is typing he is present # I really don't know where else to put this. @@ -472,126 +522,120 @@ class Session(YowsupApp): self.sendPresence(True) def sendTypingStopped(self, buddy): - if buddy != u"bot": - self.logger.info(u"Stopped typing: %s to %s", self.legacyName, buddy) + if buddy != "bot": + self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy) self.sendTyping(buddy, False) def sendMessageToWA(self, sender, message, ID): - self.logger.info(u"Message sent from %s to %s: %s", + self.logger.info("Message sent from %s to %s: %s", self.legacyName, sender, message) - message = message.encode(u"utf-8") + message = message.encode("utf-8") # FIXME: Fragile, should pass this in to onDlerror self.dlerror_message = message self.dlerror_sender = sender self.dlerror_ID = ID # End Fragile - if sender == u"bot": + if sender == "bot": self.bot.parse(message) - elif u"-" in sender: # group msg - if u"/" in sender: # directed at single user - room, nick = sender.split(u"/") - group = self.groups[room] - number = None - for othernumber, othernick in group.participants.iteritems(): - if othernick == nick: - number = othernumber - break - if number is not None: - self.logger.debug(u"Private message sent from %s to %s", self.legacyName, number) - waId = self.sendTextMessage(number + u'@s.whatsapp.net', message) - self.msgIDs[waId] = MsgIDs( ID, waId) - else: - self.logger.error(u"Attempted to send private message to non-existent user") - self.logger.debug(u"%s to %s in %s", self.legacyName, nick, room) + elif "-" in sender: # group msg + if "/" in sender: # directed at single user + room, nick = sender.split("/") + for buddy, buddy3 in self.buddies.iteritems(): + self.logger.info("Group buddy=%s nick=%s", buddy, + buddy3.nick) + if buddy3.nick == nick: + nick = buddy + waId = self.sendTextMessage(nick + '@s.whatsapp.net', message) + self.msgIDs[waId] = MsgIDs( ID, waId) else: room = sender - if message[0] == u'\\' and message[:1] != u'\\\\': - self.logger.debug(u"Executing command %s in %s", message, room) + if message[0] == '\\' and message[:1] != '\\\\': + self.logger.debug("Executing command %s in %s", message, room) self.executeCommand(message, room) else: try: group = self.groups[self._lengthenGroupId(room)] - self.logger.debug(u"Group Message from %s to %s Groups: %s", + self.logger.debug("Group Message from %s to %s Groups: %s", group.nick , group , self.groups) self.backend.handleMessage( - self.user, room, message.decode(u'utf-8'), group.nick + self.user, room, message.decode('utf-8'), group.nick ) except KeyError: - self.logger.error(u'Group not found: %s', room) + self.logger.error('Group not found: %s', room) - if (u".jpg" in message.lower()) or (u".webp" in message.lower()): - if (u".jpg" in message.lower()): - self.imgType = u"jpg" - if (u".webp" in message.lower()): - self.imgType = u"webp" + if (".jpg" in message.lower()) or (".webp" in message.lower()): + if (".jpg" in message.lower()): + self.imgType = "jpg" + if (".webp" in message.lower()): + self.imgType = "webp" self.imgMsgId = ID - self.imgBuddy = room + u"@g.us" + self.imgBuddy = room + "@g.us" downloader = MediaDownloader(self.onDlsuccess, self.onDlerror) downloader.download(message) #self.imgMsgId = ID - #self.imgBuddy = room + u"@g.us" - elif u"geo:" in message.lower(): - self._sendLocation(room + u"@g.us", message, ID) + #self.imgBuddy = room + "@g.us" + elif "geo:" in message.lower(): + self._sendLocation(room + "@g.us", message, ID) else: - self.sendTextMessage(self._lengthenGroupId(room) + u'@g.us', message) + self.sendTextMessage(self._lengthenGroupId(room) + '@g.us', message) else: # private msg buddy = sender -# if message == u"\\lastseen": -# self.call(u"presence_request", buddy = (buddy + u"@s.whatsapp.net",)) +# if message == "\\lastseen": +# self.call("presence_request", buddy = (buddy + "@s.whatsapp.net",)) # else: - if message.split(u" ").pop(0) == u"\\lastseen": + if message.split(" ").pop(0) == "\\lastseen": self.presenceRequested.append(buddy) - #self.call(u"presence_request", (buddy + u"@s.whatsapp.net",)) + #self.call("presence_request", (buddy + "@s.whatsapp.net",)) self._requestLastSeen(buddy) - elif message.split(u" ").pop(0) == u"\\gpp": - self.logger.info(u"Get Profile Picture! ") - self.sendMessageToXMPP(buddy, u"Fetching Profile Picture") - #self.call(u"contact_getProfilePicture", (buddy + u"@s.whatsapp.net",)) + elif message.split(" ").pop(0) == "\\gpp": + self.logger.info("Get Profile Picture! ") + self.sendMessageToXMPP(buddy, "Fetching Profile Picture") + #self.call("contact_getProfilePicture", (buddy + "@s.whatsapp.net",)) self.requestVCard(buddy) else: - if (u".jpg" in message.lower()) or (u".webp" in message.lower()): - #waId = self.call(u"message_imageSend", (buddy + u"@s.whatsapp.net", message, None, 0, None)) - #waId = self.call(u"message_send", (buddy + u"@s.whatsapp.net", message)) - if (u".jpg" in message.lower()): - self.imgType = u"jpg" - if (u".webp" in message.lower()): - self.imgType = u"webp" + if (".jpg" in message.lower()) or (".webp" in message.lower()): + #waId = self.call("message_imageSend", (buddy + "@s.whatsapp.net", message, None, 0, None)) + #waId = self.call("message_send", (buddy + "@s.whatsapp.net", message)) + if (".jpg" in message.lower()): + self.imgType = "jpg" + if (".webp" in message.lower()): + self.imgType = "webp" self.imgMsgId = ID - self.imgBuddy = buddy + u"@s.whatsapp.net" + self.imgBuddy = buddy + "@s.whatsapp.net" downloader = MediaDownloader(self.onDlsuccess, self.onDlerror) downloader.download(message) #self.imgMsgId = ID - #self.imgBuddy = buddy + u"@s.whatsapp.net" - elif u"geo:" in message.lower(): - self._sendLocation(buddy + u"@s.whatsapp.net", message, ID) + #self.imgBuddy = buddy + "@s.whatsapp.net" + elif "geo:" in message.lower(): + self._sendLocation(buddy + "@s.whatsapp.net", message, ID) else: - waId = self.sendTextMessage(sender + u'@s.whatsapp.net', message) + waId = self.sendTextMessage(sender + '@s.whatsapp.net', message) self.msgIDs[waId] = MsgIDs( ID, waId) - self.logger.info(u"WA Message send to %s with ID %s", buddy, waId) - #self.sendTextMessage(sender + u'@s.whatsapp.net', message) + self.logger.info("WA Message send to %s with ID %s", buddy, waId) + #self.sendTextMessage(sender + '@s.whatsapp.net', message) def executeCommand(self, command, room): - if command == u'\\leave': - self.logger.debug(u"Leaving room %s", room) + if command == '\\leave': + self.logger.debug("Leaving room %s", room) # Leave group on whatsapp side self.leaveGroup(room) # Delete Room on spectrum side group = self.groups[room] for jid in group.participants: - buddy = jid.split(u"@")[0] + buddy = jid.split("@")[0] try: nick = self.buddies[buddy].nick except KeyError: nick = buddy - if nick == u"": + if nick == "": nick = buddy if buddy == self.legacyName: nick = group.nick @@ -605,82 +649,81 @@ class Session(YowsupApp): def onSuccess(buddy, lastseen): timestamp = time.localtime(time.localtime()-lastseen) - timestring = time.strftime(u"%a, %d %b %Y %H:%M:%S", timestamp) - self.sendMessageToXMPP(buddy, u"%s (%s) %s" % (timestring, utils.ago(lastseen),str(lastseen))) + timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp) + self.sendMessageToXMPP(buddy, "%s (%s) %s" % (timestring, utils.ago(lastseen),str(lastseen))) def onError(errorIqEntity, originalIqEntity): - self.sendMessageToXMPP(errorIqEntity.getFrom(), u"LastSeen Error") + self.sendMessageToXMPP(errorIqEntity.getFrom(), "LastSeen Error") self.requestLastSeen(buddy, onSuccess, onError) def _sendLocation(self, buddy, message, ID): - #with open(u'/opt/transwhat/map.jpg', u'rb') as imageFile: + #with open('/opt/transwhat/map.jpg', 'rb') as imageFile: # raw = base64.b64encode(imageFile.read()) - latitude,longitude = message.split(u':')[1].split(u',') + latitude,longitude = message.split(':')[1].split(',') waId = self.sendLocation(buddy, float(latitude), float(longitude)) self.msgIDs[waId] = MsgIDs( ID, waId) - self.logger.info(u"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) - def sendMessageToXMPP(self, buddy, messageContent, timestamp = u"", nickname = u""): + def sendMessageToXMPP(self, buddy, messageContent, timestamp = "", nickname = ""): if timestamp: - timestamp = time.strftime(u"%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: - self.logger.debug(u"Message queued from %s to %s: %s", + self.logger.debug("Message queued from %s to %s: %s", buddy, self.legacyName, messageContent) self.offlineQueue.append((buddy, messageContent, timestamp)) else: - self.logger.debug(u"Message sent from %s to %s: %s", buddy, + self.logger.debug("Message sent from %s to %s: %s", buddy, self.legacyName, messageContent) - self.backend.handleMessage(self.user, buddy, messageContent, u"", - u"", timestamp) + self.backend.handleMessage(self.user, buddy, messageContent, "", + "", timestamp) + + def sendGroupMessageToXMPP(self, room, buddy, messageContent, timestamp = ""): + # self._refreshParticipants(room) + try: + nick = self.buddies[buddy].nick + except KeyError: + nick = buddy + if nick == "": + nick = buddy - def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = u"", defaultname = u""): if timestamp: - timestamp = time.strftime(u"%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: - self.logger.debug(u"Group message queued from %s to %s: %s", - number, room, messageContent) + self.logger.debug("Group message queued from %s to %s: %s", + buddy, room, messageContent) if room not in self.groupOfflineQueue: self.groupOfflineQueue[room] = [ ] self.groupOfflineQueue[room].append( - (number, messageContent, timestamp) + (buddy, messageContent, timestamp) ) else: - self.logger.debug(u"Group message sent from %s to %s: %s", - number, room, messageContent) + self.logger.debug("Group message sent from %s (%s) to %s: %s", + buddy, nick, room, messageContent) try: group = self.groups[room] - # Update nickname - try: - if defaultname != u"" and group.participants[number].nick == number: - group.changeNick(number, defaultname) - if self.buddies[number].nick != u"": - group.changeNick(number, self.buddies[number].nick) - except KeyError: - pass - nick = group.participants[number] if group.joined: - self.backend.handleMessage(self.user, room, messageContent, - nick, u"", timestamp) + self.backend.handleMessage(self.user,room, messageContent, + nick, "", timestamp) else: - self.bot.send(u"You have received a message in group: %s@%s" + self.bot.send("You have received a message in group: %s@%s" % (room, self.backend.spectrum_jid)) - self.bot.send(u"Join the group in order to reply") - self.bot.send(u"%s: %s" % (nick, messageContent)) + self.bot.send("Join the group in order to reply") + self.bot.send("%s: %s" % (nick, messageContent)) except KeyError: - self.logger.warn(u"Group is not in group list") + self.logger.warn("Group is not in group list") self.backend.handleMessage(self.user, self._shortenGroupId(room), - messageContent, number, u"", timestamp) + messageContent, nick, "", timestamp) def changeStatus(self, status): if status != self.status: - self.logger.info(u"Status changed: %s", status) + self.logger.info("Status changed: %s", status) self.status = status if status == protocol_pb2.STATUS_ONLINE \ @@ -692,19 +735,19 @@ class Session(YowsupApp): def changeStatusMessage(self, statusMessage): if (statusMessage != self.statusMessage) or (self.initialized == False): self.statusMessage = statusMessage - self.setStatus(statusMessage.encode(u'utf-8')) - self.logger.info(u"Status message changed: %s", statusMessage) + self.setStatus(statusMessage.encode('utf-8')) + self.logger.info("Status message changed: %s", statusMessage) #if self.initialized == False: # self.sendOfflineMessages() - # self.bot.call(u"welcome") + # self.bot.call("welcome") # self.initialized = True def sendOfflineMessages(self): # Flush Queues while self.offlineQueue: msg = self.offlineQueue.pop(0) - self.backend.handleMessage(self.user, msg[0], msg[1], u"", u"", msg[2]) + self.backend.handleMessage(self.user, msg[0], msg[1], "", "", msg[2]) # Called when user logs in to initialize the roster def loadBuddies(self, buddies): @@ -712,56 +755,56 @@ class Session(YowsupApp): # also for adding a new buddy def updateBuddy(self, buddy, nick, groups, image_hash = None): - if buddy != u"bot": + if buddy != "bot": self.buddies.update(buddy, nick, groups, image_hash) def removeBuddy(self, buddy): - if buddy != u"bot": - self.logger.info(u"Buddy removed: %s", buddy) + if buddy != "bot": + self.logger.info("Buddy removed: %s", buddy) self.buddies.remove(buddy) def requestVCard(self, buddy, ID=None): def onSuccess(response, request): - self.logger.debug(u'Sending VCard (%s) with image id %s', + self.logger.debug('Sending VCard (%s) with image id %s', ID, response.pictureId) image_hash = utils.sha1hash(response.pictureData) - self.logger.debug(u'Image hash is %s', image_hash) + self.logger.debug('Image hash is %s', image_hash) if ID != None: - self.backend.handleVCard(self.user, ID, buddy, u"", u"", response.pictureData) + self.backend.handleVCard(self.user, ID, buddy, "", "", response.pictureData) obuddy = self.buddies[buddy] self.updateBuddy(buddy, obuddy.nick, obuddy.groups, image_hash) - self.logger.debug(u'Requesting profile picture of %s', buddy) + self.logger.debug('Requesting profile picture of %s', buddy) self.requestProfilePicture(buddy, onSuccess = onSuccess) def onDlsuccess(self, path): - self.logger.info(u"Success: Image downloaded to %s", path) - os.rename(path, path+u"."+self.imgType) - if self.imgType != u"jpg": - im = Image.open(path+u"."+self.imgType) - im.save(path+u".jpg") - self.imgPath = path+u".jpg" + self.logger.info("Success: Image downloaded to %s", path) + os.rename(path, path+"."+self.imgType) + if self.imgType != "jpg": + im = Image.open(path+"."+self.imgType) + im.save(path+".jpg") + self.imgPath = path+".jpg" statinfo = os.stat(self.imgPath) name=os.path.basename(self.imgPath) - self.logger.info(u"Buddy %s",self.imgBuddy) + self.logger.info("Buddy %s",self.imgBuddy) self.image_send(self.imgBuddy, self.imgPath) - #self.logger.info(u"Sending picture %s of size %s with name %s",self.imgPath, statinfo.st_size, name) - #mtype = u"image" + #self.logger.info("Sending picture %s of size %s with name %s",self.imgPath, statinfo.st_size, name) + #mtype = "image" #sha1 = hashlib.sha256() - #fp = open(self.imgPath, u'rb') + #fp = open(self.imgPath, 'rb') #try: # sha1.update(fp.read()) # hsh = base64.b64encode(sha1.digest()) - # self.call(u"media_requestUpload", (hsh, mtype, os.path.getsize(self.imgPath))) + # self.call("media_requestUpload", (hsh, mtype, os.path.getsize(self.imgPath))) #finally: # fp.close() def onDlerror(self): - self.logger.info(u"Download Error. Sending message as is.") - waId = self.sendTextMessage(self.dlerror_sender + u'@s.whatsapp.net', self.dlerror_message) + self.logger.info("Download Error. Sending message as is.") + waId = self.sendTextMessage(self.dlerror_sender + '@s.whatsapp.net', self.dlerror_message) self.msgIDs[waId] = MsgIDs(self.dlerror_ID, waId) @@ -779,7 +822,7 @@ class Session(YowsupApp): def createThumb(self, size=100, raw=False): img = Image.open(self.imgPath) width, height = img.size - img_thumbnail = self.imgPath + u'_thumbnail' + img_thumbnail = self.imgPath + '_thumbnail' if width > height: nheight = float(height) / width * size @@ -789,47 +832,47 @@ class Session(YowsupApp): nheight = size img.thumbnail((nwidth, nheight), Image.ANTIALIAS) - img.save(img_thumbnail, u'JPEG') + img.save(img_thumbnail, 'JPEG') - with open(img_thumbnail, u'rb') as imageFile: + with open(img_thumbnail, 'rb') as imageFile: raw = base64.b64encode(imageFile.read()) return raw # Not used def onLocationReceived(self, messageId, jid, name, preview, latitude, longitude, receiptRequested, isBroadcast): - buddy = jid.split(u"@")[0] - self.logger.info(u"Location received from %s: %s, %s", buddy, latitude, longitude) + buddy = jid.split("@")[0] + self.logger.info("Location received from %s: %s, %s", buddy, latitude, longitude) - url = u"http://maps.google.de?%s" % urllib.urlencode({ u"q": u"%s %s" % (latitude, longitude) }) + url = "http://maps.google.de?%s" % urllib.urlencode({ "q": "%s %s" % (latitude, longitude) }) self.sendMessageToXMPP(buddy, utils.shorten(url)) - if receiptRequested: self.call(u"message_ack", (jid, messageId)) + if receiptRequested: self.call("message_ack", (jid, messageId)) def onGroupSubjectReceived(self, messageId, gjid, jid, subject, timestamp, receiptRequested): - room = gjid.split(u"@")[0] - buddy = jid.split(u"@")[0] + room = gjid.split("@")[0] + buddy = jid.split("@")[0] self.backend.handleSubject(self.user, room, subject, buddy) - if receiptRequested: self.call(u"subject_ack", (gjid, messageId)) + if receiptRequested: self.call("subject_ack", (gjid, messageId)) # Yowsup Notifications def onGroupParticipantRemoved(self, gjid, jid, author, timestamp, messageId, receiptRequested): - room = gjid.split(u"@")[0] - buddy = jid.split(u"@")[0] + room = gjid.split("@")[0] + buddy = jid.split("@")[0] - self.logger.info(u"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 - if receiptRequested: self.call(u"notification_ack", (gjid, messageId)) + if receiptRequested: self.call("notification_ack", (gjid, messageId)) def onContactProfilePictureUpdated(self, jid, timestamp, messageId, pictureId, receiptRequested): # TODO - if receiptRequested: self.call(u"notification_ack", (jid, messageId)) + if receiptRequested: self.call("notification_ack", (jid, messageId)) def onGroupPictureUpdated(self, jid, author, timestamp, messageId, pictureId, receiptRequested): # TODO - if receiptRequested: self.call(u"notification_ack", (jid, messageId)) + if receiptRequested: self.call("notification_ack", (jid, messageId)) diff --git a/transwhat.py b/transwhat.py index cb07119..935b185 100755 --- a/transwhat.py +++ b/transwhat.py @@ -1,12 +1,12 @@ #!/usr/bin/python -__author__ = u"Steffen Vogel" -__copyright__ = u"Copyright 2015, Steffen Vogel" -__license__ = u"GPLv3" -__maintainer__ = u"Steffen Vogel" -__email__ = u"post@steffenvogel.de" +__author__ = "Steffen Vogel" +__copyright__ = "Copyright 2015, Steffen Vogel" +__license__ = "GPLv3" +__maintainer__ = "Steffen Vogel" +__email__ = "post@steffenvogel.de" -u""" +""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -42,21 +42,21 @@ from yowsup.stacks import YowStack # Arguments parser = argparse.ArgumentParser() -parser.add_argument(u'--debug', action=u'store_true') -parser.add_argument(u'--host', type=str, required=True) -parser.add_argument(u'--port', type=int, required=True) -parser.add_argument(u'--service.backend_id', metavar=u"ID", type=int, required=True) -parser.add_argument(u'config', type=str) -parser.add_argument(u'-j', type=str, required=True) +parser.add_argument('--debug', action='store_true') +parser.add_argument('--host', type=str, 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('config', type=str) +parser.add_argument('-j', type=str, required=True) args, unknown = parser.parse_known_args() -YowConstants.PATH_STORAGE=u'/var/lib/spectrum2/' + args.j -loggingfile = u'/var/log/spectrum2/' + args.j + u'/backends/backend.log' +YowConstants.PATH_STORAGE='/var/lib/spectrum2/' + args.j +loggingfile = '/var/log/spectrum2/' + args.j + '/backends/backend.log' # Logging logging.basicConfig( \ filename=loggingfile,\ - format = u"%(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 \ ) @@ -77,9 +77,9 @@ io = IOChannel(args.host, args.port, handleTransportData, connectionClosed) plugin = WhatsAppBackend(io, args.j) plugin.handleBackendConfig({ - u'features': [ - (u'send_buddies_on_login', 1), - (u'muc', u'true'), + 'features': [ + ('send_buddies_on_login', 1), + ('muc', 'true'), ], }) @@ -87,7 +87,7 @@ while True: try: asyncore.loop(timeout=1.0, count=10, use_poll = True) try: - callback = YowStack._YowStack__detachedQueue.get(False) #doesnu't block + callback = YowStack._YowStack__detachedQueue.get(False) #doesn't block callback() except Queue.Empty: pass diff --git a/utils.py b/utils.py index b325922..f6d120c 100644 --- a/utils.py +++ b/utils.py @@ -1,10 +1,10 @@ -__author__ = u"Steffen Vogel" -__copyright__ = u"Copyright 2015, Steffen Vogel" -__license__ = u"GPLv3" -__maintainer__ = u"Steffen Vogel" -__email__ = u"post@steffenvogel.de" +__author__ = "Steffen Vogel" +__copyright__ = "Copyright 2015, Steffen Vogel" +__license__ = "GPLv3" +__maintainer__ = "Steffen Vogel" +__email__ = "post@steffenvogel.de" -u""" +""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -24,10 +24,9 @@ u""" import e4u import base64 import hashlib -import base64 def ago(secs): - periods = [u"second", u"minute", u"hour", u"day", u"week", u"month", u"year", u"decade"] + periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"] lengths = [60, 60, 24, 7,4.35, 12, 10] j = 0 @@ -39,16 +38,16 @@ def ago(secs): j += 1 period = periods[j] - if diff > 1: period += u"s" + if diff > 1: period += "s" - return u"%d %s ago" % (diff, period) + return "%d %s ago" % (diff, period) def softToUni(message): - message = message.decode(u"utf-8") + message = message.decode("utf-8") return e4u.translate(message, reverse=False, **e4u.SOFTBANK_TRANSLATE_PROFILE) def decodePassword(password): - return base64.b64decode(bytes(password.encode(u"utf-8"))) + return base64.b64decode(bytes(password.encode("utf-8"))) def sha1hash(data): - return base64.b64encode(hashlib.sha1(data).digest()) + return hashlib.sha1(data).hexdigest() diff --git a/whatsappbackend.py b/whatsappbackend.py index 7ef2c13..a2ddc9e 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -1,10 +1,10 @@ -__author__ = u"Steffen Vogel" -__copyright__ = u"Copyright 2015, Steffen Vogel" -__license__ = u"GPLv3" -__maintainer__ = u"Steffen Vogel" -__email__ = u"post@steffenvogel.de" +__author__ = "Steffen Vogel" +__copyright__ = "Copyright 2015, Steffen Vogel" +__license__ = "GPLv3" +__maintainer__ = "Steffen Vogel" +__email__ = "post@steffenvogel.de" -u""" +""" This file is part of transWhat transWhat is free software: you can redistribute it and/or modify @@ -38,11 +38,11 @@ class WhatsAppBackend(SpectrumBackend): # Used to prevent duplicate messages self.lastMessage = {} - self.logger.debug(u"Backend started") + self.logger.debug("Backend started") # RequestsHandlers def handleLoginRequest(self, user, legacyName, password, extra): - self.logger.debug(u"handleLoginRequest(user=%s, legacyName=%s)", user, legacyName) + self.logger.debug("handleLoginRequest(user=%s, legacyName=%s)", user, legacyName) if user not in self.sessions: self.sessions[user] = Session(self, user, legacyName, extra) @@ -52,13 +52,13 @@ class WhatsAppBackend(SpectrumBackend): self.sessions[user].login(password) def handleLogoutRequest(self, user, legacyName): - self.logger.debug(u"handleLogoutRequest(user=%s, legacyName=%s)", user, legacyName) + self.logger.debug("handleLogoutRequest(user=%s, legacyName=%s)", user, legacyName) if user in self.sessions: self.sessions[user].logout() del self.sessions[user] - def handleMessageSendRequest(self, user, buddy, message, xhtml = u"", ID = 0): - self.logger.debug(u"handleMessageSendRequest(user=%s, buddy=%s, message=%s, xhtml = %s)", user, buddy, message, xhtml) + def handleMessageSendRequest(self, user, buddy, message, xhtml = "", ID = 0): + self.logger.debug("handleMessageSendRequest(user=%s, buddy=%s, message=%s, xhtml = %s)", user, buddy, message, xhtml) # For some reason spectrum occasionally sends to identical messages to # a buddy, one to the bare jid and one to /bot. This causes duplicate # messages. Since it is unlikely a user wants to send the same message @@ -73,48 +73,48 @@ class WhatsAppBackend(SpectrumBackend): usersMessage[buddy] = message def handleJoinRoomRequest(self, user, room, nickname, pasword): - self.logger.debug(u"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) def handleLeaveRoomRequest(self, user, room): - self.logger.debug(u"handleLeaveRoomRequest(user=%s, room=%s)", user, room) + self.logger.debug("handleLeaveRoomRequest(user=%s, room=%s)", user, room) self.sessions[user].leaveRoom(room) def handleStatusChangeRequest(self, user, status, statusMessage): - self.logger.debug(u"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].changeStatus(status) def handleBuddies(self, buddies): - u"""Called when user logs in. Used to initialize roster.""" - self.logger.debug(u"handleBuddies(buddies=%s)", buddies) + """Called when user logs in. Used to initialize roster.""" + self.logger.debug("handleBuddies(buddies=%s)", buddies) buddies = [b for b in buddies.buddy] if len(buddies) > 0: user = buddies[0].userName self.sessions[user].loadBuddies(buddies) def handleBuddyUpdatedRequest(self, user, buddy, nick, groups): - self.logger.debug(u"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, str(groups)) self.sessions[user].updateBuddy(buddy, nick, groups) def handleBuddyRemovedRequest(self, user, buddy, groups): - self.logger.debug(u"handleBuddyRemovedRequest(user=%s, buddy=%s, groups=%s)", user, buddy, str(groups)) + self.logger.debug("handleBuddyRemovedRequest(user=%s, buddy=%s, groups=%s)", user, buddy, str(groups)) self.sessions[user].removeBuddy(buddy) def handleTypingRequest(self, user, buddy): - self.logger.debug(u"handleTypingRequest(user=%s, buddy=%s)", user, buddy) + self.logger.debug("handleTypingRequest(user=%s, buddy=%s)", user, buddy) self.sessions[user].sendTypingStarted(buddy) def handleTypedRequest(self, user, buddy): - self.logger.debug(u"handleTypedRequest(user=%s, buddy=%s)", user, buddy) + self.logger.debug("handleTypedRequest(user=%s, buddy=%s)", user, buddy) self.sessions[user].sendTypingStopped(buddy) def handleStoppedTypingRequest(self, user, buddy): - self.logger.debug(u"handleStoppedTypingRequest(user=%s, buddy=%s)", user, buddy) + self.logger.debug("handleStoppedTypingRequest(user=%s, buddy=%s)", user, buddy) self.sessions[user].sendTypingStopped(buddy) def handleVCardRequest(self, user, buddy, ID): - self.logger.debug(u"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) @@ -129,7 +129,7 @@ class WhatsAppBackend(SpectrumBackend): pass def handleFTStartRequest(self, user, buddy, fileName, size, ftID): - self.logger.debug(u'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) def handleFTFinishRequest(self, user, buddy, fileName, size, ftID): @@ -145,7 +145,7 @@ class WhatsAppBackend(SpectrumBackend): pass def handleMessageAckRequest(self, user, legacyName, ID = 0): - self.logger.info(u"Meassage ACK request for %s !!",legacyName) + self.logger.info("Meassage ACK request for %s !!",legacyName) def sendData(self, data): self.io.sendData(data) diff --git a/yowsupwrapper.py b/yowsupwrapper.py index df08b98..048ba98 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -78,7 +78,7 @@ class YowsupApp(object): ) def login(self, username, password): - u"""Login to yowsup + """Login to yowsup Should result in onAuthSuccess or onAuthFailure to be called. @@ -102,20 +102,20 @@ class YowsupApp(object): self.stack.broadcastEvent( YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT)) except TypeError as e: # Occurs when password is not correctly formated - self.onAuthFailure(u'password not base64 encoded') + self.onAuthFailure('password not base64 encoded') # try: # self.stack.loop(timeout=0.5, discrete=0.5) # except AuthError as e: # For some reason Yowsup throws an exception -# self.onAuthFailure(u"%s" % e) +# self.onAuthFailure("%s" % e) def logout(self): - u""" + """ Logout from whatsapp """ self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT)) def sendReceipt(self, _id, _from, read, participant): - u""" + """ Send a receipt (delivered: double-tick, read: blue-ticks) Args: @@ -128,7 +128,7 @@ class YowsupApp(object): self.sendEntity(receipt) def sendTextMessage(self, to, message): - u""" + """ Sends a text message Args: @@ -140,7 +140,7 @@ class YowsupApp(object): return messageEntity.getId() def sendLocation(self, to, latitude, longitude): - messageEntity = LocationMediaMessageProtocolEntity(latitude,longitude, None, None, u"raw", to = to) + messageEntity = LocationMediaMessageProtocolEntity(latitude,longitude, None, None, "raw", to = to) self.sendEntity(messageEntity) return messageEntity.getId() @@ -170,14 +170,14 @@ class YowsupApp(object): mediaUploader.start() def onRequestUploadError(self, jid, path, errorRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity): - self.logger.error(u"Request upload for file %s for %s failed" % (path, jid)) + self.logger.error("Request upload for file %s for %s failed" % (path, jid)) def onUploadError(self, filePath, jid, url): - #logger.error(u"Upload file %s to %s for %s failed!" % (filePath, url, jid)) - self.logger.error(u"Upload Error!") + #logger.error("Upload file %s to %s for %s failed!" % (filePath, url, jid)) + self.logger.error("Upload Error!") def onUploadProgress(self, filePath, jid, url, progress): - #sys.stdout.write(u"%s => %s, %d%% \r" % (os.path.basename(filePath), jid, progress)) + #sys.stdout.write("%s => %s, %d%% \r" % (os.path.basename(filePath), jid, progress)) #sys.stdout.flush() pass @@ -197,7 +197,7 @@ class YowsupApp(object): def sendPresence(self, available): - u""" + """ Send presence to whatsapp Args: @@ -209,42 +209,42 @@ class YowsupApp(object): self.sendEntity(UnavailablePresenceProtocolEntity()) def subscribePresence(self, phone_number): - u""" + """ Subscribe to presence updates from phone_number Args: - phone_number: (str) The cellphone number of the person to subscribe to """ - self.logger.debug(u"Subscribing to Presence updates from %s", (phone_number)) - jid = phone_number + u'@s.whatsapp.net' + self.logger.debug("Subscribing to Presence updates from %s", (phone_number)) + jid = phone_number + '@s.whatsapp.net' entity = SubscribePresenceProtocolEntity(jid) self.sendEntity(entity) def unsubscribePresence(self, phone_number): - u""" + """ Unsubscribe to presence updates from phone_number Args: - phone_number: (str) The cellphone number of the person to unsubscribe from """ - jid = phone_number + u'@s.whatsapp.net' + jid = phone_number + '@s.whatsapp.net' entity = UnsubscribePresenceProtocolEntity(jid) self.sendEntity(entity) def leaveGroup(self, group): - u""" + """ Permanently leave a WhatsApp group Args: - group: (str) the group id (e.g. 27831788123-144024456) """ - entity = LeaveGroupsIqProtocolEntity([group + u'@g.us']) + entity = LeaveGroupsIqProtocolEntity([group + '@g.us']) self.sendEntity(entity) def setStatus(self, statusText): - u""" + """ Send status to whatsapp Args: @@ -254,14 +254,14 @@ class YowsupApp(object): self.sendIq(iq) def sendTyping(self, phoneNumber, typing): - u""" + """ Notify buddy using phoneNumber that you are typing to him Args: - phoneNumber: (str) cellphone number of the buddy you are typing to. - typing: (bool) True if you are typing, False if you are not """ - jid = phoneNumber + u'@s.whatsapp.net' + jid = phoneNumber + '@s.whatsapp.net' if typing: state = OutgoingChatstateProtocolEntity( ChatstateProtocolEntity.STATE_TYPING, jid @@ -273,7 +273,7 @@ class YowsupApp(object): self.sendEntity(state) def sendSync(self, contacts, delta = False, interactive = True): - u""" + """ You need to sync new contacts before you interact with them, failure to do so could result in a temporary ban. @@ -293,7 +293,7 @@ class YowsupApp(object): self.sendIq(iq) def requestStatuses(self, contacts, success = None, failure = None): - u""" + """ Request the statuses of a number of users. Args: @@ -302,19 +302,19 @@ class YowsupApp(object): - success: (func) called when request is successful - failure: (func) called when request has failed """ - iq = GetStatusesIqProtocolEntity([c + u'@s.whatsapp.net' for c in contacts]) + iq = GetStatusesIqProtocolEntity([c + '@s.whatsapp.net' for c in contacts]) def onSuccess(response, request): - self.logger.debug(u"Received Statuses %s", response) + self.logger.debug("Received Statuses %s", response) s = {} for k, v in response.statuses.iteritems(): - s[k.split(u'@')[0]] = v + s[k.split('@')[0]] = v success(s) self.sendIq(iq, onSuccess = onSuccess, onError = failure) def requestLastSeen(self, phoneNumber, success = None, failure = None): - u""" + """ Requests when user was last seen. Args: - phone_number: (str) the phone number of the user @@ -323,22 +323,22 @@ class YowsupApp(object): since last seen. - failure: (func) called when request has failed """ - iq = LastseenIqProtocolEntity(phoneNumber + u'@s.whatsapp.net') + iq = LastseenIqProtocolEntity(phoneNumber + '@s.whatsapp.net') self.sendIq(iq, onSuccess = partial(self._lastSeenSuccess, success), onError = failure) def _lastSeenSuccess(self, success, response, request): - success(response._from.split(u'@')[0], response.seconds) + success(response._from.split('@')[0], response.seconds) def requestProfilePicture(self, phoneNumber, onSuccess = None, onFailure = None): - u""" + """ Requests profile picture of whatsapp user Args: - phoneNumber: (str) the phone number of the user - onSuccess: (func) called when request is successfully processed. - onFailure: (func) called when request has failed """ - iq = GetPictureIqProtocolEntity(phoneNumber + u'@s.whatsapp.net') + iq = GetPictureIqProtocolEntity(phoneNumber + '@s.whatsapp.net') self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def requestGroupsList(self, onSuccess = None, onFailure = None): @@ -346,7 +346,7 @@ class YowsupApp(object): self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def requestGroupInfo(self, group, onSuccess = None, onFailure = None): - u""" + """ Request info on a specific group (includes participants, subject, owner etc.) Args: @@ -354,11 +354,11 @@ class YowsupApp(object): - onSuccess: (func) called when request is successfully processed. - onFailure: (func) called when request is has failed """ - iq = InfoGroupsIqProtocolEntity(group + u'@g.us') + iq = InfoGroupsIqProtocolEntity(group + '@g.us') self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t): - u""" + """ Called when login is successful. Args: @@ -373,7 +373,7 @@ class YowsupApp(object): pass def onAuthFailure(self, reason): - u""" + """ Called when login is a failure Args: @@ -382,7 +382,7 @@ class YowsupApp(object): pass def onReceipt(self, _id, _from, timestamp, type, participant, offline, items): - u""" + """ Called when a receipt is received (double tick or blue tick) Args @@ -398,7 +398,7 @@ class YowsupApp(object): pass def onAck(self, _id,_class, _from, timestamp): - u""" + """ Called when Ack is received Args: @@ -410,7 +410,7 @@ class YowsupApp(object): pass def onPresenceReceived(self, _type, name, _from, last): - u""" + """ Called when presence (e.g. available, unavailable) is received from whatsapp @@ -423,12 +423,12 @@ class YowsupApp(object): pass def onDisconnect(self): - u""" + """ Called when disconnected from whatsapp """ def onContactTyping(self, number): - u""" + """ Called when contact starts to type Args: @@ -437,7 +437,7 @@ class YowsupApp(object): pass def onContactPaused(self, number): - u""" + """ Called when contact stops typing Args: @@ -446,7 +446,7 @@ class YowsupApp(object): pass def onTextMessage(self, _id, _from, to, notify, timestamp, participant, offline, retry, body): - u""" + """ Called when text message is received Args: @@ -463,7 +463,7 @@ class YowsupApp(object): pass def onImage(self, entity): - u""" + """ Called when image message is received Args: @@ -472,7 +472,7 @@ class YowsupApp(object): pass def onAudio(self, entity): - u""" + """ Called when audio message is received Args: @@ -482,7 +482,7 @@ class YowsupApp(object): def onVideo(self, entity): - u""" + """ Called when video message is received Args: @@ -491,7 +491,7 @@ class YowsupApp(object): pass def onLocation(self, entity): - u""" + """ Called when location message is received Args: @@ -500,7 +500,7 @@ class YowsupApp(object): pass def onVCard(self, _id, _from, name, card_data, to, notify, timestamp, participant): - u""" + """ Called when VCard message is received Args: @@ -516,15 +516,15 @@ class YowsupApp(object): pass def onAddedToGroup(self, entity): - u"""Called when the user has been added to a new group""" + """Called when the user has been added to a new group""" pass def onParticipantsAddedToGroup(self, entity): - u"""Called when participants have been added to a group""" + """Called when participants have been added to a group""" pass def onParticipantsRemovedFromGroup(self, group, participants): - u"""Called when participants have been removed from a group + """Called when participants have been removed from a group Args: - group: (str) id of the group (e.g. 27831788123-144024456) @@ -533,7 +533,7 @@ class YowsupApp(object): pass def sendEntity(self, entity): - u"""Sends an entity down the stack (as if YowsupAppLayer called toLower)""" + """Sends an entity down the stack (as if YowsupAppLayer called toLower)""" self.stack.broadcastEvent(YowLayerEvent(YowsupAppLayer.TO_LOWER_EVENT, entity = entity )) @@ -551,9 +551,9 @@ class YowsupApp(object): from yowsup.layers.interface import YowInterfaceLayer, ProtocolEntityCallback class YowsupAppLayer(YowInterfaceLayer): - EVENT_START = u'transwhat.event.YowsupAppLayer.start' - TO_LOWER_EVENT = u'transwhat.event.YowsupAppLayer.toLower' - SEND_IQ = u'transwhat.event.YowsupAppLayer.sendIq' + EVENT_START = 'transwhat.event.YowsupAppLayer.start' + TO_LOWER_EVENT = 'transwhat.event.YowsupAppLayer.toLower' + SEND_IQ = 'transwhat.event.YowsupAppLayer.sendIq' def onEvent(self, layerEvent): # We cannot pass instance varaibles in through init, so we use an event @@ -561,24 +561,24 @@ class YowsupAppLayer(YowInterfaceLayer): # Return False if you want the event to propogate down the stack # return True otherwise if layerEvent.getName() == YowsupAppLayer.EVENT_START: - self.caller = layerEvent.getArg(u'caller') + self.caller = layerEvent.getArg('caller') self.logger = logging.getLogger(self.__class__.__name__) return True elif layerEvent.getName() == YowNetworkLayer.EVENT_STATE_DISCONNECTED: self.caller.onDisconnect() return True elif layerEvent.getName() == YowsupAppLayer.TO_LOWER_EVENT: - self.toLower(layerEvent.getArg(u'entity')) + self.toLower(layerEvent.getArg('entity')) return True elif layerEvent.getName() == YowsupAppLayer.SEND_IQ: - iq = layerEvent.getArg(u'iq') - success = layerEvent.getArg(u'success') - failure = layerEvent.getArg(u'failure') + iq = layerEvent.getArg('iq') + success = layerEvent.getArg('success') + failure = layerEvent.getArg('failure') self._sendIq(iq, success, failure) return True return False - @ProtocolEntityCallback(u'success') + @ProtocolEntityCallback('success') def onAuthSuccess(self, entity): # entity is SuccessProtocolEntity status = entity.status @@ -587,21 +587,21 @@ class YowsupAppLayer(YowInterfaceLayer): expiration = entity.expiration props = entity.props nonce = entity.nonce - t = entity.t # I donu't know what this is + t = entity.t # I don't know what this is self.caller.onAuthSuccess(status, kind, creation, expiration, props, nonce, t) - @ProtocolEntityCallback('failureu') + @ProtocolEntityCallback('failure') def onAuthFailure(self, entity): # entity is FailureProtocolEntity reason = entity.reason self.caller.onAuthFailure(reason) - @ProtocolEntityCallback('receiptu') + @ProtocolEntityCallback('receipt') def onReceipt(self, entity): """Sends ack automatically""" # entity is IncomingReceiptProtocolEntity ack = OutgoingAckProtocolEntity(entity.getId(), - 'receiptu', entity.getType(), entity.getFrom()) + 'receipt', entity.getType(), entity.getFrom()) self.toLower(ack) _id = entity._id _from = entity._from @@ -612,7 +612,7 @@ class YowsupAppLayer(YowInterfaceLayer): items = entity.items self.caller.onReceipt(_id, _from, timestamp, type, participant, offline, items) - @ProtocolEntityCallback('acku') + @ProtocolEntityCallback('ack') def onAck(self, entity): # entity is IncomingAckProtocolEntity self.caller.onAck( @@ -622,7 +622,7 @@ class YowsupAppLayer(YowInterfaceLayer): entity.timestamp ) - @ProtocolEntityCallback('notificationu') + @ProtocolEntityCallback('notification') def onNotification(self, entity): """ Sends ack automatically @@ -635,11 +635,11 @@ class YowsupAppLayer(YowInterfaceLayer): self.caller.onParticipantsAddedToGroup(entity) elif isinstance(entity, RemoveGroupsNotificationProtocolEntity): self.caller.onParticipantsRemovedFromGroup( - entity.getGroupId().split('@u')[0], + entity.getGroupId().split('@')[0], entity.getParticipants().keys() ) - @ProtocolEntityCallback('messageu') + @ProtocolEntityCallback('message') def onMessageReceived(self, entity): self.logger.debug("Received Message: %s", entity) if entity.getType() == MessageProtocolEntity.MESSAGE_TYPE_TEXT: @@ -677,7 +677,7 @@ class YowsupAppLayer(YowInterfaceLayer): elif isinstance(entity, LocationMediaMessageProtocolEntity): self.caller.onLocation(entity) - @ProtocolEntityCallback('presenceu') + @ProtocolEntityCallback('presence') def onPresenceReceived(self, presence): _type = presence.getType() name = presence.getName() @@ -685,7 +685,7 @@ class YowsupAppLayer(YowInterfaceLayer): last = presence.getLast() self.caller.onPresenceReceived(_type, name, _from, last) - @ProtocolEntityCallback('chatstateu') + @ProtocolEntityCallback('chatstate') def onChatstate(self, chatstate): number = chatstate._from.split('@')[0] if chatstate.getState() == ChatstateProtocolEntity.STATE_TYPING: From d4a05938740c80aa3bef5e4e9a820ec8beb4e069 Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 1 Jan 2016 11:42:11 +0200 Subject: [PATCH 16/46] Replay changes from c40d477667 excluding fae4c648e fae4c648e was causing encoding errors. It's changes have been reverted. --- USAGE.md | 1 - buddy.py | 10 +++-- group.py | 67 +++++++++++++++++++++++++++- session.py | 129 ++++++++++++++++++----------------------------------- utils.py | 3 +- 5 files changed, 118 insertions(+), 92 deletions(-) diff --git a/USAGE.md b/USAGE.md index dcea058..67b4c8b 100644 --- a/USAGE.md +++ b/USAGE.md @@ -28,7 +28,6 @@ The bot is one of the contacts every user has in its contact list. It offers you | ------------ | --------------- | | `\help` | show this message | | `\prune` | clear your buddylist | -| `\sync` | sync your imported contacts with WhatsApp | | `\lastseen` | request last online timestamp from buddy | | `\leave` | permanently leave group chat | | `\groups` | print all attended groups | diff --git a/buddy.py b/buddy.py index 8e722e3..08ce734 100644 --- a/buddy.py +++ b/buddy.py @@ -25,6 +25,7 @@ from Spectrum2 import protocol_pb2 import logging import time +import utils class Buddy(): @@ -34,7 +35,7 @@ class Buddy(): self.number = number self.groups = groups self.image_hash = image_hash if image_hash is not None else "" - self.statusMsg = "" + self.statusMsg = u"" self.lastseen = 0 self.presence = 0 @@ -62,7 +63,7 @@ class BuddyList(dict): for buddy in buddies: number = buddy.buddyName nick = buddy.alias - statusMsg = buddy.statusMessage + statusMsg = buddy.statusMessage.decode('utf-8') groups = [g for g in buddy.group] image_hash = buddy.iconHash self[number] = Buddy(self.owner, number, nick, statusMsg, @@ -106,7 +107,10 @@ class BuddyList(dict): self.logger.debug("%s received statuses of: %s", self.user, contacts) for number, (status, time) in contacts.iteritems(): buddy = self[number] - buddy.statusMsg = status + if status is None: + buddy.statusMsg = "" + else: + buddy.statusMsg = utils.softToUni(status) self.updateSpectrum(buddy) diff --git a/group.py b/group.py index 8708fe7..993bae4 100644 --- a/group.py +++ b/group.py @@ -21,14 +21,77 @@ __email__ = "post@steffenvogel.de" along with transWhat. If not, see . """ +from Spectrum2 import protocol_pb2 + class Group(): - def __init__(self, id, owner, subject, subjectOwner): + def __init__(self, id, owner, subject, subjectOwner, backend, user): self.id = id self.subject = subject self.subjectOwner = subjectOwner self.owner = owner self.joined = False + self.backend = backend + self.user = user self.nick = "me" - self.participants = [] + # Participants is a number -> nickname dict + self.participants = {} + + def addParticipants(self, participants, buddies, yourNumber): + """ + Adds participants to the group. + + Args: + - participants: (Iterable) phone numbers of participants + - buddies: (dict) Used to get the nicknames of the participants + - yourNumber: The number you are using + """ + for jid in participants: + number = jid.split('@')[0] + try: + nick = buddies[number].nick + except KeyError: + nick = number + if number == yourNumber: + nick = self.nick + if nick == "": + nick = number + self.participants[number] = nick + + def sendParticipantsToSpectrum(self, yourNumber): + for number, nick in self.participants.iteritems(): + if number == self.owner: + flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR + else: + flags = protocol_pb2.PARTICIPANT_FLAG_NONE + if number == yourNumber: + flags = flags | protocol_pb2.PARTICIPANT_FLAG_ME + + self._updateParticipant(number, flags, protocol_pb2.STATUS_ONLINE) + + def removeParticipants(self, participants): + for jid in participants: + number = jid.split('@')[0] + nick = self.participants[number] + flags = protocol_pb2.PARTICIPANT_FLAG_NONE + self._updateParticipant(number, flags, protocol_pb2.STATUS_NONE) + del self.participants[number] + + def changeNick(self, number, new_nick): + if self.participants[number] == new_nick: + return + if number == self.owner: + flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR + else: + flags = protocol_pb2.PARTICIPANT_FLAG_NONE + self._updateParticipant(number, flags, protocol_pb2.STATUS_ONLINE, new_nick) + self.participants[number] = new_nick + + def _updateParticipant(self, number, flags, status, newNick = ""): + nick = self.participants[number] + # Notice the status message is the buddy's number + if self.joined: + self.backend.handleParticipantChanged( + self.user, nick, self.id, flags, + status, number, newname = newNick) diff --git a/session.py b/session.py index d481bfe..c31b75f 100644 --- a/session.py +++ b/session.py @@ -143,9 +143,10 @@ class Session(YowsupApp): oroom.subjectOwner = subjectOwner oroom.subject = subject else: - self.groups[room] = Group(room, owner, subject, subjectOwner) + self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) # self.joinRoom(self._shortenGroupId(room), self.user.split("@")[0]) - self.groups[room].participants = group.getParticipants().keys() + self.groups[room].addParticipants(group.getParticipants().keys(), + self.buddies, self.legacyName) #self._addParticipantsToRoom(room, group.getParticipants()) @@ -172,13 +173,15 @@ class Session(YowsupApp): self.legacyName, room, nick) group = self.groups[room] + group.joined = True group.nick = nick + group.participants[self.legacyName] = nick try: - ownerNick = self.buddies[group.subjectOwner].nick + ownerNick = group.participants[group.subjectOwner] except KeyError: ownerNick = group.subjectOwner - self._refreshParticipants(room) + group.sendParticipantsToSpectrum(self.legacyName) self.backend.handleSubject(self.user, self._shortenGroupId(room), group.subject, ownerNick) self.logger.debug("Room subject: room=%s, subject=%s", @@ -186,7 +189,6 @@ class Session(YowsupApp): self.backend.handleRoomNicknameChanged( self.user, self._shortenGroupId(room), group.subject ) - group.joined = True else: self.logger.warn("Room doesn't exist: %s", room) @@ -198,29 +200,6 @@ class Session(YowsupApp): else: self.logger.warn("Room doesn't exist: %s. Unable to leave.", room) - def _refreshParticipants(self, room): - group = self.groups[room] - for jid in group.participants: - buddy = jid.split("@")[0] - self.logger.info("Added %s to room %s", buddy, room) - try: - nick = self.buddies[buddy].nick - except KeyError: - nick = buddy - if nick == "": - nick = buddy - - if buddy == group.owner: - flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR - else: - flags = protocol_pb2.PARTICIPANT_FLAG_NONE - if buddy == self.legacyName: - nick = group.nick - flags = flags | protocol_pb2.PARTICIPANT_FLAG_ME - self.backend.handleParticipantChanged( - self.user, nick, self._shortenGroupId(room), flags, - protocol_pb2.STATUS_ONLINE, buddy) - def _lastSeen(self, number, seconds): self.logger.debug("Last seen %s at %s seconds" % (number, str(seconds))) if seconds < 60: @@ -300,19 +279,10 @@ class Session(YowsupApp): buddy, self.legacyName, messageContent, timestamp) if participant is not None: # Group message partname = participant.split('@')[0] - try: - part = self.buddies[partname] - if part.nick == "": - part.nick = notify - self.backend.handleParticipantChanged( - self.user, partname, self._shortenGroupId(buddy), - protocol_pb2.PARTICIPANT_FLAG_NONE, - protocol_pb2.STATUS_ONLINE, "", part.nick - ) # TODO - except KeyError: - self.updateBuddy(partname, notify, []) + if notify is None: + notify = ""; self.sendGroupMessageToXMPP(buddy, partname, messageContent, - timestamp) + timestamp, notify) else: self.sendMessageToXMPP(buddy, messageContent, timestamp) # isBroadcast always returns false, I'm not sure how to get a broadcast @@ -441,11 +411,8 @@ class Session(YowsupApp): subjectOwner = group.getSubjectOwnerJid(full = False) subject = utils.softToUni(group.getSubject()) - self.groups[room] = Group(room, owner, subject, subjectOwner) - self.groups[room].participants = group.getParticipants().keys() -# self.joinRoom(self._shortenGroupId(room), self.user.split("@")[0]) - - #self._addParticipantsToRoom(room, group.getParticipants()) + self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user) + self.groups[room].addParticipants(group.getParticipants, self.buddies, self.legacyName) self.bot.send("You have been added to group: %s@%s (%s)" % (self._shortenGroupId(room), subject, self.backend.spectrum_jid)) @@ -453,29 +420,14 @@ class Session(YowsupApp): def onParticipantsAddedToGroup(self, group): self.logger.debug("Participants added to group: %s", group) room = group.getGroupId().split('@')[0] - self.groups[room].participants.extend(group.getParticipants()) - self._refreshParticipants(room) + self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName) + self.groups[room].sendParticipantsToSpectrum(self.legacyName) # Called by superclass def onParticipantsRemovedFromGroup(self, room, participants): self.logger.debug("Participants removed from group: %s, %s", room, participants) - group = self.groups[room] - for jid in participants: - group.participants.remove(jid) - buddy = jid.split("@")[0] - try: - nick = self.buddies[buddy].nick - except KeyError: - nick = buddy - if nick == "": - nick = buddy - if buddy == self.legacyName: - nick = group.nick - flags = protocol_pb2.PARTICIPANT_FLAG_NONE - self.backend.handleParticipantChanged( - self.user, nick, self._shortenGroupId(room), flags, - protocol_pb2.STATUS_NONE, buddy) + self.groups[room].removeParticipants(participants) def onPresenceReceived(self, _type, name, jid, lastseen): self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) @@ -542,13 +494,19 @@ class Session(YowsupApp): elif "-" in sender: # group msg if "/" in sender: # directed at single user room, nick = sender.split("/") - for buddy, buddy3 in self.buddies.iteritems(): - self.logger.info("Group buddy=%s nick=%s", buddy, - buddy3.nick) - if buddy3.nick == nick: - nick = buddy - waId = self.sendTextMessage(nick + '@s.whatsapp.net', message) - self.msgIDs[waId] = MsgIDs( ID, waId) + group = self.groups[room] + number = None + for othernumber, othernick in group.participants.iteritems(): + if othernick == nick: + number = othernumber + break + if number is not None: + self.logger.debug("Private message sent from %s to %s", self.legacyName, number) + waId = self.sendTextMessage(number + '@s.whatsapp.net', message) + self.msgIDs[waId] = MsgIDs( ID, waId) + else: + self.logger.error("Attempted to send private message to non-existent user") + self.logger.debug("%s to %s in %s", self.legacyName, nick, room) else: room = sender if message[0] == '\\' and message[:1] != '\\\\': @@ -680,35 +638,36 @@ class Session(YowsupApp): self.backend.handleMessage(self.user, buddy, messageContent, "", "", timestamp) - def sendGroupMessageToXMPP(self, room, buddy, messageContent, timestamp = ""): - # self._refreshParticipants(room) - try: - nick = self.buddies[buddy].nick - except KeyError: - nick = buddy - if nick == "": - nick = buddy - + def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = u"", defaultname = u""): if timestamp: timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp)) if self.initialized == False: self.logger.debug("Group message queued from %s to %s: %s", - buddy, room, messageContent) + number, room, messageContent) if room not in self.groupOfflineQueue: self.groupOfflineQueue[room] = [ ] self.groupOfflineQueue[room].append( - (buddy, messageContent, timestamp) + (number, messageContent, timestamp) ) else: - self.logger.debug("Group message sent from %s (%s) to %s: %s", - buddy, nick, room, messageContent) + self.logger.debug("Group message sent from %s to %s: %s", + number, room, messageContent) try: group = self.groups[room] + # Update nickname + try: + if defaultname != "" and group.participants[number].nick == number: + group.changeNick(number, defaultname) + if self.buddies[number].nick != "": + group.changeNick(number, self.buddies[number].nick) + except KeyError: + pass + nick = group.participants[number] if group.joined: - self.backend.handleMessage(self.user,room, messageContent, + self.backend.handleMessage(self.user, room, messageContent, nick, "", timestamp) else: self.bot.send("You have received a message in group: %s@%s" @@ -718,7 +677,7 @@ class Session(YowsupApp): except KeyError: self.logger.warn("Group is not in group list") self.backend.handleMessage(self.user, self._shortenGroupId(room), - messageContent, nick, "", timestamp) + messageContent, number, "", timestamp) def changeStatus(self, status): diff --git a/utils.py b/utils.py index f6d120c..b9fdf11 100644 --- a/utils.py +++ b/utils.py @@ -24,6 +24,7 @@ __email__ = "post@steffenvogel.de" import e4u import base64 import hashlib +import base64 def ago(secs): periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"] @@ -50,4 +51,4 @@ def decodePassword(password): return base64.b64decode(bytes(password.encode("utf-8"))) def sha1hash(data): - return hashlib.sha1(data).hexdigest() + return base64.b64encode(hashlib.sha1(data).digest()) From 1446e82dc0c28380ebd610885652155705b51eaa Mon Sep 17 00:00:00 2001 From: moyamo Date: Sat, 2 Jan 2016 22:19:59 +0200 Subject: [PATCH 17/46] Revert "Use base64 sha1hash instead of base16" This reverts commit bed8214a1124f462beeb5c4001259f047d5929eb. It turns out the sha1hash should be a base16 string. --- utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils.py b/utils.py index b9fdf11..f6d120c 100644 --- a/utils.py +++ b/utils.py @@ -24,7 +24,6 @@ __email__ = "post@steffenvogel.de" import e4u import base64 import hashlib -import base64 def ago(secs): periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"] @@ -51,4 +50,4 @@ def decodePassword(password): return base64.b64decode(bytes(password.encode("utf-8"))) def sha1hash(data): - return base64.b64encode(hashlib.sha1(data).digest()) + return hashlib.sha1(data).hexdigest() From 5fb84bca4fa125e978106e2943959b45aa8be408 Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 25 Dec 2015 16:46:58 +0200 Subject: [PATCH 18/46] Allow the user to request their own profile image --- session.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/session.py b/session.py index c31b75f..7e7f8b0 100644 --- a/session.py +++ b/session.py @@ -730,11 +730,16 @@ class Session(YowsupApp): self.logger.debug('Image hash is %s', image_hash) if ID != None: self.backend.handleVCard(self.user, ID, buddy, "", "", response.pictureData) - obuddy = self.buddies[buddy] - self.updateBuddy(buddy, obuddy.nick, obuddy.groups, image_hash) + if not (buddy == self.user or buddy == self.user.split('@')[0]): + obuddy = self.buddies[buddy] + self.updateBuddy(buddy, obuddy.nick, obuddy.groups, image_hash) - self.logger.debug('Requesting profile picture of %s', buddy) - self.requestProfilePicture(buddy, onSuccess = onSuccess) + if buddy == self.user or buddy == self.user.split('@')[0]: + newbuddy = self.legacyName + else: + newbuddy = buddy + self.logger.debug('Requesting profile picture of %s', newbuddy) + self.requestProfilePicture(newbuddy, onSuccess = onSuccess) def onDlsuccess(self, path): self.logger.info("Success: Image downloaded to %s", path) From 03e56a4c07d69e3b9ac12e52ca5dcf304b4f136f Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 1 Jan 2016 17:31:17 +0200 Subject: [PATCH 19/46] Add Deferreds --- deferred.py | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++ session.py | 45 ++++++++++++++++--------- 2 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 deferred.py diff --git a/deferred.py b/deferred.py new file mode 100644 index 0000000..0bbc4e2 --- /dev/null +++ b/deferred.py @@ -0,0 +1,97 @@ +class Deferred(object): + """ + Represents a delayed computation. This is a more elegant way to deal with + callbacks. + + A Deferred object can be thought of as a computation whose value is yet to + be determined. We can manipulate the Deferred as if it where a regular + value by using the then method. Computations dependent on the Deferred will + only proceed when the run method is called. + + Attributes of a Deferred can be accessed directly as methods. + + Example: + image = Deferred() + getImageWithCallback(image.run) + image.then(displayFunc) + + colors = Deferred() + colors.append('blue') + colors.then(print) + colors.run(['red', 'green']) #=> ['red', 'green', 'blue'] + """ + + def __init__(self): + self.subscribers = [] + self.computed = False + self.args = None + self.kwargs = None + + def run(self, *args, **kwargs): + """ + Give a value to the deferred. Calling this method more than once will + result in a DeferredHasValue exception to be raised. + """ + if self.computed: + raise DeferredHasValue("Deferred object already has a value.") + else: + self.args = args + self.kwargs = kwargs + for func, deferred in self.subscribers: + deferred.run(func(*args, **kwargs)) + self.computed = True + + def then(self, func): + """ + Apply func to Deferred value. Returns a Deferred whose value will be + the result of applying func. + """ + result = Deferred() + if self.computed: + result.run(func(*self.args, **self.kwargs)) + else: + self.subscribers.append((func, result)) + return result + + def arg(self, n): + """ + Returns the nth positional argument of a deferred as a deferred + + Args: + n - the index of the positional argument + """ + def helper(*args, **kwargs): + return args[n] + return self.then(helper) + + def __getattr__(self, method_name): + return getattr(Then(self), method_name) + + +class Then(object): + """ + Allows you to call methods on a Deferred. + + Example: + colors = Deferred() + Then(colors).append('blue') + colors.run(['red', 'green']) + colors.then(print) #=> ['red', 'green', 'blue'] + """ + def __init__(self, deferred): + self.deferred = deferred + + def __getattr__(self, name): + def tryCall(obj, *args, **kwargs): + if callable(obj): + return obj(*args, **kwargs) + else: + return obj + def helper(*args, **kwargs): + func = (lambda x: tryCall(getattr(x, name), *args, **kwargs)) + return self.deferred.then(func) + return helper + +class DeferredHasValue(Exception): + def __init__(self, string): + super(DeferredHasValue, self).__init__(string) diff --git a/session.py b/session.py index 7e7f8b0..b21af20 100644 --- a/session.py +++ b/session.py @@ -39,7 +39,9 @@ from buddy import BuddyList from threading import Timer from group import Group from bot import Bot +import deferred from yowsupwrapper import YowsupApp +from functools import partial class MsgIDs: @@ -723,23 +725,34 @@ class Session(YowsupApp): self.buddies.remove(buddy) def requestVCard(self, buddy, ID=None): - def onSuccess(response, request): - self.logger.debug('Sending VCard (%s) with image id %s', - ID, response.pictureId) - image_hash = utils.sha1hash(response.pictureData) - self.logger.debug('Image hash is %s', image_hash) - if ID != None: - self.backend.handleVCard(self.user, ID, buddy, "", "", response.pictureData) - if not (buddy == self.user or buddy == self.user.split('@')[0]): - obuddy = self.buddies[buddy] - self.updateBuddy(buddy, obuddy.nick, obuddy.groups, image_hash) - if buddy == self.user or buddy == self.user.split('@')[0]: - newbuddy = self.legacyName - else: - newbuddy = buddy - self.logger.debug('Requesting profile picture of %s', newbuddy) - self.requestProfilePicture(newbuddy, onSuccess = onSuccess) + buddy = self.legacyName + + # Get profile picture + self.logger.debug('Requesting profile picture of %s', buddy) + response = deferred.Deferred() + self.requestProfilePicture(buddy, onSuccess = response.run) + response = response.arg(0) + + # Send VCard + if ID != None: + response.pictureId().then(partial( + self.logger.debug, 'Sending VCard (%s) with image id %s', ID + )) + pictureData = response.pictureData() + response.pictureData().then(partial( + self.backend.handleVCard, self.user, ID, buddy, "", "" + )) + + # Send image hash + if not buddy == self.legacyName: + obuddy = self.buddies[buddy] + image_hash = pictureData.then(utils.sha1hash) + image_hash.then(partial(self.logger.debug, 'Image hash is %s')) + image_hash.then(partial( + self.updateBuddy, buddy, obuddy.nick, obuddy.groups + )) + def onDlsuccess(self, path): self.logger.info("Success: Image downloaded to %s", path) From 0b4e2f13d43b7accdb37c40e82f62c8504975e28 Mon Sep 17 00:00:00 2001 From: moyamo Date: Sat, 2 Jan 2016 16:00:47 +0200 Subject: [PATCH 20/46] Add call function to use for deferreds --- deferred.py | 38 +++++++++++++++++++++++++++++++++++++- session.py | 18 +++++++----------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/deferred.py b/deferred.py index 0bbc4e2..3e86dcf 100644 --- a/deferred.py +++ b/deferred.py @@ -1,3 +1,5 @@ +from functools import partial + class Deferred(object): """ Represents a delayed computation. This is a more elegant way to deal with @@ -8,7 +10,8 @@ class Deferred(object): value by using the then method. Computations dependent on the Deferred will only proceed when the run method is called. - Attributes of a Deferred can be accessed directly as methods. + Attributes of a Deferred can be accessed directly as methods. The result of + calling these functions will be Deferred. Example: image = Deferred() @@ -92,6 +95,39 @@ class Then(object): return self.deferred.then(func) return helper +def call(func, *args, **kwargs): + """ + Call a function with deferred arguments + + Example: + colors = Deferred() + colors.append('blue') + colors.run(['red', 'green']) +call(print, colors) #=> ['red', 'green', 'blue'] + call(print, 'hi', colors) #=> hi ['red', 'green', 'blue'] + """ + for i, c in enumerate(args): + if isinstance(c, Deferred): + # Function without deferred arguments + normalfunc = partial(func, *args[:i]) + # Function with deferred and possibly deferred arguments + def restfunc(*arg2, **kwarg2): + apply_deferred = partial(normalfunc, *arg2, **kwarg2) + return call(apply_deferred, *args[i + 1:], **kwargs) + return c.then(restfunc) + items = kwargs.items() + for i, (k, v) in enumerate(items): + if isinstance(v, Deferred): + # Function without deferred arguments + normalfunc = partial(func, *args, **dict(items[:i])) + # Function with deferred and possibly deferred arguments + def restfunc2(*arg2, **kwarg2): + apply_deferred = partial(normalfunc, *arg2, **kwarg2) + return call(apply_deferred, **dict(items[i + 1:])) + return c.then(restfunc2) + # No items deferred + return func(*args, **kwargs) + class DeferredHasValue(Exception): def __init__(self, string): super(DeferredHasValue, self).__init__(string) diff --git a/session.py b/session.py index b21af20..6b97580 100644 --- a/session.py +++ b/session.py @@ -40,8 +40,8 @@ from threading import Timer from group import Group from bot import Bot import deferred +from deferred import call from yowsupwrapper import YowsupApp -from functools import partial class MsgIDs: @@ -736,22 +736,18 @@ class Session(YowsupApp): # Send VCard if ID != None: - response.pictureId().then(partial( - self.logger.debug, 'Sending VCard (%s) with image id %s', ID - )) + call(self.logger.debug, 'Sending VCard (%s) with image id %s', + ID, response.pictureId()) pictureData = response.pictureData() - response.pictureData().then(partial( - self.backend.handleVCard, self.user, ID, buddy, "", "" - )) + call(self.backend.handleVCard, self.user, ID, buddy, "", "", + response.pictureData()) # Send image hash if not buddy == self.legacyName: obuddy = self.buddies[buddy] image_hash = pictureData.then(utils.sha1hash) - image_hash.then(partial(self.logger.debug, 'Image hash is %s')) - image_hash.then(partial( - self.updateBuddy, buddy, obuddy.nick, obuddy.groups - )) + call(self.logger.debug, 'Image hash is %s', image_hash) + call(self.updateBuddy, buddy, obuddy.nick, obuddy.groups, image_hash) def onDlsuccess(self, path): From ef04f491524316fa76bcc0e574a5fa3800cfe703 Mon Sep 17 00:00:00 2001 From: moyamo Date: Sat, 2 Jan 2016 17:08:30 +0200 Subject: [PATCH 21/46] Move requestVCard to BuddyList and send image_hash of new contact --- buddy.py | 67 +++++++++++++++++++++++++++++++++++++----------------- session.py | 25 +------------------- 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/buddy.py b/buddy.py index 08ce734..b7afa55 100644 --- a/buddy.py +++ b/buddy.py @@ -26,6 +26,10 @@ from Spectrum2 import protocol_pb2 import logging import time import utils +import base64 + +import deferred +from deferred import call class Buddy(): @@ -39,7 +43,6 @@ class Buddy(): self.lastseen = 0 self.presence = 0 - def update(self, nick, groups, image_hash): self.nick = nick self.groups = groups @@ -71,10 +74,6 @@ class BuddyList(dict): self.logger.debug("Update roster") -# old = self.buddies.keys() -# self.buddies.load() -# new = self.buddies.keys() -# contacts = new contacts = self.keys() contacts.remove('bot') @@ -82,18 +81,8 @@ class BuddyList(dict): self.session.sendSync(contacts, delta = False, interactive = True) self.synced = True -# add = set(new) - set(old) -# remove = set(old) - set(new) - -# self.logger.debug("Roster remove: %s", str(list(remove))) self.logger.debug("Roster add: %s", str(list(contacts))) -# for number in remove: -# self.backend.handleBuddyChanged(self.user, number, "", [], -# protocol_pb2.STATUS_NONE) -# self.backend.handleBuddyRemoved(self.user, number) -# self.unsubscribePresence(number) -# for number in contacts: buddy = self[number] self.backend.handleBuddyChanged(self.user, number, buddy.nick, @@ -125,13 +114,14 @@ class BuddyList(dict): buddy = self[number] buddy.update(nick, groups, image_hash) else: - self.session.sendSync([number], delta = True, interactive = True) - self.session.subscribePresence(number) - self.session.requestStatuses([number], success = self.onStatus) 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 @@ -148,9 +138,13 @@ class BuddyList(dict): 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 = buddy.image_hash if buddy.image_hash is not None else "") + buddy.groups, status, statusMessage=statusmsg, iconHash=iconHash) def remove(self, number): @@ -165,3 +159,34 @@ class BuddyList(dict): return buddy except KeyError: return None + + def requestVCard(self, buddy, ID=None): + if buddy == self.user or buddy == self.user.split('@')[0]: + buddy = self.session.legacyName + + # Get profile picture + self.logger.debug('Requesting profile picture of %s', buddy) + response = deferred.Deferred() + self.session.requestProfilePicture(buddy, onSuccess = response.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) + + # Send image hash + if not buddy == self.session.legacyName: + try: + obuddy = self[buddy] + 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, buddy, nick, groups, image_hash) diff --git a/session.py b/session.py index 6b97580..510b97b 100644 --- a/session.py +++ b/session.py @@ -725,30 +725,7 @@ class Session(YowsupApp): self.buddies.remove(buddy) def requestVCard(self, buddy, ID=None): - if buddy == self.user or buddy == self.user.split('@')[0]: - buddy = self.legacyName - - # Get profile picture - self.logger.debug('Requesting profile picture of %s', buddy) - response = deferred.Deferred() - self.requestProfilePicture(buddy, onSuccess = response.run) - response = response.arg(0) - - # Send VCard - if ID != None: - call(self.logger.debug, 'Sending VCard (%s) with image id %s', - ID, response.pictureId()) - pictureData = response.pictureData() - call(self.backend.handleVCard, self.user, ID, buddy, "", "", - response.pictureData()) - - # Send image hash - if not buddy == self.legacyName: - obuddy = self.buddies[buddy] - image_hash = pictureData.then(utils.sha1hash) - call(self.logger.debug, 'Image hash is %s', image_hash) - call(self.updateBuddy, buddy, obuddy.nick, obuddy.groups, image_hash) - + self.buddies.requestVCard(buddy, ID) def onDlsuccess(self, path): self.logger.info("Success: Image downloaded to %s", path) From ee743ac327fbe3b2cfa36d1d2e2e58d4b0fd135f Mon Sep 17 00:00:00 2001 From: moyamo Date: Sat, 2 Jan 2016 22:40:11 +0200 Subject: [PATCH 22/46] Remove superfluous error messages and handle uncaught exceptions --- Spectrum2/backend.py | 1 - session.py | 8 +++++--- transwhat.py | 8 +++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Spectrum2/backend.py b/Spectrum2/backend.py index 4504bb1..5a91a27 100644 --- a/Spectrum2/backend.py +++ b/Spectrum2/backend.py @@ -262,7 +262,6 @@ class SpectrumBackend: def handleConvMessagePayload(self, data): payload = protocol_pb2.ConversationMessage() - self.logger.error("handleConvMessagePayload") if (payload.ParseFromString(data) == False): #TODO: ERROR return diff --git a/session.py b/session.py index 510b97b..b5706d9 100644 --- a/session.py +++ b/session.py @@ -434,10 +434,12 @@ class Session(YowsupApp): def onPresenceReceived(self, _type, name, jid, lastseen): self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) buddy = jid.split("@")[0] - try: - buddy = self.buddies[buddy] + try: + buddy = self.buddies[buddy] except KeyError: - self.logger.error("Buddy not found: %s", buddy) + # Sometimes whatsapp send our own presence + if buddy != self.legacyName: + self.logger.error("Buddy not found: %s", buddy) return if (lastseen == str(buddy.lastseen)) and (_type == buddy.presence): diff --git a/transwhat.py b/transwhat.py index 935b185..776b1cc 100755 --- a/transwhat.py +++ b/transwhat.py @@ -62,7 +62,13 @@ logging.basicConfig( \ # Handler def handleTransportData(data): - plugin.handleDataRead(data) + try: + plugin.handleDataRead(data) + except SystemExit as e: + raise e + except: + logger = logging.getLogger('transwhat') + logger.error(traceback.format_exc()) e4u.load() From 2d9ac3037eb800ad1df24e98aac66741be2c9eee Mon Sep 17 00:00:00 2001 From: moyamo Date: Tue, 5 Jan 2016 15:46:32 +0200 Subject: [PATCH 23/46] See https://github.com/stv0g/transwhat/issues/37 --- session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.py b/session.py index b5706d9..ee7fe9c 100644 --- a/session.py +++ b/session.py @@ -663,7 +663,7 @@ class Session(YowsupApp): group = self.groups[room] # Update nickname try: - if defaultname != "" and group.participants[number].nick == number: + if defaultname != "" and group.participants[number] == number: group.changeNick(number, defaultname) if self.buddies[number].nick != "": group.changeNick(number, self.buddies[number].nick) From 36eda158f2d6a982183598bef7b0727e53fbc3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Wed, 6 Jan 2016 13:39:34 +0100 Subject: [PATCH 24/46] bot - better handling of autocompletion on mobile devices --- bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot.py b/bot.py index ea54d42..631d82a 100644 --- a/bot.py +++ b/bot.py @@ -42,7 +42,7 @@ class Bot(): } def parse(self, message): - args = message.split(" ") + args = message.strip().split(" ") cmd = args.pop(0) if cmd[0] == '\\': @@ -56,7 +56,7 @@ class Bot(): self.send("a valid command starts with a backslash") def call(self, cmd, args = []): - func = self.commands[cmd] + func = self.commands[cmd.lower()] spec = inspect.getargspec(func) maxs = len(spec.args) - 1 reqs = maxs - len(spec.defaults or []) From ed6affe1ec46351be9447082a5e4c5826b9684fe Mon Sep 17 00:00:00 2001 From: moyamo Date: Wed, 6 Jan 2016 15:00:00 +0200 Subject: [PATCH 25/46] Allow identical consecutive message This used to be blocked to prevent duplicate messages. Now the ID is checked instead of the body to detect duplicates. --- whatsappbackend.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/whatsappbackend.py b/whatsappbackend.py index a2ddc9e..c40f577 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -36,7 +36,7 @@ class WhatsAppBackend(SpectrumBackend): self.sessions = { } self.spectrum_jid = spectrum_jid # Used to prevent duplicate messages - self.lastMessage = {} + self.lastMsgId = {} self.logger.debug("Backend started") @@ -46,9 +46,6 @@ class WhatsAppBackend(SpectrumBackend): if user not in self.sessions: self.sessions[user] = Session(self, user, legacyName, extra) - if user not in self.lastMessage: - self.lastMessage[user] = {} - self.sessions[user].login(password) def handleLogoutRequest(self, user, legacyName): @@ -57,20 +54,17 @@ class WhatsAppBackend(SpectrumBackend): self.sessions[user].logout() del self.sessions[user] - def handleMessageSendRequest(self, user, buddy, message, xhtml = "", ID = 0): - self.logger.debug("handleMessageSendRequest(user=%s, buddy=%s, message=%s, xhtml = %s)", user, buddy, message, xhtml) + 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) # For some reason spectrum occasionally sends to identical messages to - # a buddy, one to the bare jid and one to /bot. This causes duplicate - # messages. Since it is unlikely a user wants to send the same message - # twice, we should just ignore the second message - # - # TODO Proper fix, this work around drops all duplicate messages even - # intentional ones. - # IDEA there is an ID field in ConvMessage. If it is extracted it will work - usersMessage = self.lastMessage[user] - if buddy not in usersMessage or usersMessage[buddy] != message: + # a buddy, one to the bare jid and one to the /bot resource. This + # causes duplicate messages. Thus we should not send consecutive + # messages with the same id + if ID == '': self.sessions[user].sendMessageToWA(buddy, message, ID) - usersMessage[buddy] = message + elif user not in self.lastMsgId or self.lastMsgId[user] != ID: + self.sessions[user].sendMessageToWA(buddy, message, ID) + self.lastMsgId[user] = ID def handleJoinRoomRequest(self, user, room, nickname, pasword): self.logger.debug("handleJoinRoomRequest(user=%s, room=%s, nickname=%s)", user, room, nickname) From 7ff39317cdf6bcb161297d4cd54483e601a9c16a Mon Sep 17 00:00:00 2001 From: moyamo Date: Wed, 6 Jan 2016 16:33:28 +0200 Subject: [PATCH 26/46] Clean up \leave room code --- group.py | 6 ++++++ session.py | 15 +-------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/group.py b/group.py index 993bae4..224b183 100644 --- a/group.py +++ b/group.py @@ -78,6 +78,12 @@ class Group(): self._updateParticipant(number, flags, protocol_pb2.STATUS_NONE) del self.participants[number] + def leaveRoom(self): + for number in self.participants: + nick = self.participants[number] + flags = protocol_pb2.PARTICIPANT_FLAG_ROOM_NOT_FOUND + self._updateParticipant(number, flags, protocol_pb2.STATUS_NONE) + def changeNick(self, number, new_nick): if self.participants[number] == new_nick: return diff --git a/session.py b/session.py index ee7fe9c..88b5b91 100644 --- a/session.py +++ b/session.py @@ -591,20 +591,7 @@ class Session(YowsupApp): self.leaveGroup(room) # Delete Room on spectrum side group = self.groups[room] - for jid in group.participants: - buddy = jid.split("@")[0] - try: - nick = self.buddies[buddy].nick - except KeyError: - nick = buddy - if nick == "": - nick = buddy - if buddy == self.legacyName: - nick = group.nick - flags = protocol_pb2.PARTICIPANT_FLAG_ROOM_NOT_FOUND - self.backend.handleParticipantChanged( - self.user, nick, self._shortenGroupId(room), flags, - protocol_pb2.STATUS_NONE, buddy) + group.leaveRoom() del self.groups[room] def _requestLastSeen(self, buddy): From 5cd4442ddcc1a05fd419e26eb85734ae17262d14 Mon Sep 17 00:00:00 2001 From: moyamo Date: Mon, 4 Jan 2016 10:14:02 +0200 Subject: [PATCH 27/46] Add better logging to onReceipt --- session.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/session.py b/session.py index 88b5b91..7357c2d 100644 --- a/session.py +++ b/session.py @@ -248,16 +248,13 @@ class Session(YowsupApp): type, participant, offline, items])) ) try: - buddy = self.buddies[_from.split('@')[0]] - #self.backend.handleBuddyChanged(self.user, buddy.number.number, - # buddy.nick, buddy.groups, protocol_pb2.STATUS_ONLINE) - self.backend.handleMessageAck(self.user, buddy.number, self.msgIDs[_id].xmppId) - self.msgIDs[_id].cnt = self.msgIDs[_id].cnt +1 - if self.msgIDs[_id].cnt == 2: - del self.msgIDs[_id] - + number = _from.split('@')[0] + self.backend.handleMessageAck(self.user, number, self.msgIDs[_id].xmppId) + self.msgIDs[_id].cnt = self.msgIDs[_id].cnt + 1 + if self.msgIDs[_id].cnt == 2: + del self.msgIDs[_id] except KeyError: - pass + self.logger.error("Message %s not found. Unable to send ack", _id) # Called by superclass def onAck(self, _id, _class, _from, timestamp): From e32365a06533056fb70cb9ebff705aa7af5a018a Mon Sep 17 00:00:00 2001 From: moyamo Date: Wed, 6 Jan 2016 16:57:17 +0200 Subject: [PATCH 28/46] Send xhtml on group message to XMPP MUC --- session.py | 12 ++++++------ whatsappbackend.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/session.py b/session.py index 7357c2d..2e0ad99 100644 --- a/session.py +++ b/session.py @@ -279,7 +279,7 @@ class Session(YowsupApp): if participant is not None: # Group message partname = participant.split('@')[0] if notify is None: - notify = ""; + notify = "" self.sendGroupMessageToXMPP(buddy, partname, messageContent, timestamp, notify) else: @@ -479,9 +479,9 @@ class Session(YowsupApp): self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy) self.sendTyping(buddy, False) - def sendMessageToWA(self, sender, message, ID): - self.logger.info("Message sent from %s to %s: %s", - self.legacyName, sender, message) + def sendMessageToWA(self, sender, message, ID, xhtml=""): + self.logger.info("Message sent from %s to %s: %s (xhtml=%s)", + self.legacyName, sender, message, xhtml) message = message.encode("utf-8") # FIXME: Fragile, should pass this in to onDlerror @@ -519,11 +519,11 @@ class Session(YowsupApp): self.logger.debug("Group Message from %s to %s Groups: %s", group.nick , group , self.groups) self.backend.handleMessage( - self.user, room, message.decode('utf-8'), group.nick + self.user, room, message.decode('utf-8'), group.nick, xhtml=xhtml ) except KeyError: self.logger.error('Group not found: %s', room) - + if (".jpg" in message.lower()) or (".webp" in message.lower()): if (".jpg" in message.lower()): self.imgType = "jpg" diff --git a/whatsappbackend.py b/whatsappbackend.py index c40f577..233843a 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -61,9 +61,9 @@ class WhatsAppBackend(SpectrumBackend): # causes duplicate messages. Thus we should not send consecutive # messages with the same id if ID == '': - self.sessions[user].sendMessageToWA(buddy, message, ID) + self.sessions[user].sendMessageToWA(buddy, message, ID, xhtml) elif user not in self.lastMsgId or self.lastMsgId[user] != ID: - self.sessions[user].sendMessageToWA(buddy, message, ID) + self.sessions[user].sendMessageToWA(buddy, message, ID, xhtml) self.lastMsgId[user] = ID def handleJoinRoomRequest(self, user, room, nickname, pasword): From 2895f78ec7684346d4871123730634693a21d9d8 Mon Sep 17 00:00:00 2001 From: moyamo Date: Wed, 6 Jan 2016 17:26:59 +0200 Subject: [PATCH 29/46] Use the result of Sync to delete invalid numbers --- buddy.py | 29 ++++++++++++++++++++--------- yowsupwrapper.py | 16 +++++++++++----- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/buddy.py b/buddy.py index b7afa55..d769590 100644 --- a/buddy.py +++ b/buddy.py @@ -60,7 +60,6 @@ class BuddyList(dict): self.session = session self.user = user self.logger = logging.getLogger(self.__class__.__name__) - self.synced = False def _load(self, buddies): for buddy in buddies: @@ -77,20 +76,32 @@ class BuddyList(dict): contacts = self.keys() contacts.remove('bot') - if self.synced == False: - self.session.sendSync(contacts, delta = False, interactive = True) - self.synced = True + self.session.sendSync(contacts, delta=False, interactive=True, + success=self.onSync) self.logger.debug("Roster add: %s", str(list(contacts))) for number in contacts: buddy = self[number] - self.backend.handleBuddyChanged(self.user, number, buddy.nick, - buddy.groups, protocol_pb2.STATUS_NONE, - iconHash = buddy.image_hash if buddy.image_hash is not None else "") + 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, contacts) - self.session.requestStatuses(contacts, success = self.onStatus) + 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) + del self[number] + + self.logger.debug("Removing invalid buddies %s", invalid) + for number in invalid: + self.remove(number) + del self[number] def onStatus(self, contacts): self.logger.debug("%s received statuses of: %s", self.user, contacts) diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 048ba98..14e2256 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -271,12 +271,12 @@ class YowsupApp(object): ChatstateProtocolEntity.STATE_PAUSED, jid ) self.sendEntity(state) - - def sendSync(self, contacts, delta = False, interactive = True): + + def sendSync(self, contacts, delta = False, interactive = True, success = None, failure = None): """ You need to sync new contacts before you interact with them, failure to do so could result in a temporary ban. - + Args: - contacts: ([str]) a list of phone numbers of the contacts you wish to sync @@ -285,13 +285,19 @@ class YowsupApp(object): contact list. - interactive: (bool; default: True) Set to false if you are sure this is the first time registering + - success: (func) - Callback; Takes three arguments: existing numbers, + non-existing numbers, invalid numbers. """ # TODO: Implement callbacks mode = GetSyncIqProtocolEntity.MODE_DELTA if delta else GetSyncIqProtocolEntity.MODE_FULL context = GetSyncIqProtocolEntity.CONTEXT_INTERACTIVE if interactive else GetSyncIqProtocolEntity.CONTEXT_REGISTRATION iq = GetSyncIqProtocolEntity(contacts, mode, context) - self.sendIq(iq) - + def onSuccess(response, request): + success(response.inNumbers.keys(), response.outNumbers.keys(), response.invalidNumbers) + + self.sendIq(iq, onSuccess = onSuccess, onError = failure) + + def requestStatuses(self, contacts, success = None, failure = None): """ Request the statuses of a number of users. From 8e1be863191e12caf40cacb8f3b968190eabbb27 Mon Sep 17 00:00:00 2001 From: moyamo Date: Wed, 6 Jan 2016 17:38:52 +0200 Subject: [PATCH 30/46] Correct the format of the contacts numbers when syncing --- yowsupwrapper.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 14e2256..8271400 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -291,9 +291,16 @@ class YowsupApp(object): # TODO: Implement callbacks mode = GetSyncIqProtocolEntity.MODE_DELTA if delta else GetSyncIqProtocolEntity.MODE_FULL context = GetSyncIqProtocolEntity.CONTEXT_INTERACTIVE if interactive else GetSyncIqProtocolEntity.CONTEXT_REGISTRATION + # International contacts must be preceded by a plus. Other numbers are + # considered local. + contacts = ['+' + c for c in contacts] iq = GetSyncIqProtocolEntity(contacts, mode, context) def onSuccess(response, request): - success(response.inNumbers.keys(), response.outNumbers.keys(), response.invalidNumbers) + # Remove leading plus + existing = [s[1:] for s in response.inNumbers.keys()] + nonexisting = [s[1:] for s in response.outNumbers.keys()] + invalid = [s[1:] for s in response.invalidNumbers] + success(existing, nonexisting, invalid) self.sendIq(iq, onSuccess = onSuccess, onError = failure) From 7b327c2d0f1ad350b89032126ca9f552563a7243 Mon Sep 17 00:00:00 2001 From: moyamo Date: Wed, 6 Jan 2016 18:31:42 +0200 Subject: [PATCH 31/46] Add functionality to receive broadcast messages --- session.py | 97 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/session.py b/session.py index 2e0ad99..a345c69 100644 --- a/session.py +++ b/session.py @@ -54,6 +54,7 @@ class MsgIDs: class Session(YowsupApp): + broadcast_prefix = u'\U0001F4E2 ' def __init__(self, backend, user, legacyName, extra): super(Session, self).__init__() @@ -276,20 +277,18 @@ class Session(YowsupApp): self.sendReceipt(_id, _from, None, participant) self.logger.info("Message received from %s to %s: %s (at ts=%s)", buddy, self.legacyName, messageContent, timestamp) - if participant is not None: # Group message + if participant is not None: # Group message or broadcast partname = participant.split('@')[0] - if notify is None: - notify = "" - self.sendGroupMessageToXMPP(buddy, partname, messageContent, - timestamp, notify) + if _from.split('@')[1] == 'broadcast': # Broadcast message + message = self.broadcast_prefix + messageContent + self.sendMessageToXMPP(partname, message, timestamp) + else: # Group message + if notify is None: + notify = "" + self.sendGroupMessageToXMPP(buddy, partname, messageContent, + timestamp, notify) else: self.sendMessageToXMPP(buddy, messageContent, timestamp) - # isBroadcast always returns false, I'm not sure how to get a broadcast - # message. - #if messageEntity.isBroadcast(): - # self.logger.info("Broadcast received from %s to %s: %s (at ts=%s)",\ - # buddy, self.legacyName, messageContent, timestamp) - # messageContent = "[Broadcast] " + messageContent # Called by superclass def onImage(self, image): @@ -300,10 +299,13 @@ class Session(YowsupApp): image.caption = '' message = image.url + ' ' + image.caption if participant is not None: # Group message - partname = participant.split('@')[0] - self.sendGroupMessageToXMPP(buddy, partname, message, image.timestamp) - else: - + partname = participant.split('@')[0] + if image._from.split('@')[1] == 'broadcast': # Broadcast message + self.sendMessageToXMPP(partname, self.broadcast_prefix, image.timestamp) + self.sendMessageToXMPP(partname, message, image.timestamp) + else: # Group message + self.sendGroupMessageToXMPP(buddy, partname, message, image.timestamp) + else: self.sendMessageToXMPP(buddy, message, image.timestamp) self.sendReceipt(image._id, image._from, None, image.participant) @@ -314,10 +316,13 @@ class Session(YowsupApp): participant = audio.participant message = audio.url if participant is not None: # Group message - partname = participant.split('@')[0] - self.sendGroupMessageToXMPP(buddy, partname, message, audio.timestamp) - else: - + partname = participant.split('@')[0] + if audio._from.split('@')[1] == 'broadcast': # Broadcast message + self.sendMessageToXMPP(partname, self.broadcast_prefix, audio.timestamp) + self.sendMessageToXMPP(partname, message, audio.timestamp) + else: # Group message + self.sendGroupMessageToXMPP(buddy, partname, message, audio.timestamp) + else: self.sendMessageToXMPP(buddy, message, audio.timestamp) self.sendReceipt(audio._id, audio._from, None, audio.participant) @@ -329,10 +334,13 @@ class Session(YowsupApp): message = video.url if participant is not None: # Group message - partname = participant.split('@')[0] - self.sendGroupMessageToXMPP(buddy, partname, message, video.timestamp) - else: - + partname = participant.split('@')[0] + if video._from.split('@')[1] == 'broadcast': # Broadcast message + self.sendMessageToXMPP(partname, self.broadcast_prefix, video.timestamp) + self.sendMessageToXMPP(partname, message, video.timestamp) + else: # Group message + self.sendGroupMessageToXMPP(buddy, partname, message, video.timestamp) + else: self.sendMessageToXMPP(buddy, message, video.timestamp) self.sendReceipt(video._id, video._from, None, video.participant) @@ -340,23 +348,30 @@ class Session(YowsupApp): buddy = location._from.split('@')[0] latitude = location.getLatitude() longitude = location.getLongitude() - url = location.getLocationUrl() + url = location.getLocationURL() participant = location.participant + latlong = 'geo:' + latitude + ',' + longitude self.logger.debug("Location received from %s: %s, %s", buddy, latitude, longitude) - if participant is not None: # Group message - partname = participant.split('@')[0] - self.sendGroupMessageToXMPP(buddy, partname, url, location.timestamp) - self.sendGroupMessageToXMPP(buddy, partname, 'geo:' + latitude + ',' + longitude, - location.timestamp) - else: - self.sendMessageToXMPP(buddy, url, location.timestamp) - self.sendMessageToXMPP(buddy, 'geo:' + latitude + ',' + longitude, - location.timestamp) - self.sendReceipt(location._id, location._from, None, location.participant, location.timestamp) + if participant is not None: # Group message + partname = participant.split('@')[0] + if location._from.split('@')[1] == 'broadcast': # Broadcast message + self.sendMessageToXMPP(partname, self.broadcast_prefix, location.timestamp) + if url is not None: + self.sendMessageToXMPP(partname, url, location.timestamp) + self.sendMessageToXMPP(partname, latlong, location.timestamp) + else: # Group message + if url is not None: + self.sendGroupMessageToXMPP(buddy, partname, url, location.timestamp) + self.sendGroupMessageToXMPP(buddy, partname, latlong, location.timestamp) + else: + if url is not None: + self.sendMessageToXMPP(buddy, url, location.timestamp) + self.sendMessageToXMPP(buddy, latlong, location.timestamp) + self.sendReceipt(location._id, location._from, None, location.participant) # Called by superclass @@ -366,13 +381,17 @@ class Session(YowsupApp): _id, _from, name, card_data, to, notify, timestamp, participant ])) ) + message = "Received VCard (not implemented yet)" buddy = _from.split("@")[0] if participant is not None: # Group message - partname = participant.split('@')[0] - self.sendGroupMessageToXMPP(buddy, partname, "Received VCard (not implemented yet)", timestamp) - else: - - self.sendMessageToXMPP(buddy, "Received VCard (not implemented yet)") + partname = participant.split('@')[0] + if _from.split('@')[1] == 'broadcast': # Broadcast message + message = self.broadcast_prefix + message + self.sendMessageToXMPP(partname, message, timestamp) + else: # Group message + self.sendGroupMessageToXMPP(buddy, partname, message, timestamp) + else: + self.sendMessageToXMPP(buddy, message, timestamp) # self.sendMessageToXMPP(buddy, card_data) #self.transferFile(buddy, str(name), card_data) self.sendReceipt(_id, _from, None, participant) From 16bdc85471d59c07dd0738f792dcbc863e9400b6 Mon Sep 17 00:00:00 2001 From: moyamo Date: Mon, 14 Dec 2015 20:27:10 +0200 Subject: [PATCH 32/46] Update buddies status when a notification is received --- session.py | 10 ++++++++++ yowsupwrapper.py | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/session.py b/session.py index a345c69..2455f9e 100644 --- a/session.py +++ b/session.py @@ -447,6 +447,16 @@ class Session(YowsupApp): room, participants) self.groups[room].removeParticipants(participants) + # Called by superclass + def onContactStatusChanged(self, number, status): + self.logger.debug("%s changed their status to %s", number, status) + try: + buddy = self.buddies[number] + buddy.statusMsg = status + self.buddies.updateSpectrum(buddy) + except KeyError: + self.logger.debug("%s not in buddy list", number) + def onPresenceReceived(self, _type, name, jid, lastseen): self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) buddy = jid.split("@")[0] diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 8271400..fa4819a 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -35,6 +35,7 @@ from yowsup.layers.protocol_chatstate.protocolentities import * from yowsup.layers.protocol_contacts.protocolentities import * from yowsup.layers.protocol_groups.protocolentities import * from yowsup.layers.protocol_media.protocolentities import * +from yowsup.layers.protocol_notifications.protocolentities import * from yowsup.layers.protocol_messages.protocolentities import * from yowsup.layers.protocol_presence.protocolentities import * from yowsup.layers.protocol_profiles.protocolentities import * @@ -545,6 +546,15 @@ class YowsupApp(object): """ pass + def onContactStatusChanged(self, number, status): + """Called when a contacts changes their status + + Args + number: (str) the number of the contact who changed their status + status: (str) the new status + """ + pass + def sendEntity(self, entity): """Sends an entity down the stack (as if YowsupAppLayer called toLower)""" self.stack.broadcastEvent(YowLayerEvent(YowsupAppLayer.TO_LOWER_EVENT, @@ -651,6 +661,11 @@ class YowsupAppLayer(YowInterfaceLayer): entity.getGroupId().split('@')[0], entity.getParticipants().keys() ) + elif isinstance(entity, StatusNotificationProtocolEntity): + self.caller.onContactStatusChanged( + entity._from.split('@')[0], + entity.status + ) @ProtocolEntityCallback('message') def onMessageReceived(self, entity): From 59f0af9f8dcd926a29ace28bea4544f6fa47ce33 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 11:47:33 +0200 Subject: [PATCH 33/46] Request new profile picture on profile picture changed notification --- session.py | 7 ++++++- yowsupwrapper.py | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/session.py b/session.py index 2455f9e..becacb5 100644 --- a/session.py +++ b/session.py @@ -457,6 +457,11 @@ class Session(YowsupApp): except KeyError: self.logger.debug("%s not in buddy list", number) + # Called by superclass + def onContactPictureChanged(self, number): + self.logger.debug("%s changed their profile picture", number) + self.buddies.requestVCard(number) + def onPresenceReceived(self, _type, name, jid, lastseen): self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) buddy = jid.split("@")[0] @@ -471,7 +476,7 @@ class Session(YowsupApp): if (lastseen == str(buddy.lastseen)) and (_type == buddy.presence): return - if ((lastseen != "deny") and (lastseen != None) and (lastseen != "none")): + if ((lastseen != "deny") and (lastseen != None) and (lastseen != "none")): buddy.lastseen = int(lastseen) if (_type == None): buddy.lastseen = time.time() diff --git a/yowsupwrapper.py b/yowsupwrapper.py index fa4819a..b0ed44b 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -549,12 +549,19 @@ class YowsupApp(object): def onContactStatusChanged(self, number, status): """Called when a contacts changes their status - Args + Args: number: (str) the number of the contact who changed their status status: (str) the new status """ pass + def onContactPictureChanged(self, number): + """Called when a contact changes their profile picture + Args + number: (str) the number of the contact who changed their picture + """ + pass + def sendEntity(self, entity): """Sends an entity down the stack (as if YowsupAppLayer called toLower)""" self.stack.broadcastEvent(YowLayerEvent(YowsupAppLayer.TO_LOWER_EVENT, @@ -666,6 +673,9 @@ class YowsupAppLayer(YowInterfaceLayer): entity._from.split('@')[0], entity.status ) + elif (isinstance(entity, SetPictureNotificationProtocolEntity) or + isinstance(entity, DeletePictureNotificationProtocolEntity)): + self.caller.onContactPictureChanged(entity.setJid.split('@')[0]) @ProtocolEntityCallback('message') def onMessageReceived(self, entity): From f57b276665cf3862545e26302a836d7363106d58 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 12:27:40 +0200 Subject: [PATCH 34/46] Update buddies when contact notification is received --- buddy.py | 6 ++++++ session.py | 27 +++++++++++++++++++++++++++ yowsupwrapper.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/buddy.py b/buddy.py index d769590..542f798 100644 --- a/buddy.py +++ b/buddy.py @@ -201,3 +201,9 @@ class BuddyList(dict): image_hash = pictureData.then(utils.sha1hash) call(self.logger.debug, 'Image hash is %s', image_hash) call(self.update, buddy, nick, groups, image_hash) + + def refresh(self, number): + self.session.unsubscribePresence(number) + self.session.subscribePresence(number) + self.requestVCard(number) + self.session.requestStatuses([number], success = self.onStatus) diff --git a/session.py b/session.py index becacb5..821ad5a 100644 --- a/session.py +++ b/session.py @@ -462,6 +462,33 @@ class Session(YowsupApp): self.logger.debug("%s changed their profile picture", number) self.buddies.requestVCard(number) + # Called by superclass + def onContactAdded(self, number, nick): + self.logger.debug("Adding new contact %s (%s)", nick, number) + self.updateBuddy(number, nick, []) + + # Called by superclass + def onContactRemoved(self, number): + self.logger.debug("Removing contact %s", number) + self.removeBuddy(number) + + def onContactUpdated(self, oldnumber, newnumber): + self.logger.debug("Contact has changed number from %s to %s", + oldnumber, newnumber) + if newnumber in self.buddies: + self.logger.warn("Contact %s exists, just updating", newnumber) + self.buddies.refresh(newnumber) + try: + buddy = self.buddies[oldnumber] + except KeyError: + self.logger.warn("Old contact (%s) not found. Adding new contact (%s)", + oldnumber, newnumber) + nick = "" + else: + self.removeBuddy(buddy.number) + nick = buddy.nick + self.updateBuddy(newnumber, nick, []) + def onPresenceReceived(self, _type, name, jid, lastseen): self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen) buddy = jid.split("@")[0] diff --git a/yowsupwrapper.py b/yowsupwrapper.py index b0ed44b..0cecec7 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -562,6 +562,32 @@ class YowsupApp(object): """ pass + def onContactRemoved(self, number): + """Called when a contact has been removed + + Args: + number: (str) the number of the contact who has been removed + """ + pass + + def onContactAdded(self, number, nick): + """Called when a contact has been added + + Args: + number: (str) contacts number + nick: (str) contacts nickname + """ + pass + + def onContactUpdated(self, oldNumber, newNumber): + """Called when a contact has changed their number + + Args: + oldNumber: (str) the number the contact previously used + newNumber: (str) the new number of the contact + """ + pass + def sendEntity(self, entity): """Sends an entity down the stack (as if YowsupAppLayer called toLower)""" self.stack.broadcastEvent(YowLayerEvent(YowsupAppLayer.TO_LOWER_EVENT, @@ -676,6 +702,18 @@ class YowsupAppLayer(YowInterfaceLayer): elif (isinstance(entity, SetPictureNotificationProtocolEntity) or isinstance(entity, DeletePictureNotificationProtocolEntity)): self.caller.onContactPictureChanged(entity.setJid.split('@')[0]) + elif isinstance(entity, RemoveContactNotificationProtocolEntity): + self.caller.onContactRemoved(entity.contactJid.split('@')[0]) + elif isinstance(entity, AddContactNotificationProtocolEntity): + self.caller.onContactAdded( + entity.contactJid.split('@')[0], + entity.notify + ) + elif isinstance(entity, UpdateContactNotificationProtocolEntity): + self.caller.onContactUpdated( + entity._from.split('@')[0], + entity.contactJid.split('@')[0], + ) @ProtocolEntityCallback('message') def onMessageReceived(self, entity): From 9c32dfbb38f6839f5bf390609b3d7537e0868763 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 12:58:03 +0200 Subject: [PATCH 35/46] Update group subject when notification received --- session.py | 18 ++++++++++++++++++ yowsupwrapper.py | 24 +++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/session.py b/session.py index 821ad5a..1bc3495 100644 --- a/session.py +++ b/session.py @@ -441,6 +441,24 @@ class Session(YowsupApp): self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName) self.groups[room].sendParticipantsToSpectrum(self.legacyName) + # Called by superclass + def onSubjectChanged(self, room, subject, subjectOwner, timestamp): + self.logger.debug( + "onSubjectChange(rrom=%s, subject=%s, subjectOwner=%s, timestamp=%s)", + room, subject, subjectOwner, timestamp) + try: + group = self.groups[room] + except KeyError: + self.logger.error("Subject of non-existant group (%s) changed", group) + else: + group.subject = subject + group.subjectOwner = subjectOwner + if not group.joined: + # We have not joined group so we should not send subject + return + self.backend.handleSubject(self.user, room, subject, subjectOwner) + self.backend.handleRoomNicknameChanged(self.user, room, subject) + # Called by superclass def onParticipantsRemovedFromGroup(self, room, participants): self.logger.debug("Participants removed from group: %s, %s", diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 0cecec7..2996d22 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -539,19 +539,30 @@ class YowsupApp(object): def onParticipantsRemovedFromGroup(self, group, participants): """Called when participants have been removed from a group - + Args: - group: (str) id of the group (e.g. 27831788123-144024456) - participants: (list) jids of participants that are removed """ pass + def onSubjectChanged(self, group, subject, subjectOwner, timestamp): + """Called when someone changes the grousp subject + + Args: + - group: (str) id of the group (e.g. 27831788123-144024456) + - subject: (str) the new subject + - subjectOwner: (str) the number of the person who changed the subject + - timestamp: (str) time the subject was changed + """ + pass + def onContactStatusChanged(self, number, status): """Called when a contacts changes their status Args: - number: (str) the number of the contact who changed their status - status: (str) the new status + number: (str) the number of the contact who changed their status + status: (str) the new status """ pass @@ -694,6 +705,13 @@ class YowsupAppLayer(YowInterfaceLayer): entity.getGroupId().split('@')[0], entity.getParticipants().keys() ) + elif isinstance(entity, SubjectGroupsNotificationProtocolEntity): + self.caller.onSubjectChanged( + entity.getGroupId().split('@')[0], + entity.getSubject(), + entity.getSubjectOwner(full=False), + entity.getSubjectTimestamp() + ) elif isinstance(entity, StatusNotificationProtocolEntity): self.caller.onContactStatusChanged( entity._from.split('@')[0], From e85040209538bf6669e807c6f424c40b146208ed Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 17:56:55 +0200 Subject: [PATCH 36/46] Add threading utilities --- threadutils.py | 19 +++++++++++++++++++ transwhat.py | 10 +++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 threadutils.py diff --git a/threadutils.py b/threadutils.py new file mode 100644 index 0000000..59e7d51 --- /dev/null +++ b/threadutils.py @@ -0,0 +1,19 @@ +import Queue +import threading + +# This queue is for other threads that want to execute code in the main thread +eventQueue = Queue.Queue() + +def runInThread(threadFunc, callback): + """ + Executes threadFunc in a new thread. The result of threadFunc will be + pass as the first argument to callback. callback will be called in the main + thread. + """ + def helper(): + # Execute threadfunc in new thread + result = threadFunc() + # Queue callback to be call in main thread + eventQueue.put(lambda: callback(result)) + thread = threading.Thread(target=helper) + thread.start() diff --git a/transwhat.py b/transwhat.py index 776b1cc..07e36bf 100755 --- a/transwhat.py +++ b/transwhat.py @@ -29,8 +29,8 @@ import logging import asyncore import sys, os import e4u -import threading import Queue +import threadutils sys.path.insert(0, os.getcwd()) @@ -89,6 +89,7 @@ plugin.handleBackendConfig({ ], }) + while True: try: asyncore.loop(timeout=1.0, count=10, use_poll = True) @@ -101,6 +102,13 @@ while True: break if closed: break + while True: + try: + callback = threadutils.eventQueue.get_nowait() + except Queue.Empty: + break + else: + callback() except SystemExit: break except: From 513cdbcf10307c60e884cc1a1f128998427824c4 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 17:57:41 +0200 Subject: [PATCH 37/46] Add functionality to request sms code via bot --- registersession.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++ whatsappbackend.py | 10 ++++- yowsupwrapper.py | 18 +++++++++ 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 registersession.py diff --git a/registersession.py b/registersession.py new file mode 100644 index 0000000..b07b675 --- /dev/null +++ b/registersession.py @@ -0,0 +1,94 @@ +from Spectrum2 import protocol_pb2 + +from yowsupwrapper import YowsupApp +import logging +import threadutils + +class RegisterSession(YowsupApp): + """ + A dummy Session object that is used to register a user to whatsapp + """ + WANT_CC = 0 + WANT_SMS = 1 + def __init__(self, backend, user, legacyName, extra): + self.user = user + self.number = legacyName + self.backend = backend + self.logger = logging.getLogger(self.__class__.__name__) + self.state = self.WANT_CC + + def login(self, password=""): + self.backend.handleConnected(self.user) + self.backend.handleBuddyChanged(self.user, 'bot', 'bot', + ['Admin'], protocol_pb2.STATUS_ONLINE) + self.backend.handleMessage(self.user, 'bot', + 'Please enter your country code') + + def sendMessageToWA(self, buddy, message, ID='', xhtml=''): + if buddy == 'bot' and self.state == self.WANT_CC: + try: + country_code = int(message.strip()) + except ValueError: + self.backend.handleMessage(self.user, 'bot', + 'Country code must be a number') + else: # Succeded in decoding country code + country_code = str(country_code) + if country_code != self.number[:len(country_code)]: + self.backend.handleMessage(self.user, + 'bot', 'Number does not start with provided country code') + else: + self.backend.handleMessage(self.user, 'bot', 'Requesting sms code') + self.logger.debug('Requesting SMS code for %s', self.user) + self._requestSMSCodeNonBlock(country_code) + elif buddy == 'bot' and self.state == self.WANT_SMS: + self.backend.handleMessage(self.user, 'bot', 'Not implemented') + else: + self.logger.warn('Unauthorised user (%s) attempting to send messages', + self.user) + self.backend.handleMessage(self.user, buddy, + 'You are not logged in yet. You can only send messages to bot.') + + def _requestSMSCodeNonBlock(self, country_code): + number = self.number[len(country_code):] + threadFunc = lambda: self.requestSMSCode(country_code, number) + threadutils.runInThread(threadFunc, self._confirmation) + + def _confirmation(self, result): + self.backend.handleMessage(self.user, 'bot', 'SMS Code Sent') + self.state = self.WANT_SMS + self.backend.handleMessage(self.user, 'bot', 'Please enter SMS Code') + + # Dummy methods. Whatsapp backend might call these, but they should have no + # effect + def logout(self): + pass + + def joinRoom(self, room, nickname): + pass + + def leaveRoom(self, room): + pass + + def changeStatusMessage(self, statusMessage): + pass + + def changeStatus(self, status): + pass + + def loadBuddies(self, buddies): + pass + + def updateBuddy(self, buddies): + pass + + def removeBuddy(self, buddies): + pass + + def sendTypingStarted(self, buddy): + pass + + def sendTypingStopped(self, buddy): + pass + + def requestVCard(self, buddy, ID): + pass diff --git a/whatsappbackend.py b/whatsappbackend.py index 233843a..068f1a8 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -25,6 +25,7 @@ from Spectrum2.backend import SpectrumBackend from Spectrum2 import protocol_pb2 from session import Session +from registersession import RegisterSession import logging @@ -43,8 +44,13 @@ class WhatsAppBackend(SpectrumBackend): # RequestsHandlers def handleLoginRequest(self, user, legacyName, password, extra): self.logger.debug("handleLoginRequest(user=%s, legacyName=%s)", user, legacyName) - if user not in self.sessions: - self.sessions[user] = Session(self, user, legacyName, extra) + # Key word means we should register + if password == 'register': + if user not in self.sessions: + self.sessions[user] = RegisterSession(self, user, legacyName, extra) + else: + if user not in self.sessions: + self.sessions[user] = Session(self, user, legacyName, extra) self.sessions[user].login(password) diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 2996d22..f6f0116 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -42,6 +42,11 @@ from yowsup.layers.protocol_profiles.protocolentities import * from yowsup.layers.protocol_receipts.protocolentities import * from yowsup.layers.protocol_media.mediauploader import MediaUploader + +# Registration + +from yowsup.registration.coderequest import WACodeRequest + from functools import partial #from session import MsgIDs @@ -371,6 +376,19 @@ class YowsupApp(object): iq = InfoGroupsIqProtocolEntity(group + '@g.us') self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) + def requestSMSCode(self, countryCode, phoneNumber): + """ + Request an sms regitration code. WARNING: this function is blocking + + Args: + countryCode: The country code of the phone you wish to register + phoneNumber: phoneNumber of the phone you wish to register without + the country code. + """ + request = WACodeRequest(countryCode, phoneNumber) + return request.send() + + def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t): """ Called when login is successful. From aeb43dff55175c512ca541f92bc151d09d18d610 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 19:31:05 +0200 Subject: [PATCH 38/46] Add ability to request password after receiving sms code --- Spectrum2/backend.py | 6 +++++ registersession.py | 62 +++++++++++++++++++++++++++++++++++++++----- whatsappbackend.py | 16 +++++++++++- yowsupwrapper.py | 18 ++++++++++++- 4 files changed, 94 insertions(+), 8 deletions(-) diff --git a/Spectrum2/backend.py b/Spectrum2/backend.py index 5a91a27..b2e5891 100644 --- a/Spectrum2/backend.py +++ b/Spectrum2/backend.py @@ -239,6 +239,12 @@ class SpectrumBackend: message = WRAP(c.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_BACKEND_CONFIG); self.send(message) + def handleQuery(self, command): + c = protocol_pb2.BackendConfig() + c.config = command + message = WRAP(c.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_QUERY); + self.send(message) + def handleLoginPayload(self, data): payload = protocol_pb2.Login() if (payload.ParseFromString(data) == False): diff --git a/registersession.py b/registersession.py index b07b675..963eeb2 100644 --- a/registersession.py +++ b/registersession.py @@ -3,6 +3,7 @@ from Spectrum2 import protocol_pb2 from yowsupwrapper import YowsupApp import logging import threadutils +import sys class RegisterSession(YowsupApp): """ @@ -14,6 +15,7 @@ class RegisterSession(YowsupApp): self.user = user self.number = legacyName self.backend = backend + self.countryCode = '' self.logger = logging.getLogger(self.__class__.__name__) self.state = self.WANT_CC @@ -39,25 +41,73 @@ class RegisterSession(YowsupApp): else: self.backend.handleMessage(self.user, 'bot', 'Requesting sms code') self.logger.debug('Requesting SMS code for %s', self.user) - self._requestSMSCodeNonBlock(country_code) + self.countryCode = country_code + self._requestSMSCodeNonBlock() elif buddy == 'bot' and self.state == self.WANT_SMS: - self.backend.handleMessage(self.user, 'bot', 'Not implemented') + code = message.strip() + if self._checkSMSFormat(code): + self._requestPassword(code) + else: + self.backend.handleMessage(self.user, + 'bot', 'Invalid code. Must be of the form XXX-XXX.') else: self.logger.warn('Unauthorised user (%s) attempting to send messages', self.user) self.backend.handleMessage(self.user, buddy, 'You are not logged in yet. You can only send messages to bot.') - def _requestSMSCodeNonBlock(self, country_code): - number = self.number[len(country_code):] - threadFunc = lambda: self.requestSMSCode(country_code, number) + def _checkSMSFormat(self, sms): + splitting = sms.split('-') + if len(splitting) != 2: + return False + a, b = splitting + if len(a) != 3 and len(b) != 3: + return False + try: + int(a) + int(b) + except ValueError: + return False + return True + + def _requestSMSCodeNonBlock(self): + number = self.number[len(self.countryCode):] + threadFunc = lambda: self.requestSMSCode(self.countryCode, number) threadutils.runInThread(threadFunc, self._confirmation) + self.backend.handleMessage(self.user, 'bot', 'SMS Code Sent') def _confirmation(self, result): - self.backend.handleMessage(self.user, 'bot', 'SMS Code Sent') self.state = self.WANT_SMS + resultStr = self._resultToString(result) + self.backend.handleMessage(self.user, 'bot', 'Response:') + self.backend.handleMessage(self.user, 'bot', resultStr) self.backend.handleMessage(self.user, 'bot', 'Please enter SMS Code') + def _requestPassword(self, smsCode): + cc = self.countryCode + number = self.number[len(cc):] + threadFunc = lambda: self.requestPassword(cc, number, smsCode) + threadutils.runInThread(threadFunc, self._gotPassword) + self.backend.handleMessage(self.user, 'bot', 'Getting Password') + + def _gotPassword(self, result): + resultStr = self._resultToString(result) + self.backend.handleMessage(self.user, 'bot', 'Response:') + self.backend.handleMessage(self.user, 'bot', resultStr) + self.backend.handleMessage(self.user, 'bot', 'Logging you in') + password = result['pw'] + self.backend.relogin(self.user, self.number, password, None) + + def _resultToString(self, result): + unistr = str if sys.version_info >= (3, 0) else unicode + out = [] + for k, v in result.items(): + if v is None: + continue + out.append("%s: %s" %(k, v.encode("utf-8") if type(v) is unistr else v)) + + return "\n".join(out) + # Dummy methods. Whatsapp backend might call these, but they should have no # effect def logout(self): diff --git a/whatsappbackend.py b/whatsappbackend.py index 068f1a8..46b2dc5 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -44,7 +44,7 @@ class WhatsAppBackend(SpectrumBackend): # RequestsHandlers def handleLoginRequest(self, user, legacyName, password, extra): self.logger.debug("handleLoginRequest(user=%s, legacyName=%s)", user, legacyName) - # Key word means we should register + # Key word means we should register a new password if password == 'register': if user not in self.sessions: self.sessions[user] = RegisterSession(self, user, legacyName, extra) @@ -117,6 +117,20 @@ class WhatsAppBackend(SpectrumBackend): self.logger.debug("handleVCardRequest(user=%s, buddy=%s, ID=%s)", user, buddy, ID) self.sessions[user].requestVCard(buddy, ID) + def relogin(self, user, legacyName, password, extra): + """ + Used to re-initialize the session object. Used when finished with + registration session and the user needs to login properly + """ + self.logger.debug("relogin(user=%s, legacyName=%s)", user, legacyName) + # Change password in spectrum database + self.handleQuery('register %s %s %s' % (user, legacyName, password)) + # Key word means we should register a new password + if password == 'register': # This shouldn't happen, but just in case + self.sessions[user] = RegisterSession(self, user, legacyName, extra) + else: + self.sessions[user] = Session(self, user, legacyName, extra) + self.sessions[user].login(password) # TODO def handleBuddyBlockToggled(self, user, buddy, blocked): diff --git a/yowsupwrapper.py b/yowsupwrapper.py index f6f0116..23b84de 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -45,7 +45,8 @@ from yowsup.layers.protocol_media.mediauploader import MediaUploader # Registration -from yowsup.registration.coderequest import WACodeRequest +from yowsup.registration import WACodeRequest +from yowsup.registration import WARegRequest from functools import partial @@ -388,6 +389,21 @@ class YowsupApp(object): request = WACodeRequest(countryCode, phoneNumber) return request.send() + def requestPassword(self, countryCode, phoneNumber, smsCode): + """ + Request a password. WARNING: this function is blocking + + Args: + countryCode: The country code of the phone you wish to register + phoneNumber: phoneNumber of the phone you wish to register without + the country code. + smsCode: The sms code that you asked for previously + """ + smsCode = smsCode.replace('-', '') + request = WARegRequest(countryCode, phoneNumber, smsCode) + return request.send() + + def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t): """ From 026f21f43ebef657582e2a6daa25705930d30777 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 21:12:16 +0200 Subject: [PATCH 39/46] Send image and caption in two different messages This allows conversations to autodownload the images. --- session.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/session.py b/session.py index 1bc3495..266cc29 100644 --- a/session.py +++ b/session.py @@ -296,17 +296,19 @@ class Session(YowsupApp): buddy = image._from.split('@')[0] participant = image.participant if image.caption is None: - image.caption = '' - message = image.url + ' ' + image.caption + image.caption = '' if participant is not None: # Group message partname = participant.split('@')[0] if image._from.split('@')[1] == 'broadcast': # Broadcast message self.sendMessageToXMPP(partname, self.broadcast_prefix, image.timestamp) - self.sendMessageToXMPP(partname, message, image.timestamp) + self.sendMessageToXMPP(partname, image.url, image.timestamp) + self.sendMessageToXMPP(partname, image.caption, image.timestamp) else: # Group message - self.sendGroupMessageToXMPP(buddy, partname, message, image.timestamp) + self.sendGroupMessageToXMPP(buddy, partname, image.url, image.timestamp) + self.sendGroupMessageToXMPP(buddy, partname, image.caption, image.timestamp) else: - self.sendMessageToXMPP(buddy, message, image.timestamp) + self.sendMessageToXMPP(buddy, image.url, image.timestamp) + self.sendMessageToXMPP(buddy, image.caption, image.timestamp) self.sendReceipt(image._id, image._from, None, image.participant) # Called by superclass From dbf4a477ec5ac2b401e74d6664b6373cc3ac0e56 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 21:12:59 +0200 Subject: [PATCH 40/46] Prevent None callback from causing crashes --- yowsupwrapper.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 23b84de..bfdba95 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -304,10 +304,11 @@ class YowsupApp(object): iq = GetSyncIqProtocolEntity(contacts, mode, context) def onSuccess(response, request): # Remove leading plus - existing = [s[1:] for s in response.inNumbers.keys()] - nonexisting = [s[1:] for s in response.outNumbers.keys()] - invalid = [s[1:] for s in response.invalidNumbers] - success(existing, nonexisting, invalid) + if success is not None: + existing = [s[1:] for s in response.inNumbers.keys()] + nonexisting = [s[1:] for s in response.outNumbers.keys()] + invalid = [s[1:] for s in response.invalidNumbers] + success(existing, nonexisting, invalid) self.sendIq(iq, onSuccess = onSuccess, onError = failure) @@ -324,11 +325,12 @@ class YowsupApp(object): """ iq = GetStatusesIqProtocolEntity([c + '@s.whatsapp.net' for c in contacts]) def onSuccess(response, request): - self.logger.debug("Received Statuses %s", response) - s = {} - for k, v in response.statuses.iteritems(): - s[k.split('@')[0]] = v - success(s) + if success is not None: + self.logger.debug("Received Statuses %s", response) + s = {} + for k, v in response.statuses.iteritems(): + s[k.split('@')[0]] = v + success(s) self.sendIq(iq, onSuccess = onSuccess, onError = failure) From 8bbdbb970dd3411310d4cd1c1f54172609c115a5 Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 25 Dec 2015 16:38:09 +0200 Subject: [PATCH 41/46] Allow user to set their whatsapp profile picture --- registersession.py | 3 +++ whatsappbackend.py | 6 ++++-- yowsupwrapper.py | 15 ++++++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/registersession.py b/registersession.py index 963eeb2..2fc63ca 100644 --- a/registersession.py +++ b/registersession.py @@ -142,3 +142,6 @@ class RegisterSession(YowsupApp): def requestVCard(self, buddy, ID): pass + + def setProfilePicture(self, previewPicture, fullPicture = None): + pass diff --git a/whatsappbackend.py b/whatsappbackend.py index 46b2dc5..9c00798 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -117,6 +117,10 @@ class WhatsAppBackend(SpectrumBackend): self.logger.debug("handleVCardRequest(user=%s, buddy=%s, ID=%s)", user, buddy, ID) self.sessions[user].requestVCard(buddy, ID) + def handleVCardUpdatedRequest(self, user, photo, nickname): + self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, photo, nickname) + self.session[user].setProfilePicture(photo) + def relogin(self, user, legacyName, password, extra): """ Used to re-initialize the session object. Used when finished with @@ -136,8 +140,6 @@ class WhatsAppBackend(SpectrumBackend): def handleBuddyBlockToggled(self, user, buddy, blocked): pass - def handleVCardUpdatedRequest(self, user, photo, nickname): - pass def handleAttentionRequest(self, user, buddy, message): pass diff --git a/yowsupwrapper.py b/yowsupwrapper.py index bfdba95..7d195a6 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -259,7 +259,20 @@ class YowsupApp(object): """ iq = SetStatusIqProtocolEntity(statusText) self.sendIq(iq) - + + def setProfilePicture(self, previewPicture, fullPicture = None): + """ + Requests profile picture of whatsapp user + Args: + - previewPicture: (bytes) The preview picture + - fullPicture: (bytes) The full profile picture + """ + if fullPicture == None: + fullPicture = previewPicture + ownJid = self.stack.getLayerInterface(YowAuthenticationProtocolLayer).getUsername(full = True) + iq = SetPictureIqProtocolEntity(ownJid, previewPicture, fullPicture) + self.sendIq(iq) + def sendTyping(self, phoneNumber, typing): """ Notify buddy using phoneNumber that you are typing to him From 2c2c0fde8d9fe419098d58c528705afbd35978a1 Mon Sep 17 00:00:00 2001 From: moyamo Date: Thu, 7 Jan 2016 23:04:20 +0200 Subject: [PATCH 42/46] Fix DeletePictureNotification --- buddy.py | 11 ++++++++++- deferred.py | 8 +++++++- yowsupwrapper.py | 5 +++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/buddy.py b/buddy.py index 542f798..d8d3da7 100644 --- a/buddy.py +++ b/buddy.py @@ -178,7 +178,10 @@ class BuddyList(dict): # Get profile picture self.logger.debug('Requesting profile picture of %s', buddy) response = deferred.Deferred() - self.session.requestProfilePicture(buddy, onSuccess = response.run) + # Error probably means image doesn't exist + error = deferred.Deferred() + self.session.requestProfilePicture(buddy, onSuccess=response.run, + onFailure=error.run) response = response.arg(0) pictureData = response.pictureData() @@ -188,6 +191,9 @@ class BuddyList(dict): 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 buddy == self.session.legacyName: @@ -201,6 +207,9 @@ class BuddyList(dict): image_hash = pictureData.then(utils.sha1hash) call(self.logger.debug, 'Image hash is %s', image_hash) call(self.update, buddy, nick, groups, image_hash) + # No image + error.when(self.logger.debug, 'No image') + error.when(self.update, buddy, nick, groups, '') def refresh(self, number): self.session.unsubscribePresence(number) diff --git a/deferred.py b/deferred.py index 3e86dcf..5ab3d02 100644 --- a/deferred.py +++ b/deferred.py @@ -67,6 +67,12 @@ class Deferred(object): return args[n] return self.then(helper) + def when(self, func, *args, **kwargs): + """ Calls when func(*args, **kwargs) when deferred gets a value """ + def helper(*args2, **kwargs2): + func(*args, **kwargs) + self.then(helper) + def __getattr__(self, method_name): return getattr(Then(self), method_name) @@ -103,7 +109,7 @@ def call(func, *args, **kwargs): colors = Deferred() colors.append('blue') colors.run(['red', 'green']) -call(print, colors) #=> ['red', 'green', 'blue'] + call(print, colors) #=> ['red', 'green', 'blue'] call(print, 'hi', colors) #=> hi ['red', 'green', 'blue'] """ for i, c in enumerate(args): diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 7d195a6..4cb8787 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -766,9 +766,10 @@ class YowsupAppLayer(YowInterfaceLayer): entity._from.split('@')[0], entity.status ) - elif (isinstance(entity, SetPictureNotificationProtocolEntity) or - isinstance(entity, DeletePictureNotificationProtocolEntity)): + elif isinstance(entity, SetPictureNotificationProtocolEntity): self.caller.onContactPictureChanged(entity.setJid.split('@')[0]) + elif isinstance(entity, DeletePictureNotificationProtocolEntity): + self.caller.onContactPictureChanged(entity.deleteJid.split('@')[0]) elif isinstance(entity, RemoveContactNotificationProtocolEntity): self.caller.onContactRemoved(entity.contactJid.split('@')[0]) elif isinstance(entity, AddContactNotificationProtocolEntity): From 3fba33ac8b370bbd9ac39a5b45f595415403e130 Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 8 Jan 2016 15:30:47 +0200 Subject: [PATCH 43/46] Fully intialise with whatsapp on every login --- session.py | 22 ++++++++++++++++------ yowsupwrapper.py | 17 +++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/session.py b/session.py index 266cc29..853c642 100644 --- a/session.py +++ b/session.py @@ -218,16 +218,26 @@ class Session(YowsupApp): self.backend.handleConnected(self.user) self.backend.handleBuddyChanged(self.user, "bot", self.bot.name, ["Admin"], protocol_pb2.STATUS_ONLINE) - if self.initialized == False: - self.sendOfflineMessages() - #self.bot.call("welcome") - self.initialized = True + # Initialisation? + self.requestPrivacyList() + self.requestClientConfig() + self.requestServerProperties() + # ? + + self.logger.debug('Requesting groups list') + self.requestGroupsList(self._updateGroups) + # self.requestBroadcastList() + + # This should handle, sync, statuses, and presence self.sendPresence(True) for func in self.loginQueue: func() - self.logger.debug('Requesting groups list') - self.requestGroupsList(self._updateGroups) + if self.initialized == False: + self.sendOfflineMessages() + #self.bot.call("welcome") + self.initialized = True + self.loggedIn = True # Called by superclass diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 4cb8787..1cef653 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -39,7 +39,9 @@ from yowsup.layers.protocol_notifications.protocolentities import * from yowsup.layers.protocol_messages.protocolentities import * from yowsup.layers.protocol_presence.protocolentities import * from yowsup.layers.protocol_profiles.protocolentities import * +from yowsup.layers.protocol_privacy.protocolentities import * from yowsup.layers.protocol_receipts.protocolentities import * +from yowsup.layers.protocol_iq.protocolentities import * from yowsup.layers.protocol_media.mediauploader import MediaUploader @@ -325,6 +327,21 @@ class YowsupApp(object): self.sendIq(iq, onSuccess = onSuccess, onError = failure) + def requestClientConfig(self, success = None, failure = None): + """I'm not sure what this does, but it might be required on first login.""" + iq = PushIqProtocolEntity() + self.sendIq(iq, onSuccess = success, onError = failure) + + + def requestPrivacyList(self, success = None, failure = None): + """I'm not sure what this does, but it might be required on first login.""" + iq = PrivacyListIqProtocolEntity() + self.sendIq(iq, onSuccess = success, onError = failure) + + def requestServerProperties(self, success = None, failure = None): + """I'm not sure what this does, but it might be required on first login.""" + iq = PropsIqProtocolEntity() + self.sendIq(iq, onSuccess = success, onError = failure) def requestStatuses(self, contacts, success = None, failure = None): """ From 4273153d84f1d2ba2142e3f439c4fcc60826fed0 Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 8 Jan 2016 15:32:06 +0200 Subject: [PATCH 44/46] Amend 8bbdbb970dd --- whatsappbackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/whatsappbackend.py b/whatsappbackend.py index 9c00798..1c908f5 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -118,8 +118,8 @@ class WhatsAppBackend(SpectrumBackend): self.sessions[user].requestVCard(buddy, ID) def handleVCardUpdatedRequest(self, user, photo, nickname): - self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, photo, nickname) - self.session[user].setProfilePicture(photo) + self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, nickname) + self.sessions[user].setProfilePicture(photo) def relogin(self, user, legacyName, password, extra): """ From df305b84916c43768be19d85fe9f9e14866d84d1 Mon Sep 17 00:00:00 2001 From: moyamo Date: Sat, 9 Jan 2016 17:12:59 +0200 Subject: [PATCH 45/46] Allow multiple pictures to be sent in a short period of time Picture data was stored in the session object. This means that if you send multiple pictures within a short period of time the last picture would overwrite the data of the other pictures. This would cause pictures to be lost. Now picture data is stored in closures --- deferred.py | 4 +- session.py | 121 ++++++++++++++++----------------------------- whatsappbackend.py | 7 ++- yowsupwrapper.py | 33 ++++++++----- 4 files changed, 68 insertions(+), 97 deletions(-) diff --git a/deferred.py b/deferred.py index 5ab3d02..e270c5d 100644 --- a/deferred.py +++ b/deferred.py @@ -71,7 +71,7 @@ class Deferred(object): """ Calls when func(*args, **kwargs) when deferred gets a value """ def helper(*args2, **kwargs2): func(*args, **kwargs) - self.then(helper) + return self.then(helper) def __getattr__(self, method_name): return getattr(Then(self), method_name) @@ -130,7 +130,7 @@ def call(func, *args, **kwargs): def restfunc2(*arg2, **kwarg2): apply_deferred = partial(normalfunc, *arg2, **kwarg2) return call(apply_deferred, **dict(items[i + 1:])) - return c.then(restfunc2) + return v.then(restfunc2) # No items deferred return func(*args, **kwargs) diff --git a/session.py b/session.py index 853c642..f1f6c34 100644 --- a/session.py +++ b/session.py @@ -570,16 +570,46 @@ class Session(YowsupApp): self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy) self.sendTyping(buddy, False) + def sendImage(self, message, ID, to): + if (".jpg" in message.lower()): + imgType = "jpg" + if (".webp" in message.lower()): + imgType = "webp" + + success = deferred.Deferred() + error = deferred.Deferred() + self.downloadMedia(message, success.run, error.run) + + # Success + path = success.arg(0) + call(self.logger.info, "Success: Image downloaded to %s", path) + pathWithExt = path.then(lambda p: p + "." + imgType) + call(os.rename, path, pathWithExt) + pathJpg = path.then(lambda p: p + ".jpg") + if imgType != "jpg": + im = call(Image.open, pathWithExt) + call(im.save, pathJpg) + call(os.remove, pathWithExt) + call(self.logger.info, "Sending image to %s", to) + waId = deferred.Deferred() + call(super(Session, self).sendImage, to, pathJpg, onSuccess = waId.run) + call(self.setWaId, ID, waId) + waId.when(call, os.remove, pathJpg) + waId.when(self.logger.info, "Image sent") + + # Error + error.when(self.logger.info, "Download Error. Sending message as is.") + waId = error.when(self.sendTextMessage, to, message) + call(self.setWaId, ID, waId) + + def setWaId(self, XmppId, waId): + self.msgIDs[waId] = MsgIDs(XmppId, waId) + def sendMessageToWA(self, sender, message, ID, xhtml=""): self.logger.info("Message sent from %s to %s: %s (xhtml=%s)", self.legacyName, sender, message, xhtml) message = message.encode("utf-8") - # FIXME: Fragile, should pass this in to onDlerror - self.dlerror_message = message - self.dlerror_sender = sender - self.dlerror_ID = ID - # End Fragile if sender == "bot": self.bot.parse(message) @@ -616,24 +646,11 @@ class Session(YowsupApp): self.logger.error('Group not found: %s', room) if (".jpg" in message.lower()) or (".webp" in message.lower()): - if (".jpg" in message.lower()): - self.imgType = "jpg" - if (".webp" in message.lower()): - self.imgType = "webp" - self.imgMsgId = ID - self.imgBuddy = room + "@g.us" - - - downloader = MediaDownloader(self.onDlsuccess, self.onDlerror) - downloader.download(message) - #self.imgMsgId = ID - #self.imgBuddy = room + "@g.us" - elif "geo:" in message.lower(): - self._sendLocation(room + "@g.us", message, ID) - - else: - - self.sendTextMessage(self._lengthenGroupId(room) + '@g.us', message) + self.sendImage(message, ID, room + '@g.us') + elif "geo:" in message.lower(): + self._sendLocation(room + "@g.us", message, ID) + else: + self.sendTextMessage(room + '@g.us', message) else: # private msg buddy = sender # if message == "\\lastseen": @@ -649,20 +666,8 @@ class Session(YowsupApp): #self.call("contact_getProfilePicture", (buddy + "@s.whatsapp.net",)) self.requestVCard(buddy) else: - if (".jpg" in message.lower()) or (".webp" in message.lower()): - #waId = self.call("message_imageSend", (buddy + "@s.whatsapp.net", message, None, 0, None)) - #waId = self.call("message_send", (buddy + "@s.whatsapp.net", message)) - if (".jpg" in message.lower()): - self.imgType = "jpg" - if (".webp" in message.lower()): - self.imgType = "webp" - self.imgMsgId = ID - self.imgBuddy = buddy + "@s.whatsapp.net" - - downloader = MediaDownloader(self.onDlsuccess, self.onDlerror) - downloader.download(message) - #self.imgMsgId = ID - #self.imgBuddy = buddy + "@s.whatsapp.net" + if (".jpg" in message.lower()) or (".webp" in message.lower()): + self.sendImage(message, ID, buddy + "@s.whatsapp.net") elif "geo:" in message.lower(): self._sendLocation(buddy + "@s.whatsapp.net", message, ID) else: @@ -804,48 +809,6 @@ class Session(YowsupApp): def requestVCard(self, buddy, ID=None): self.buddies.requestVCard(buddy, ID) - def onDlsuccess(self, path): - self.logger.info("Success: Image downloaded to %s", path) - os.rename(path, path+"."+self.imgType) - if self.imgType != "jpg": - im = Image.open(path+"."+self.imgType) - im.save(path+".jpg") - self.imgPath = path+".jpg" - statinfo = os.stat(self.imgPath) - name=os.path.basename(self.imgPath) - self.logger.info("Buddy %s",self.imgBuddy) - self.image_send(self.imgBuddy, self.imgPath) - - #self.logger.info("Sending picture %s of size %s with name %s",self.imgPath, statinfo.st_size, name) - #mtype = "image" - - #sha1 = hashlib.sha256() - #fp = open(self.imgPath, 'rb') - #try: - # sha1.update(fp.read()) - # hsh = base64.b64encode(sha1.digest()) - # self.call("media_requestUpload", (hsh, mtype, os.path.getsize(self.imgPath))) - #finally: - # fp.close() - - - def onDlerror(self): - self.logger.info("Download Error. Sending message as is.") - waId = self.sendTextMessage(self.dlerror_sender + '@s.whatsapp.net', self.dlerror_message) - self.msgIDs[waId] = MsgIDs(self.dlerror_ID, waId) - - - def _doSendImage(self, filePath, url, to, ip = None, caption = None): - waId = self.doSendImage(filePath, url, to, ip, caption) - self.msgIDs[waId] = MsgIDs(self.imgMsgId, waId) - - def _doSendAudio(self, filePath, url, to, ip = None, caption = None): - waId = self.doSendAudio(filePath, url, to, ip, caption) - self.msgIDs[waId] = MsgIDs(self.imgMsgId, waId) - - - - def createThumb(self, size=100, raw=False): img = Image.open(self.imgPath) width, height = img.size diff --git a/whatsappbackend.py b/whatsappbackend.py index 1c908f5..691dc73 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -121,6 +121,9 @@ class WhatsAppBackend(SpectrumBackend): self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, nickname) self.sessions[user].setProfilePicture(photo) + def handleBuddyBlockToggled(self, user, buddy, blocked): + self.logger.debug("handleBuddyBlockedToggled(user=%s, buddy=%s, blocked=%s)", user, buddy, blocked) + def relogin(self, user, legacyName, password, extra): """ Used to re-initialize the session object. Used when finished with @@ -137,10 +140,6 @@ class WhatsAppBackend(SpectrumBackend): self.sessions[user].login(password) # TODO - def handleBuddyBlockToggled(self, user, buddy, blocked): - pass - - def handleAttentionRequest(self, user, buddy, message): pass diff --git a/yowsupwrapper.py b/yowsupwrapper.py index 1cef653..f5bece2 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -43,6 +43,7 @@ from yowsup.layers.protocol_privacy.protocolentities import * from yowsup.layers.protocol_receipts.protocolentities import * from yowsup.layers.protocol_iq.protocolentities import * from yowsup.layers.protocol_media.mediauploader import MediaUploader +from yowsup.layers.protocol_media.mediadownloader import MediaDownloader # Registration @@ -136,6 +137,10 @@ class YowsupApp(object): receipt = OutgoingReceiptProtocolEntity(_id, _from, read, participant) self.sendEntity(receipt) + def downloadMedia(self, url, onSuccess = None, onFailure = None): + downloader = MediaDownloader(onSuccess, onFailure) + downloader.download(url) + def sendTextMessage(self, to, message): """ Sends a text message @@ -153,26 +158,27 @@ class YowsupApp(object): self.sendEntity(messageEntity) return messageEntity.getId() - def image_send(self, jid, path, caption = None): - entity = RequestUploadIqProtocolEntity(RequestUploadIqProtocolEntity.MEDIA_TYPE_IMAGE, filePath=path) - successFn = lambda successEntity, originalEntity: self.onRequestUploadResult(jid, path, successEntity, originalEntity, caption) + def sendImage(self, jid, path, caption = None, onSuccess = None, onFailure = None): + entity = RequestUploadIqProtocolEntity(RequestUploadIqProtocolEntity.MEDIA_TYPE_IMAGE, filePath=path) + successFn = lambda successEntity, originalEntity: self.onRequestUploadResult(jid, path, successEntity, originalEntity, caption, onSuccess, onFailure) errorFn = lambda errorEntity, originalEntity: self.onRequestUploadError(jid, path, errorEntity, originalEntity) self.sendIq(entity, successFn, errorFn) - def onRequestUploadResult(self, jid, filePath, resultRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity, caption = None): + def onRequestUploadResult(self, jid, filePath, resultRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity, caption = None, onSuccess=None, onFailure=None): if requestUploadIqProtocolEntity.mediaType == RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO: - doSendFn = self._doSendAudio + doSendFn = self.doSendAudio else: - doSendFn = self._doSendImage + doSendFn = self.doSendImage if resultRequestUploadIqProtocolEntity.isDuplicate(): doSendFn(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid, resultRequestUploadIqProtocolEntity.getIp(), caption) else: - successFn = lambda filePath, jid, url: doSendFn(filePath, url, jid, resultRequestUploadIqProtocolEntity.getIp(), caption) - mediaUploader = MediaUploader(jid, self.legacyName, filePath, + successFn = lambda filePath, jid, url: doSendFn(filePath, url, jid, resultRequestUploadIqProtocolEntity.getIp(), caption, onSuccess, onFailure) + ownNumber = self.stack.getLayerInterface(YowAuthenticationProtocolLayer).getUsername(full=False) + mediaUploader = MediaUploader(jid, ownNumber, filePath, resultRequestUploadIqProtocolEntity.getUrl(), resultRequestUploadIqProtocolEntity.getResumeOffset(), successFn, self.onUploadError, self.onUploadProgress, async=False) @@ -190,17 +196,21 @@ class YowsupApp(object): #sys.stdout.flush() pass - def doSendImage(self, filePath, url, to, ip = None, caption = None): + def doSendImage(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None): entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to, caption = caption) self.sendEntity(entity) - #self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId()) + #self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId()) + if onSuccess is not None: + onSuccess(entity.getId()) return entity.getId() - def doSendAudio(self, filePath, url, to, ip = None, caption = None): + def doSendAudio(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None): entity = AudioDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to) self.sendEntity(entity) #self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId()) + if onSuccess is not None: + onSuccess(entity.getId()) return entity.getId() @@ -310,7 +320,6 @@ class YowsupApp(object): - success: (func) - Callback; Takes three arguments: existing numbers, non-existing numbers, invalid numbers. """ - # TODO: Implement callbacks mode = GetSyncIqProtocolEntity.MODE_DELTA if delta else GetSyncIqProtocolEntity.MODE_FULL context = GetSyncIqProtocolEntity.CONTEXT_INTERACTIVE if interactive else GetSyncIqProtocolEntity.CONTEXT_REGISTRATION # International contacts must be preceded by a plus. Other numbers are From 72365712a97d3a4021e7150f95c2f0d10e9a2b6e Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sun, 10 Jan 2016 16:54:46 +0100 Subject: [PATCH 46/46] removed obsolete file (readded by previous revert) --- reader.py | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 reader.py diff --git a/reader.py b/reader.py deleted file mode 100644 index 1137973..0000000 --- a/reader.py +++ /dev/null @@ -1,48 +0,0 @@ -__author__ = "Steffen Vogel" -__copyright__ = "Copyright 2015, 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 . -""" - -import time - -def get_token(number, timeout = 30): - file = open('tokens') - file.seek(-1, 2) - - count = 0 - while count < timeout: - line = file.readline() - - if line in ["", "\n"]: - time.sleep(1) - count += 1 - continue - else: - t, n, tk = line[:-1].split("\t") - - if (n == number): - file.close() - return tk - - file.close() - - -print get_token("4917696978528")