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())