Example #1
0
    def __init__(self, backend, user, legacyName, extra, db):
        super(Session, self).__init__()
        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.info("Created: %s", legacyName)

        self.db = db
        self.backend = backend
        self.user = user
        self.legacyName = legacyName
        self.buddies = BuddyList(self.legacyName, self.db)
        self.bot = Bot(self)

        self.status = protocol_pb2.STATUS_NONE
        self.statusMessage = ''

        self.groups = {}
        self.presenceRequested = []
        self.offlineQueue = []
        self.groupOfflineQueue = {}

        self.timer = None
        self.password = None
        self.initialized = False
        self.loggedin = False

        self.bot = Bot(self)
Example #2
0
    def __init__(self, backend, user, legacyName, extra, db):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.info("Created: %s", legacyName)

        self.db = db
        self.backend = backend
        self.user = user
        self.legacyName = legacyName

        self.status = protocol_pb2.STATUS_NONE
        self.statusMessage = ''

        self.groups = {}
        self.presenceRequested = []
        self.offlineQueue = []
        self.groupOfflineQueue = {}

        self.timer = None
        self.password = None
        self.initialized = False

        self.buddies = BuddyList(legacyName, db)
        self.frontend = YowsupConnectionManager()

        self.bot = Bot(self)

        # Events
        self.listen("auth_success", self.onAuthSuccess)
        self.listen("auth_fail", self.onAuthFailed)
        self.listen("disconnected", self.onDisconnected)

        self.listen("contact_typing", self.onContactTyping)
        self.listen("contact_paused", self.onContactPaused)

        self.listen("presence_updated", self.onPrecenceUpdated)
        self.listen("presence_available", self.onPrecenceAvailable)
        self.listen("presence_unavailable", self.onPrecenceUnavailable)

        self.listen("message_received", self.onMessageReceived)
        self.listen("image_received", self.onMediaReceived)
        self.listen("video_received", self.onMediaReceived)
        self.listen("audio_received", self.onMediaReceived)
        self.listen("location_received", self.onLocationReceived)
        self.listen("vcard_received", self.onVcardReceived)

        self.listen("group_messageReceived", self.onGroupMessageReceived)
        self.listen("group_gotInfo", self.onGroupGotInfo)
        self.listen("group_gotParticipants", self.onGroupGotParticipants)
        self.listen("group_subjectReceived", self.onGroupSubjectReceived)

        self.listen("notification_groupParticipantAdded",
                    self.onGroupParticipantAdded)
        self.listen("notification_groupParticipantRemoved",
                    self.onGroupParticipantRemoved)
        self.listen("notification_contactProfilePictureUpdated",
                    self.onContactProfilePictureUpdated)
        self.listen("notification_groupPictureUpdated",
                    self.onGroupPictureUpdated)
Example #3
0
	def __init__(self, backend, user, legacyName, extra, db):
		self.logger = logging.getLogger(self.__class__.__name__)
		self.logger.info("Created: %s", legacyName)

		self.db = db
		self.backend = backend
		self.user = user
		self.legacyName = legacyName

		self.status = protocol_pb2.STATUS_NONE
		self.statusMessage = ''

		self.groups = {}
		self.presenceRequested = []
		self.offlineQueue = []
		self.groupOfflineQueue = { }

		self.timer = None
		self.password = None
		self.initialized = False

		self.buddies = BuddyList(legacyName, db)
		self.frontend = YowsupConnectionManager()

		self.bot = Bot(self)

		# Events
		self.listen("auth_success", self.onAuthSuccess)
		self.listen("auth_fail", self.onAuthFailed)
		self.listen("disconnected", self.onDisconnected)

		self.listen("contact_typing", self.onContactTyping)
		self.listen("contact_paused", self.onContactPaused)

		self.listen("presence_updated", self.onPrecenceUpdated)
		self.listen("presence_available", self.onPrecenceAvailable)
		self.listen("presence_unavailable", self.onPrecenceUnavailable)

		self.listen("message_received", self.onMessageReceived)
		self.listen("image_received", self.onMediaReceived)
		self.listen("video_received", self.onMediaReceived)
		self.listen("audio_received", self.onMediaReceived)
		self.listen("location_received", self.onLocationReceived)
		self.listen("vcard_received", self.onVcardReceived)

		self.listen("group_messageReceived", self.onGroupMessageReceived)
		self.listen("group_gotInfo", self.onGroupGotInfo)
		self.listen("group_gotParticipants", self.onGroupGotParticipants)
		self.listen("group_subjectReceived", self.onGroupSubjectReceived)

		self.listen("notification_groupParticipantAdded", self.onGroupParticipantAdded)
		self.listen("notification_groupParticipantRemoved", self.onGroupParticipantRemoved)
		self.listen("notification_contactProfilePictureUpdated", self.onContactProfilePictureUpdated)
		self.listen("notification_groupPictureUpdated", self.onGroupPictureUpdated)
Example #4
0
    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.backend = backend
        self.user = user
        self.legacyName = legacyName

        self.status = protocol_pb2.STATUS_NONE
        self.statusMessage = ''

        self.groups = {}
        self.gotGroupList = False
        # Functions to exectute when logged in via yowsup
        self.loginQueue = []
        self.joinRoomQueue = []
        self.presenceRequested = []
        self.offlineQueue = []
        self.msgIDs = {}
        self.groupOfflineQueue = {}
        self.loggedIn = False
        self.recvMsgIDs = []

        self.timer = None
        self.password = None
        self.initialized = False
        self.lastMsgId = None
        self.synced = False

        self.buddies = BuddyList(self.legacyName, self.backend, self.user,
                                 self)
        self.bot = Bot(self)

        self.imgMsgId = None
        self.imgPath = ""
        self.imgBuddy = None
        self.imgType = ""
Example #5
0
class Session:

	def __init__(self, backend, user, legacyName, extra, db):
		self.logger = logging.getLogger(self.__class__.__name__)
		self.logger.info("Created: %s", legacyName)

		self.db = db
		self.backend = backend
		self.user = user
		self.legacyName = legacyName

		self.status = protocol_pb2.STATUS_NONE
		self.statusMessage = ''

		self.groups = {}
		self.presenceRequested = []
		self.offlineQueue = []
		self.groupOfflineQueue = { }

		self.timer = None
		self.password = None
		self.initialized = False

		self.buddies = BuddyList(legacyName, db)
		self.frontend = YowsupConnectionManager()

		self.bot = Bot(self)

		# Events
		self.listen("auth_success", self.onAuthSuccess)
		self.listen("auth_fail", self.onAuthFailed)
		self.listen("disconnected", self.onDisconnected)

		self.listen("contact_typing", self.onContactTyping)
		self.listen("contact_paused", self.onContactPaused)

		self.listen("presence_updated", self.onPrecenceUpdated)
		self.listen("presence_available", self.onPrecenceAvailable)
		self.listen("presence_unavailable", self.onPrecenceUnavailable)

		self.listen("message_received", self.onMessageReceived)
		self.listen("image_received", self.onMediaReceived)
		self.listen("video_received", self.onMediaReceived)
		self.listen("audio_received", self.onMediaReceived)
		self.listen("location_received", self.onLocationReceived)
		self.listen("vcard_received", self.onVcardReceived)

		self.listen("group_messageReceived", self.onGroupMessageReceived)
		self.listen("group_gotInfo", self.onGroupGotInfo)
		self.listen("group_gotParticipants", self.onGroupGotParticipants)
		self.listen("group_subjectReceived", self.onGroupSubjectReceived)

		self.listen("notification_groupParticipantAdded", self.onGroupParticipantAdded)
		self.listen("notification_groupParticipantRemoved", self.onGroupParticipantRemoved)
		self.listen("notification_contactProfilePictureUpdated", self.onContactProfilePictureUpdated)
		self.listen("notification_groupPictureUpdated", self.onGroupPictureUpdated)

	def __del__(self): # handleLogoutRequest
		self.logout()

	def call(self, method, args = ()):
		args = [str(s) for s in args]
		self.logger.debug("%s(%s)", method, ", ".join(args))
		self.frontend.methodInterface.call(method, args)

	def listen(self, event, callback):
		self.frontend.signalInterface.registerListener(event, callback)

	def logout(self):
		self.call("disconnect", ("logout",))

	def login(self, password):
		self.password = utils.decodePassword(password)
		self.call("auth_login", (self.legacyName, self.password))

	def updateRoomList(self):
		rooms = []
		for room, group in self.groups.iteritems():
			rooms.append([room, group.subject])

		self.backend.handleRoomList(rooms)

	# spectrum RequestMethods
	def sendTypingStarted(self, buddy):
		if buddy != "bot":
			self.logger.info("Started typing: %s to %s", self.legacyName, buddy)
			self.call("typing_send", (buddy + "@s.whatsapp.net",))

	def sendTypingStopped(self, buddy):
		if buddy != "bot":
			self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy)
			self.call("typing_paused", (buddy + "@s.whatsapp.net",))

	def sendMessageToWA(self, sender, message):
		self.logger.info("Message sent from %s to %s: %s", self.legacyName, sender, message)
		message = message.encode("utf-8")

		if sender == "bot":
			self.bot.parse(message)
		elif "-" in sender: # group msg
			if "/" in sender:
				room, buddy = sender.split("/")
				self.call("message_send", (buddy + "@s.whatsapp.net", message))
			else:
				room = sender
				group = self.groups[room]

				self.backend.handleMessage(self.user, room, message, group.nick)
				self.call("message_send", (room + "@g.us", message))
		else: # private msg
			buddy = sender
			if message == "\\lastseen":
				self.presenceRequested.append(buddy)
				self.call("presence_request", (buddy + "@s.whatsapp.net",))
			else:
				self.call("message_send", (buddy + "@s.whatsapp.net", message))

	def sendMessageToXMPP(self, buddy, messageContent, timestamp = ""):
		if timestamp:
			timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))

		if self.initialized == False:
			self.logger.debug("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.legacyName, messageContent)
			self.backend.handleMessage(self.user, buddy, messageContent, "", "", timestamp)

	def sendGroupMessageToXMPP(self, room, buddy, 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)

			if room not in self.groupOfflineQueue:
				self.groupOfflineQueue[room] = [ ]

			self.groupOfflineQueue[room].append((buddy, messageContent, timestamp))
		else:
			self.logger.debug("Group message sent from %s to %s: %s", buddy, room, messageContent) 
			self.backend.handleMessage(self.user, room, messageContent, buddy, "", timestamp)

	def changeStatus(self, status):
		if status != self.status:
			self.logger.info("Status changed: %s", status)
			self.status = status

			if status == protocol_pb2.STATUS_ONLINE or status == protocol_pb2.STATUS_FFC:
				self.call("presence_sendAvailable")
			else:
				self.call("presence_sendUnavailable")

	def changeStatusMessage(self, statusMessage):
		if statusMessage != self.statusMessage:
			self.statusMessage = statusMessage
			self.call("profile_setStatus", (statusMessage.encode("utf-8"),))
			self.logger.info("Status message changed: %s", statusMessage)

			if self.initialized == False:
				self.sendOfflineMessages()
				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], "", "", msg[2])

	# also for adding a new buddy
	def updateBuddy(self, buddy, nick, groups):
		if buddy != "bot":
			self.buddies.update(buddy, nick, groups)
			self.updateRoster()

	def removeBuddy(self, buddy):
		if buddy != "bot":
			self.logger.info("Buddy removed: %s", buddy)
			self.buddies.remove(buddy)
			self.updateRoster()

	def joinRoom(self, room, nick):
		if room in self.groups:
			group = self.groups[room]

			self.logger.info("Joining room: %s room=%s, nick=%s", self.legacyName, room, nick)

			group.nick = nick

			self.call("group_getParticipants", (room + "@g.us",))
			self.backend.handleSubject(self.user, room, group.subject, group.subjectOwner)
		else:
			self.logger.warn("Room doesn't exist: %s", room)

	def updateRoster(self):
		self.logger.debug("Update roster")

		old = self.buddies.keys()
		self.buddies.load()
		new = self.buddies.keys()

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

		for number in remove:
			self.backend.handleBuddyChanged(self.user, number, "", [], protocol_pb2.STATUS_NONE)
			self.backend.handleBuddyRemoved(self.user, number)
			self.call("presence_unsubscribe", (number + "@s.whatsapp.net",))

		for number in add:
			buddy = self.buddies[number]
			self.backend.handleBuddyChanged(self.user, number, buddy.nick, buddy.groups, protocol_pb2.STATUS_NONE)
			self.call("presence_request", (number + "@s.whatsapp.net",)) # includes presence_subscribe


	# yowsup Signals
	def onAuthSuccess(self, user):
		self.logger.info("Auth success: %s", user)

		self.backend.handleConnected(self.user)
		self.backend.handleBuddyChanged(self.user, "bot", self.bot.name, ["Admin"], protocol_pb2.STATUS_ONLINE)

		self.updateRoster()

		self.call("ready")
		self.call("group_getGroups", ("participating",))

	def onAuthFailed(self, user, reason):
		self.logger.info("Auth failed: %s (%s)", user, reason)
		self.backend.handleDisconnected(self.user, 0, reason)
		self.password = None

	def onDisconnected(self, reason):
		self.logger.info("Disconnected from whatsapp: %s (%s)", self.legacyName, reason)
		self.backend.handleDisconnected(self.user, 0, reason)

	def onMessageReceived(self, messageId, jid, messageContent, timestamp, receiptRequested, pushName, isBroadCast):
		buddy = jid.split("@")[0]
		messageContent = utils.softToUni(messageContent)

		if isBroadCast:
			self.logger.info("Broadcast received from %s to %s: %s (at ts=%s)", buddy, self.legacyName, messageContent, timestamp)
			messageContent = "[Broadcast] " + messageContent
		else:
			self.logger.info("Message received from %s to %s: %s (at ts=%s)", buddy, self.legacyName, messageContent, timestamp)

		self.sendMessageToXMPP(buddy, messageContent, timestamp)
		if receiptRequested: self.call("message_ack", (jid, messageId))

	def onMediaReceived(self, messageId, jid, preview, url, size,  receiptRequested, isBroadcast):
		buddy = jid.split("@")[0]

		self.logger.info("Media received from %s: %s", buddy, url)
		self.sendMessageToXMPP(buddy, utils.shorten(url))
		if receiptRequested: self.call("message_ack", (jid, messageId))

	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)

		url = "http://maps.google.de?%s" % urllib.urlencode({ "q": "%s %s" % (latitude, longitude) })
		self.sendMessageToXMPP(buddy, utils.shorten(url))
		if receiptRequested: self.call("message_ack", (jid, messageId))

	def onVcardReceived(self, messageId, jid, name, data, receiptRequested, isBroadcast): # TODO
		buddy = jid.split("@")[0]
		self.logger.info("VCard received from %s", buddy)
		self.sendMessageToXMPP(buddy, "Received VCard (not implemented yet)")
		if receiptRequested: self.call("message_ack", (jid, messageId))

	def onContactTyping(self, jid):
		buddy = jid.split("@")[0]
		self.logger.info("Started typing: %s", buddy)
		self.backend.handleBuddyTyping(self.user, buddy)

		if self.timer != None:
			self.timer.cancel()

	def onContactPaused(self, jid):
		buddy = jid.split("@")[0]
		self.logger.info("Paused typing: %s", buddy)
		self.backend.handleBuddyTyped(self.user, jid.split("@")[0])
		self.timer = Timer(3, self.backend.handleBuddyStoppedTyping, (self.user, buddy)).start()

	def onPrecenceUpdated(self, jid, lastseen):
		buddy = jid.split("@")[0]
		self.logger.info("Lastseen: %s %s", buddy, utils.ago(lastseen))

		if buddy in self.presenceRequested:
			timestamp = time.localtime(time.time() - lastseen)
			timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp)
			self.sendMessageToXMPP(buddy, "%s (%s)" % (timestring, utils.ago(lastseen)))
			self.presenceRequested.remove(buddy)

		if lastseen < 60:
			self.onPrecenceAvailable(jid)
		else:
			self.onPrecenceUnavailable(jid)

	def onPrecenceAvailable(self, jid):
		buddy = jid.split("@")[0]

		try:
			buddy = self.buddies[buddy]
			self.logger.info("Is available: %s", buddy)
			self.backend.handleBuddyChanged(self.user, buddy.number.number, buddy.nick, buddy.groups, protocol_pb2.STATUS_ONLINE)
		except KeyError:
			self.logger.error("Buddy not found: %s", buddy)

	def onPrecenceUnavailable(self, jid):
		buddy = jid.split("@")[0]

		try:
			buddy = self.buddies[buddy]
			self.logger.info("Is unavailable: %s", buddy)
			self.backend.handleBuddyChanged(self.user, buddy.number.number, buddy.nick, buddy.groups, protocol_pb2.STATUS_XA)
		except KeyError:
			self.logger.error("Buddy not found: %s", buddy)

	def onGroupGotInfo(self, gjid, owner, subject, subjectOwner, subjectTimestamp, creationTimestamp):
		room = gjid.split("@")[0]
		owner = owner.split("@")[0]
		subjectOwner = subjectOwner.split("@")[0]

		if room in self.groups:
			room = self.groups[room]
			room.owner = owner
			room.subjectOwner = subjectOwner
			room.subject = subject
		else:
			self.groups[room] = Group(room, owner, subject, subjectOwner)

		self.updateRoomList()

	def onGroupGotParticipants(self, gjid, jids):
		room = gjid.split("@")[0]
		group = self.groups[room]

		for jid in jids:
			buddy = jid.split("@")[0]
			self.logger.info("Added %s to room %s", buddy, room)

			if buddy == group.owner:
				flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR
			else:
				flags = protocol_pb2.PARTICIPANT_FLAG_NONE

			self.backend.handleParticipantChanged(self.user, buddy, room, flags, protocol_pb2.STATUS_ONLINE) # TODO check status

			if room in self.groupOfflineQueue:
				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],msg[1], msg[2])

	def onGroupMessageReceived(self, messageId, gjid, jid, messageContent, timestamp, receiptRequested, pushName):
		buddy = jid.split("@")[0]
		room = gjid.split("@")[0]

		self.logger.info("Group message received in  %s from %s: %s", room, buddy, messageContent)

		self.sendGroupMessageToXMPP(room, buddy, utils.softToUni(messageContent), timestamp)
		if receiptRequested: self.call("message_ack", (gjid, messageId))

	def onGroupSubjectReceived(self, messageId, gjid, jid, subject, timestamp, receiptRequested):
		room = gjid.split("@")[0]
		buddy = jid.split("@")[0]

		self.backend.handleSubject(self.user, room, subject, buddy)
		if receiptRequested: self.call("subject_ack", (gjid, messageId))

	# Yowsup Notifications
	def onGroupParticipantAdded(self, gjid, jid, author, timestamp, messageId, receiptRequested):
		room = gjid.split("@")[0]
		buddy = jid.split("@")[0]

		loggin.info("Added % to room %s", buddy, room)

		self.backend.handleParticipantChanged(self.user, buddy, room, protocol_pb2.PARTICIPANT_FLAG_NONE, protocol_pb2.STATUS_ONLINE)
		if receiptRequested: self.call("notification_ack", (gjid, messageId))

	def onGroupParticipantRemoved(self, gjid, jid, author, timestamp, messageId, receiptRequested):
		room = gjid.split("@")[0]
		buddy = jid.split("@")[0]

		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("notification_ack", (gjid, messageId))

	def onContactProfilePictureUpdated(self, jid, timestamp, messageId, pictureId, receiptRequested):
		# TODO
		if receiptRequested: self.call("notification_ack", (jid, messageId))

	def onGroupPictureUpdated(self, jid, author, timestamp, messageId, pictureId, receiptRequested):
		# TODO
		if receiptRequested: self.call("notification_ack", (jid, messageId))
Example #6
0
class Session(YowsupApp):
    broadcast_prefix = '\U0001F4E2 '

    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.backend = backend
        self.user = user
        self.legacyName = legacyName

        self.status = protocol_pb2.STATUS_NONE
        self.statusMessage = ''

        self.groups = {}
        self.gotGroupList = False
        # Functions to exectute when logged in via yowsup
        self.loginQueue = []
        self.joinRoomQueue = []
        self.presenceRequested = []
        self.offlineQueue = []
        self.msgIDs = {}
        self.groupOfflineQueue = {}
        self.loggedIn = False
        self.recvMsgIDs = []

        self.timer = None
        self.password = None
        self.initialized = False
        self.lastMsgId = None
        self.synced = False

        self.buddies = BuddyList(self.legacyName, self.backend, self.user,
                                 self)
        self.bot = Bot(self)

        self.imgMsgId = None
        self.imgPath = ""
        self.imgBuddy = None
        self.imgType = ""

    def __del__(self):  # handleLogoutRequest
        self.logout()

    def logout(self):
        self.logger.info("%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.password = password
        self.shouldBeConncted = True
        super(Session, self).login(self.legacyName, self.password)

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

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

    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)

        self.logger.debug("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"
        #self.bot.send(message)

    def _updateGroups(self, response, _):
        self.logger.debug('Received groups list %s' % response)
        groups = response.getGroups()
        for group in groups:
            room = group.getId()
            # ensure self.groups[room] exists
            if room not in self.groups:
                owner = group.getOwner().split('@')[0]
                subjectOwner = group.getSubjectOwner().split('@')[0]
                subject = utils.softToUni(group.getSubject())
                self.groups[room] = Group(room, owner, subject, subjectOwner,
                                          self.backend, self.user)
            # add/update room participants
            self.groups[room].addParticipants(group.getParticipants().keys(),
                                              self.buddies, self.legacyName)
        self.gotGroupList = True
        # join rooms
        while self.joinRoomQueue:
            self.joinRoom(*self.joinRoomQueue.pop(0))
        # deliver queued offline messages
        for room in self.groupOfflineQueue:
            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], msg[1], msg[2]))
        # pass update to backend
        self.updateRoomList()

    def joinRoom(self, room, nick):
        if not self.gotGroupList:
            self.joinRoomQueue.append((room, nick))
            return
        room = self._lengthenGroupId(room)
        if room in self.groups:
            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]
            except KeyError:
                ownerNick = group.subjectOwner

            group.sendParticipantsToSpectrum(self.legacyName)
            self.backend.handleSubject(self.user, self._shortenGroupId(room),
                                       group.subject, ownerNick)
            self.logger.debug("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)

    def leaveRoom(self, room):
        if room in self.groups:
            self.logger.info("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)

    def _lastSeen(self, number, seconds):
        self.logger.debug("Last seen %s at %s seconds" % (number, seconds))
        if seconds < 60:
            self.onPresenceAvailable(number)
        else:
            self.onPresenceUnavailable(number)

    def sendReadReceipts(self, buddy):
        for _id, _from, participant, t in self.recvMsgIDs:
            if _from.split('@')[0] == buddy:
                self.sendReceipt(_id, _from, 'read', participant)
                self.recvMsgIDs.remove((_id, _from, participant, t))
                self.logger.debug("Send read receipt to %s (ID: %s)", _from,
                                  _id)

    # Called by superclass
    def onAuthSuccess(self, status, kind, creation, expiration, props, nonce,
                      t):
        self.logger.info("Auth success: %s" % self.user)

        self.backend.handleConnected(self.user)
        self.backend.handleBuddyChanged(self.user, "bot", self.bot.name,
                                        ["Admin"], protocol_pb2.STATUS_ONLINE)
        # 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()

        if self.initialized == False:
            self.sendOfflineMessages()
            #self.bot.call("welcome")
            self.initialized = True

        self.loggedIn = True

    # Called by superclass
    def onAuthFailed(self, 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('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(
            "received receipt, sending ack: %s" %
            [_id, _from, timestamp, type, participant, offline, items])
        try:
            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:
            self.logger.error("Message %s not found. Unable to send ack" % _id)

    # Called by superclass
    def onAck(self, _id, _class, _from, timestamp):
        self.logger.debug('received ack: %s' % [_id, _class, _from, timestamp])

    # Called by superclass
    def onTextMessage(self, _id, _from, to, notify, timestamp, participant,
                      offline, retry, body):
        buddy = _from.split('@')[0]
        messageContent = utils.softToUni(body)
        self.sendReceipt(_id, _from, None, participant)
        self.recvMsgIDs.append((_id, _from, participant, timestamp))
        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 or broadcast
            partname = participant.split('@')[0]
            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)

    # Called by superclass
    def onImage(self, image):
        if image.caption is None:
            image.caption = ''

        self.onMedia(image, "image")

    # Called by superclass
    def onAudio(self, audio):
        self.onMedia(audio, "audio")

    # Called by superclass
    def onVideo(self, video):
        self.onMedia(video, "video")

    def onMedia(self, media, type):
        self.logger.debug('Received %s message: %s' % (type, media))
        buddy = media._from.split('@')[0]
        participant = media.participant
        caption = ''

        if media.isEncrypted():
            self.logger.debug('Received encrypted media message')
            if self.backend.specConf is not None and self.backend.specConf.__getitem__(
                    "service.web_directory"
            ) is not None and self.backend.specConf.__getitem__(
                    "service.web_url") is not None:
                ipath = "/" + str(media.timestamp) + media.getExtension()

                with open(
                        self.backend.specConf.__getitem__(
                            "service.web_directory") + ipath, "wb") as f:
                    f.write(media.getMediaContent())
                url = self.backend.specConf.__getitem__(
                    "service.web_url") + ipath
            else:
                self.logger.warn(
                    'Received encrypted media: web storage not set in config!')
                url = media.url

        else:
            url = media.url

        if type == 'image':
            caption = media.caption

        if participant is not None:  # Group message
            partname = participant.split('@')[0]
            if media._from.split('@')[1] == 'broadcast':  # Broadcast message
                self.sendMessageToXMPP(partname, self.broadcast_prefix,
                                       media.timestamp)
                self.sendMessageToXMPP(partname, url, media.timestamp)
                self.sendMessageToXMPP(partname, caption, media.timestamp)
            else:  # Group message
                self.sendGroupMessageToXMPP(buddy, partname, url,
                                            media.timestamp)
                self.sendGroupMessageToXMPP(buddy, partname, caption,
                                            media.timestamp)
        else:
            self.sendMessageToXMPP(buddy, url, media.timestamp)
            self.sendMessageToXMPP(buddy, caption, media.timestamp)

        self.sendReceipt(media._id, media._from, None, media.participant)
        self.recvMsgIDs.append(
            (media._id, media._from, media.participant, media.timestamp))

    def onLocation(self, location):
        buddy = location._from.split('@')[0]
        latitude = location.getLatitude()
        longitude = location.getLongitude()
        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]
            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)
        self.recvMsgIDs.append((location._id, location._from,
                                location.participant, location.timestamp))

    # Called by superclass
    def onVCard(self, _id, _from, name, card_data, to, notify, timestamp,
                participant):
        self.logger.debug(
            'received VCard: %s' %
            [_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]
            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)
        self.recvMsgIDs.append((_id, _from, participant, timestamp))

    def transferFile(self, buddy, name, data):
        # Not working
        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("Started typing: %s" % buddy)
        if buddy != 'bot':
            self.sendPresence(True)
            self.backend.handleBuddyTyping(self.user, buddy)

            if self.timer != None:
                self.timer.cancel()

    # Called by superclass
    def onContactPaused(self, buddy):
        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("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(
            "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.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" %
                          (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)

    # Called by superclass
    def onContactPictureChanged(self, number):
        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]
        try:
            buddy = self.buddies[buddy]
        except KeyError:
            # Sometimes whatsapp send our own presence
            if buddy != self.legacyName:
                self.logger.error("Buddy not found: %s" % buddy)
            return

        if (lastseen == buddy.lastseen) and (_type == buddy.presence):
            return

        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 == "unavailable":
            self.onPresenceUnavailable(buddy)
        else:
            self.onPresenceAvailable(buddy)

    def onPresenceAvailable(self, buddy):
        self.logger.info("Is available: %s" % buddy)
        self.buddies.updateSpectrum(buddy)

    def onPresenceUnavailable(self, buddy):
        self.logger.info("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))
            self.sendTyping(buddy, True)
            self.sendReadReceipts(buddy)
        # If he is typing he is present
        # I really don't know where else to put this.
        # Ideally, this should be sent if the user is looking at his client
        self.sendPresence(True)

    def sendTypingStopped(self, buddy):
        if buddy != "bot":
            self.logger.info("Stopped typing: %s to %s" %
                             (self.legacyName, buddy))
            self.sendTyping(buddy, False)
            self.sendReadReceipts(buddy)

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

        self.sendReadReceipts(sender)

        if sender == "bot":
            self.bot.parse(message)
        elif "-" in sender:  # group msg
            if "/" in sender:  # directed at single user
                room, nick = sender.split("/")
                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] != '\\\\':
                    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(
                            "Group Message from %s to %s Groups: %s" %
                            (group.nick, group, self.groups))
                        self.backend.handleMessage(self.user,
                                                   room,
                                                   message,
                                                   group.nick,
                                                   xhtml=xhtml)
                    except KeyError:
                        self.logger.error('Group not found: %s' % room)

                if (".jpg" in message.lower()) or (".webp" in message.lower()):
                    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.split(" ").pop(0) == "\\lastseen":
                self.presenceRequested.append(buddy)
                self._requestLastSeen(buddy)
            elif message.split(" ").pop(0) == "\\gpp":
                self.sendMessageToXMPP(buddy, "Fetching Profile Picture")
                self.requestVCard(buddy)
            elif (".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:
                waId = self.sendTextMessage(sender + '@s.whatsapp.net',
                                            message)
                self.msgIDs[waId] = MsgIDs(ID, waId)

            # self.logger.info("WA Message send to %s with ID %s", buddy, waId)

    def executeCommand(self, command, room):
        if command == '\\leave':
            self.logger.debug("Leaving room %s", room)
            self.leaveGroup(room)  # Leave group on whatsapp side
            group = self.groups[room]
            group.leaveRoom()  # Delete Room on spectrum side
            del self.groups[room]

    def _requestLastSeen(self, buddy):
        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)))

        def onError(errorIqEntity, originalIqEntity):
            self.sendMessageToXMPP(errorIqEntity.getFrom(), "LastSeen Error")

        self.requestLastSeen(buddy, onSuccess, onError)

    def _sendLocation(self, buddy, message, ID):
        latitude, longitude = message.split(':')[1].split(',')
        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)

    def sendMessageToXMPP(self,
                          buddy,
                          messageContent,
                          timestamp="",
                          nickname=""):
        if timestamp:
            timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))

        if self.initialized == False:
            self.logger.debug("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.legacyName, messageContent))
            self.backend.handleMessage(self.user, buddy, messageContent, "",
                                       "", timestamp)

    def sendGroupMessageToXMPP(self,
                               room,
                               number,
                               messageContent,
                               timestamp="",
                               defaultname=""):
        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" %
                              (number, room, messageContent))

            if room not in self.groupOfflineQueue:
                self.groupOfflineQueue[room] = []

            self.groupOfflineQueue[room].append(
                (number, messageContent, timestamp))
        else:
            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] == 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,
                                               nick, "", timestamp)
                else:
                    self.bot.send(
                        "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))
            except KeyError:
                self.logger.warn("Group is not in group list")
                self.backend.handleMessage(self.user,
                                           self._shortenGroupId(room),
                                           messageContent, number, "",
                                           timestamp)

    def changeStatus(self, status):
        if status != self.status:
            self.logger.info("Status changed: %s" % status)
            self.status = status

            if status == protocol_pb2.STATUS_ONLINE \
              or status == protocol_pb2.STATUS_FFC:
                self.sendPresence(True)
            else:
                self.sendPresence(False)

    def changeStatusMessage(self, statusMessage):
        if (statusMessage != self.statusMessage) or (self.initialized
                                                     == False):
            self.statusMessage = statusMessage
            self.setStatus(statusMessage)
            self.logger.info("Status message changed: %s" % statusMessage)

            #if self.initialized == False:
            #	self.sendOfflineMessages()
            #	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], "", "",
                                       msg[2])

    # Called when user logs in to initialize the roster
    def loadBuddies(self, buddies):
        self.buddies.load(buddies)

    # also for adding a new buddy
    def updateBuddy(self, buddy, nick, groups, image_hash=None):
        if buddy != "bot":
            self.buddies.update(buddy, nick, groups, image_hash)

    def removeBuddy(self, buddy):
        if buddy != "bot":
            self.logger.info("Buddy removed: %s" % buddy)
            self.buddies.remove(buddy)

    def requestVCard(self, buddy, ID=None):
        self.buddies.requestVCard(buddy, ID)

    def createThumb(self, size=100, raw=False):
        img = Image.open(self.imgPath)
        width, height = img.size
        img_thumbnail = self.imgPath + '_thumbnail'

        if width > height:
            nheight = float(height) / width * size
            nwidth = size
        else:
            nwidth = float(width) / height * size
            nheight = size

        img.thumbnail((nwidth, nheight), Image.ANTIALIAS)
        img.save(img_thumbnail, 'JPEG')

        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("@")[0]
        self.logger.info("Location received from %s: %s, %s" %
                         (buddy, latitude, longitude))

        url = "http://maps.google.de?%s" % urllib.urlencode(
            {"q": "%s %s" % (latitude, longitude)})
        self.sendMessageToXMPP(buddy, utils.shorten(url))
        if receiptRequested:
            self.call("message_ack", (jid, messageId))

    def onGroupSubjectReceived(self, messageId, gjid, jid, subject, timestamp,
                               receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        self.backend.handleSubject(self.user, room, subject, buddy)
        if receiptRequested:
            self.call("subject_ack", (gjid, messageId))

    # Yowsup Notifications
    def onGroupParticipantRemoved(self, gjid, jid, author, timestamp,
                                  messageId, receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        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("notification_ack", (gjid, messageId))

    def onContactProfilePictureUpdated(self, jid, timestamp, messageId,
                                       pictureId, receiptRequested):
        # TODO
        if receiptRequested:
            self.call("notification_ack", (jid, messageId))

    def onGroupPictureUpdated(self, jid, author, timestamp, messageId,
                              pictureId, receiptRequested):
        # TODO
        if receiptRequested:
            self.call("notification_ack", (jid, messageId))
Example #7
0
class Session(YowsupApp):
    def __init__(self, backend, user, legacyName, extra, db):
        super(Session, self).__init__()
        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.info("Created: %s", legacyName)

        self.db = db
        self.backend = backend
        self.user = user
        self.legacyName = legacyName
        self.buddies = BuddyList(self.legacyName, self.db)
        self.bot = Bot(self)

        self.status = protocol_pb2.STATUS_NONE
        self.statusMessage = ''

        self.groups = {}
        self.presenceRequested = []
        self.offlineQueue = []
        self.groupOfflineQueue = {}

        self.timer = None
        self.password = None
        self.initialized = False
        self.loggedin = False

        self.bot = Bot(self)

    def __del__(self):  # handleLogoutRequest
        self.logout()

    def call(self, method, **kwargs):
        self.logger.debug(
            "%s(%s)", method,
            ", ".join(str(k) + ': ' + str(v) for k, v in kwargs.items()))
        ##self.stack.broadcastEvent(YowLayerEvent(method, **kwargs))

    def logout(self):
        self.loggedin = False
        super(Session, self).logout()

    def login(self, password):
        self.loggedin = True
        self.password = password
        super(Session, self).login(self.legacyName, self.password)

    def updateRoomList(self):
        rooms = []
        for room, group in self.groups.iteritems():
            rooms.append([room, group.subject])
        self.logger.debug("Got rooms: %s", rooms)
        self.backend.handleRoomList(rooms)

    def updateRoster(self):
        self.logger.debug("Update roster")

        old = self.buddies.keys()
        self.buddies.load()
        new = self.buddies.keys()

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

        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 add:
            buddy = self.buddies[number]
            self.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.requestLastSeen(number, self._lastSeen)
        self.logger.debug('Requesting groups list')
        self.requestGroupsList(self._updateGroups)

    def _updateGroups(self, response, request):
        self.logger.debug('Received groups list %s', response)
        # This XMPP client is not receiving this for some reason.
        groups = response.getGroups()
        for group in groups:
            room = group.getId()
            owner = group.getOwner()
            subjectOwner = group.getSubjectOwner()
            subject = group.getSubject()

            if room in self.groups:
                oroom = self.groups[room]
                oroom.owner = owner
                oroom.subjectOwner = subjectOwner
                oroom.subject = subject
            else:
                self.groups[room] = Group(room, owner, subject, subjectOwner)
            # A crude implemtation of groups that act like buddies

            self.backend.handleBuddyChanged(self.user, room, subject, [],
                                            protocol_pb2.STATUS_NONE)
        # This XMPP client is not receiving this for some reason.
#		self.updateRoomList()
#		for group in groups:
#			room = group.getId()
#			subjectOwner = group.getSubjectOwner()
#			subject = group.getSubject()
#			self.backend.handleSubject(self.user, room, subject, subjectOwner)
#			for participant in group.getParticipants():
#				buddy = participant.split('@')[0]
#				self.logger.debug("Added %s to room %s", buddy, room)
#				self.backend.handleParticipantChanged(self.user, buddy, room,
#					protocol_pb2.PARTICIPANT_FLAG_NONE, protocol_pb2.STATUS_ONLINE)

    def _lastSeen(self, number, seconds):
        self.logger.debug("Last seen %s at %s seconds" %
                          (number, str(seconds)))
        if seconds < 60:
            self.onPresenceAvailable(number)
        else:
            self.onPresenceUnavailable(number)

    # Called by superclass
    def onAuthSuccess(self, status, kind, creation, expiration, props, nonce,
                      t):
        self.logger.info("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.initialized = True
        self.sendPresence(True)

        self.updateRoster()

    # Called by superclass
    def onAuthFailed(self, reason):
        self.logger.info("Auth failed: %s (%s)", self.user, reason)
        self.backend.handleDisconnected(self.user, 0, reason)
        self.password = None

    # Called by superclass
    def onDisconnect(self):
        self.logger.debug('Disconnected')
        self.backend.handleDisconnected(self.user, 0,
                                        'Disconnected for unknown reasons')
        self.loggedin = False

    # 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, type, participant, offline, items])))
        buddy = self.buddies[_from.split('@')[0]]
        self.backend.handleBuddyChanged(self.user, buddy.number.number,
                                        buddy.nick, buddy.groups,
                                        protocol_pb2.STATUS_ONLINE)

    # Called by superclass
    def onAck(self, _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('received TextMessage' + ' '.join(
            map(str, [
                _id, _from, to, notify, timestamp, participant, offline, retry,
                body
            ])))
        buddy = _from.split('@')[0]
        messageContent = utils.softToUni(body)
        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:
            partname = participant.split('@')[0]
            message = partname + ': ' + messageContent
            self.sendMessageToXMPP(buddy, message, timestamp)
        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):
        self.logger.debug('Received image message %s', str(image))
        buddy = image._from.split('@')[0]
        message = image.url + ' ' + image.caption
        self.sendMessageToXMPP(buddy, message, image.timestamp)
        self.sendReceipt(image._id, image._from, None, image.participant)

    # Called by superclass
    def onAudio(self, audio):
        self.logger.debug('Received audio message %s', str(audio))
        buddy = audio._from.split('@')[0]
        message = audio.url
        self.sendMessageToXMPP(buddy, message, audio.timestamp)
        self.sendReceipt(audio._id, audio._from, None, audio.participant)

    # Called by superclass
    def onVideo(self, video):
        self.logger.debug('Received video message %s', str(video))
        buddy = video._from.split('@')[0]
        message = video.url
        self.sendMessageToXMPP(buddy, message, video.timestamp)
        self.sendReceipt(video._id, video._from, None, video.participant)

    # Called by superclass
    def onVCard(self, _id, _from, name, card_data, to, notify, timestamp,
                participant):
        self.logger.debug('received VCard' + ' '.join(
            map(str, [
                _id, _from, name, card_data, to, notify, timestamp, participant
            ])))
        buddy = _from.split("@")[0]
        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 %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("Started typing: %s", buddy)
        self.sendPresence(True)
        self.backend.handleBuddyTyping(self.user, buddy)

        if self.timer != None:
            self.timer.cancel()

    # Called by superclass
    def onContactPaused(self, buddy):
        self.logger.info("Paused typing: %s", buddy)
        self.backend.handleBuddyTyped(self.user, buddy)
        self.timer = Timer(3, self.backend.handleBuddyStoppedTyping,
                           (self.user, buddy)).start()

    def onPresenceReceived(self, _type, name, jid, lastseen):
        self.logger.info("Presence received: %s %s %s %s", _type, name, jid,
                         lastseen)
        buddy = jid.split("@")[0]
        #		seems to be causing an error
        #		self.logger.info("Lastseen: %s %s", buddy, utils.ago(lastseen))

        if buddy in self.presenceRequested:
            timestamp = time.localtime(time.time() - lastseen)
            timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp)
            self.sendMessageToXMPP(
                buddy, "%s (%s)" % (timestring, utils.ago(lastseen)))
            self.presenceRequested.remove(buddy)

        if lastseen < 60:
            self.onPresenceAvailable(buddy)
        else:
            self.onPresenceUnavailable(buddy)

    def onPresenceAvailable(self, buddy):
        try:
            buddy = self.buddies[buddy]
            self.logger.info("Is available: %s", buddy)
            self.backend.handleBuddyChanged(self.user, buddy.number.number,
                                            buddy.nick, buddy.groups,
                                            protocol_pb2.STATUS_ONLINE)
        except KeyError:
            self.logger.error("Buddy not found: %s", buddy)

    def onPresenceUnavailable(self, buddy):
        try:
            buddy = self.buddies[buddy]
            self.logger.info("Is unavailable: %s", buddy)
            self.backend.handleBuddyChanged(self.user, buddy.number.number,
                                            buddy.nick, buddy.groups,
                                            protocol_pb2.STATUS_XA)
        except KeyError:
            self.logger.error("Buddy not found: %s", buddy)

    # spectrum RequestMethods
    def sendTypingStarted(self, 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.
        # Ideally, this should be sent if the user is looking at his client
        self.sendPresence(True)

    def sendTypingStopped(self, buddy):
        if buddy != "bot":
            self.logger.info("Stopped typing: %s to %s", self.legacyName,
                             buddy)
            self.sendTyping(buddy, False)

    def sendMessageToWA(self, sender, message):
        self.logger.info("Message sent from %s to %s: %s", self.legacyName,
                         sender, message)
        message = message.encode("utf-8")

        if sender == "bot":
            self.bot.parse(message)
        elif "-" in sender:  # group msg
            if "/" in sender:
                room, buddy = sender.split("/")
                self.sendTextMessage(buddy + '@s.whatsapp.net', message)
            else:
                room = sender
                #				group = self.groups[room]

                #				self.backend.handleMessage(self.user, room, message, group.nick)
                self.sendTextMessage(room + '@g.us', message)

        else:  # private msg
            buddy = sender
            #			if message == "\\lastseen":
            #				self.call("presence_request", buddy = (buddy + "@s.whatsapp.net",))
            #			else:
            self.sendTextMessage(sender + '@s.whatsapp.net', message)

    def sendMessageToXMPP(self,
                          buddy,
                          messageContent,
                          timestamp="",
                          nickname=""):
        if timestamp:
            timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))

        if self.initialized == False:
            self.logger.debug("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.legacyName, messageContent)
            self.backend.handleMessage(self.user, buddy, messageContent, "",
                                       "", timestamp)

    def sendGroupMessageToXMPP(self,
                               room,
                               buddy,
                               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)

            if room not in self.groupOfflineQueue:
                self.groupOfflineQueue[room] = []

            self.groupOfflineQueue[room].append(
                (buddy, messageContent, timestamp))
        else:
            self.logger.debug("Group message sent from %s to %s: %s", buddy,
                              room, messageContent)
            self.backend.handleMessage(self.user, room, messageContent, buddy,
                                       "", timestamp)

    def changeStatus(self, status):
        if status != self.status:
            self.logger.info("Status changed: %s", status)
            self.status = status

            if status == protocol_pb2.STATUS_ONLINE or status == protocol_pb2.STATUS_FFC:
                self.sendPresence(True)
            else:
                self.sendPresence(False)

    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)

            if self.initialized == False:
                self.sendOfflineMessages()
                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], "", "",
                                       msg[2])

    # also for adding a new buddy
    def updateBuddy(self, buddy, nick, groups, image_hash=""):
        if buddy != "bot":
            self.buddies.update(buddy, nick, groups, image_hash)
            self.updateRoster()

    def removeBuddy(self, buddy):
        if buddy != "bot":
            self.logger.info("Buddy removed: %s", buddy)
            self.buddies.remove(buddy)
            self.updateRoster()

    def joinRoom(self, room, nick):
        if room in self.groups:
            group = self.groups[room]

            self.logger.info("Joining room: %s room=%s, nick=%s",
                             self.legacyName, room, nick)

            group.nick = nick

            self.call("group_getParticipants", (room + "@g.us", ))
            self.backend.handleSubject(self.user, room, group.subject,
                                       group.subjectOwner)
        else:
            self.logger.warn("Room doesn't exist: %s", room)

    def requestVCard(self, buddy, ID):
        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)
            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('Requesting profile picture of %s', buddy)
        self.requestProfilePicture(buddy, onSuccess=onSuccess)

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

        url = "http://maps.google.de?%s" % urllib.urlencode(
            {"q": "%s %s" % (latitude, longitude)})
        self.sendMessageToXMPP(buddy, utils.shorten(url))
        if receiptRequested: self.call("message_ack", (jid, messageId))

    def onGroupGotParticipants(self, gjid, jids):
        room = gjid.split("@")[0]
        group = self.groups[room]

        for jid in jids:
            buddy = jid.split("@")[0]
            self.logger.info("Added %s to room %s", buddy, room)

            if buddy == group.owner:
                flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR
            else:
                flags = protocol_pb2.PARTICIPANT_FLAG_NONE

            self.backend.handleParticipantChanged(
                self.user, buddy, room, flags,
                protocol_pb2.STATUS_ONLINE)  # TODO check status

            if room in self.groupOfflineQueue:
                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], msg[1], msg[2])

    def onGroupMessageReceived(self, messageId, gjid, jid, messageContent,
                               timestamp, receiptRequested, pushName):
        buddy = jid.split("@")[0]
        room = gjid.split("@")[0]

        self.logger.info("Group message received in  %s from %s: %s", room,
                         buddy, messageContent)

        self.sendGroupMessageToXMPP(room, buddy,
                                    utils.softToUni(messageContent), timestamp)
        if receiptRequested: self.call("message_ack", (gjid, messageId))

    def onGroupSubjectReceived(self, messageId, gjid, jid, subject, timestamp,
                               receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        self.backend.handleSubject(self.user, room, subject, buddy)
        if receiptRequested: self.call("subject_ack", (gjid, messageId))

    # Yowsup Notifications
    def onGroupParticipantRemoved(self, gjid, jid, author, timestamp,
                                  messageId, receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        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("notification_ack", (gjid, messageId))

    def onContactProfilePictureUpdated(self, jid, timestamp, messageId,
                                       pictureId, receiptRequested):
        # TODO
        if receiptRequested: self.call("notification_ack", (jid, messageId))

    def onGroupPictureUpdated(self, jid, author, timestamp, messageId,
                              pictureId, receiptRequested):
        # TODO
        if receiptRequested: self.call("notification_ack", (jid, messageId))
Example #8
0
class Session:
    def __init__(self, backend, user, legacyName, extra, db):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.info("Created: %s", legacyName)

        self.db = db
        self.backend = backend
        self.user = user
        self.legacyName = legacyName

        self.status = protocol_pb2.STATUS_NONE
        self.statusMessage = ''

        self.groups = {}
        self.presenceRequested = []
        self.offlineQueue = []
        self.groupOfflineQueue = {}

        self.timer = None
        self.password = None
        self.initialized = False

        self.buddies = BuddyList(legacyName, db)
        self.frontend = YowsupConnectionManager()

        self.bot = Bot(self)

        # Events
        self.listen("auth_success", self.onAuthSuccess)
        self.listen("auth_fail", self.onAuthFailed)
        self.listen("disconnected", self.onDisconnected)

        self.listen("contact_typing", self.onContactTyping)
        self.listen("contact_paused", self.onContactPaused)

        self.listen("presence_updated", self.onPrecenceUpdated)
        self.listen("presence_available", self.onPrecenceAvailable)
        self.listen("presence_unavailable", self.onPrecenceUnavailable)

        self.listen("message_received", self.onMessageReceived)
        self.listen("image_received", self.onMediaReceived)
        self.listen("video_received", self.onMediaReceived)
        self.listen("audio_received", self.onMediaReceived)
        self.listen("location_received", self.onLocationReceived)
        self.listen("vcard_received", self.onVcardReceived)

        self.listen("group_messageReceived", self.onGroupMessageReceived)
        self.listen("group_gotInfo", self.onGroupGotInfo)
        self.listen("group_gotParticipants", self.onGroupGotParticipants)
        self.listen("group_subjectReceived", self.onGroupSubjectReceived)

        self.listen("notification_groupParticipantAdded",
                    self.onGroupParticipantAdded)
        self.listen("notification_groupParticipantRemoved",
                    self.onGroupParticipantRemoved)
        self.listen("notification_contactProfilePictureUpdated",
                    self.onContactProfilePictureUpdated)
        self.listen("notification_groupPictureUpdated",
                    self.onGroupPictureUpdated)

    def __del__(self):  # handleLogoutRequest
        self.logout()

    def call(self, method, args=()):
        args = [str(s) for s in args]
        self.logger.debug("%s(%s)", method, ", ".join(args))
        self.frontend.methodInterface.call(method, args)

    def listen(self, event, callback):
        self.frontend.signalInterface.registerListener(event, callback)

    def logout(self):
        self.call("disconnect", ("logout", ))

    def login(self, password):
        self.password = utils.decodePassword(password)
        self.call("auth_login", (self.legacyName, self.password))

    def updateRoomList(self):
        rooms = []
        for room, group in self.groups.iteritems():
            rooms.append([room, group.subject])

        self.backend.handleRoomList(rooms)

    # spectrum RequestMethods
    def sendTypingStarted(self, buddy):
        if buddy != "bot":
            self.logger.info("Started typing: %s to %s", self.legacyName,
                             buddy)
            self.call("typing_send", (buddy + "@s.whatsapp.net", ))

    def sendTypingStopped(self, buddy):
        if buddy != "bot":
            self.logger.info("Stopped typing: %s to %s", self.legacyName,
                             buddy)
            self.call("typing_paused", (buddy + "@s.whatsapp.net", ))

    def sendMessageToWA(self, sender, message):
        self.logger.info("Message sent from %s to %s: %s", self.legacyName,
                         sender, message)
        message = message.encode("utf-8")

        if sender == "bot":
            self.bot.parse(message)
        elif "-" in sender:  # group msg
            if "/" in sender:
                room, buddy = sender.split("/")
                self.call("message_send", (buddy + "@s.whatsapp.net", message))
            else:
                room = sender
                group = self.groups[room]

                self.backend.handleMessage(self.user, room, message,
                                           group.nick)
                self.call("message_send", (room + "@g.us", message))
        else:  # private msg
            buddy = sender
            if message == "\\lastseen":
                self.presenceRequested.append(buddy)
                self.call("presence_request", (buddy + "@s.whatsapp.net", ))
            else:
                self.call("message_send", (buddy + "@s.whatsapp.net", message))

    def sendMessageToXMPP(self, buddy, messageContent, timestamp=""):
        if timestamp:
            timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))

        if self.initialized == False:
            self.logger.debug("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.legacyName, messageContent)
            self.backend.handleMessage(self.user, buddy, messageContent, "",
                                       "", timestamp)

    def sendGroupMessageToXMPP(self,
                               room,
                               buddy,
                               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)

            if room not in self.groupOfflineQueue:
                self.groupOfflineQueue[room] = []

            self.groupOfflineQueue[room].append(
                (buddy, messageContent, timestamp))
        else:
            self.logger.debug("Group message sent from %s to %s: %s", buddy,
                              room, messageContent)
            self.backend.handleMessage(self.user, room, messageContent, buddy,
                                       "", timestamp)

    def changeStatus(self, status):
        if status != self.status:
            self.logger.info("Status changed: %s", status)
            self.status = status

            if status == protocol_pb2.STATUS_ONLINE or status == protocol_pb2.STATUS_FFC:
                self.call("presence_sendAvailable")
            else:
                self.call("presence_sendUnavailable")

    def changeStatusMessage(self, statusMessage):
        if (statusMessage != self.statusMessage) or (self.initialized
                                                     == False):
            self.statusMessage = statusMessage
            self.call("profile_setStatus", (statusMessage.encode("utf-8"), ))
            self.logger.info("Status message changed: %s", statusMessage)

            if self.initialized == False:
                self.sendOfflineMessages()
                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], "", "",
                                       msg[2])

    # also for adding a new buddy
    def updateBuddy(self, buddy, nick, groups):
        if buddy != "bot":
            self.buddies.update(buddy, nick, groups)
            self.updateRoster()

    def removeBuddy(self, buddy):
        if buddy != "bot":
            self.logger.info("Buddy removed: %s", buddy)
            self.buddies.remove(buddy)
            self.updateRoster()

    def joinRoom(self, room, nick):
        if room in self.groups:
            group = self.groups[room]

            self.logger.info("Joining room: %s room=%s, nick=%s",
                             self.legacyName, room, nick)

            group.nick = nick

            self.call("group_getParticipants", (room + "@g.us", ))
            self.backend.handleSubject(self.user, room, group.subject,
                                       group.subjectOwner)
        else:
            self.logger.warn("Room doesn't exist: %s", room)

    def updateRoster(self):
        self.logger.debug("Update roster")

        old = self.buddies.keys()
        self.buddies.load()
        new = self.buddies.keys()

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

        for number in remove:
            self.backend.handleBuddyChanged(self.user, number, "", [],
                                            protocol_pb2.STATUS_NONE)
            self.backend.handleBuddyRemoved(self.user, number)
            self.call("presence_unsubscribe", (number + "@s.whatsapp.net", ))

        for number in add:
            buddy = self.buddies[number]
            self.backend.handleBuddyChanged(self.user, number, buddy.nick,
                                            buddy.groups,
                                            protocol_pb2.STATUS_NONE)
            self.call(
                "presence_request",
                (number + "@s.whatsapp.net", ))  # includes presence_subscribe

    # yowsup Signals
    def onAuthSuccess(self, user):
        self.logger.info("Auth success: %s", user)

        self.backend.handleConnected(self.user)
        self.backend.handleBuddyChanged(self.user, "bot", self.bot.name,
                                        ["Admin"], protocol_pb2.STATUS_ONLINE)

        self.updateRoster()

        self.call("ready")
        self.call("group_getGroups", ("participating", ))

    def onAuthFailed(self, user, reason):
        self.logger.info("Auth failed: %s (%s)", user, reason)
        self.backend.handleDisconnected(self.user, 0, reason)
        self.password = None

    def onDisconnected(self, reason):
        self.logger.info("Disconnected from whatsapp: %s (%s)",
                         self.legacyName, reason)
        self.backend.handleDisconnected(self.user, 0, reason)

    def onMessageReceived(self, messageId, jid, messageContent, timestamp,
                          receiptRequested, pushName, isBroadCast):
        buddy = jid.split("@")[0]
        messageContent = utils.softToUni(messageContent)

        if isBroadCast:
            self.logger.info("Broadcast received from %s to %s: %s (at ts=%s)",
                             buddy, self.legacyName, messageContent, timestamp)
            messageContent = "[Broadcast] " + messageContent
        else:
            self.logger.info("Message received from %s to %s: %s (at ts=%s)",
                             buddy, self.legacyName, messageContent, timestamp)

        self.sendMessageToXMPP(buddy, messageContent, timestamp)
        if receiptRequested: self.call("message_ack", (jid, messageId))

    def onMediaReceived(self, messageId, jid, preview, url, size,
                        receiptRequested, isBroadcast):
        buddy = jid.split("@")[0]

        self.logger.info("Media received from %s: %s", buddy, url)
        self.sendMessageToXMPP(buddy, utils.shorten(url))
        if receiptRequested: self.call("message_ack", (jid, messageId))

    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)

        url = "http://maps.google.de?%s" % urllib.urlencode(
            {"q": "%s %s" % (latitude, longitude)})
        self.sendMessageToXMPP(buddy, utils.shorten(url))
        if receiptRequested: self.call("message_ack", (jid, messageId))

    def onVcardReceived(self, messageId, jid, name, data, receiptRequested,
                        isBroadcast):  # TODO
        buddy = jid.split("@")[0]
        self.logger.info("VCard received from %s", buddy)
        self.sendMessageToXMPP(buddy, "Received VCard (not implemented yet)")
        if receiptRequested: self.call("message_ack", (jid, messageId))

    def onContactTyping(self, jid):
        buddy = jid.split("@")[0]
        self.logger.info("Started typing: %s", buddy)
        self.backend.handleBuddyTyping(self.user, buddy)

        if self.timer != None:
            self.timer.cancel()

    def onContactPaused(self, jid):
        buddy = jid.split("@")[0]
        self.logger.info("Paused typing: %s", buddy)
        self.backend.handleBuddyTyped(self.user, jid.split("@")[0])
        self.timer = Timer(3, self.backend.handleBuddyStoppedTyping,
                           (self.user, buddy)).start()

    def onPrecenceUpdated(self, jid, lastseen):
        buddy = jid.split("@")[0]
        self.logger.info("Lastseen: %s %s", buddy, utils.ago(lastseen))

        if buddy in self.presenceRequested:
            timestamp = time.localtime(time.time() - lastseen)
            timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp)
            self.sendMessageToXMPP(
                buddy, "%s (%s)" % (timestring, utils.ago(lastseen)))
            self.presenceRequested.remove(buddy)

        if lastseen < 60:
            self.onPrecenceAvailable(jid)
        else:
            self.onPrecenceUnavailable(jid)

    def onPrecenceAvailable(self, jid):
        buddy = jid.split("@")[0]

        try:
            buddy = self.buddies[buddy]
            self.logger.info("Is available: %s", buddy)
            self.backend.handleBuddyChanged(self.user, buddy.number.number,
                                            buddy.nick, buddy.groups,
                                            protocol_pb2.STATUS_ONLINE)
        except KeyError:
            self.logger.error("Buddy not found: %s", buddy)

    def onPrecenceUnavailable(self, jid):
        buddy = jid.split("@")[0]

        try:
            buddy = self.buddies[buddy]
            self.logger.info("Is unavailable: %s", buddy)
            self.backend.handleBuddyChanged(self.user, buddy.number.number,
                                            buddy.nick, buddy.groups,
                                            protocol_pb2.STATUS_XA)
        except KeyError:
            self.logger.error("Buddy not found: %s", buddy)

    def onGroupGotInfo(self, gjid, owner, subject, subjectOwner,
                       subjectTimestamp, creationTimestamp):
        room = gjid.split("@")[0]
        owner = owner.split("@")[0]
        subjectOwner = subjectOwner.split("@")[0]

        if room in self.groups:
            room = self.groups[room]
            room.owner = owner
            room.subjectOwner = subjectOwner
            room.subject = subject
        else:
            self.groups[room] = Group(room, owner, subject, subjectOwner)

        self.updateRoomList()

    def onGroupGotParticipants(self, gjid, jids):
        room = gjid.split("@")[0]
        group = self.groups[room]

        for jid in jids:
            buddy = jid.split("@")[0]
            self.logger.info("Added %s to room %s", buddy, room)

            if buddy == group.owner:
                flags = protocol_pb2.PARTICIPANT_FLAG_MODERATOR
            else:
                flags = protocol_pb2.PARTICIPANT_FLAG_NONE

            self.backend.handleParticipantChanged(
                self.user, buddy, room, flags,
                protocol_pb2.STATUS_ONLINE)  # TODO check status

            if room in self.groupOfflineQueue:
                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], msg[1], msg[2])

    def onGroupMessageReceived(self, messageId, gjid, jid, messageContent,
                               timestamp, receiptRequested, pushName):
        buddy = jid.split("@")[0]
        room = gjid.split("@")[0]

        self.logger.info("Group message received in  %s from %s: %s", room,
                         buddy, messageContent)

        self.sendGroupMessageToXMPP(room, buddy,
                                    utils.softToUni(messageContent), timestamp)
        if receiptRequested: self.call("message_ack", (gjid, messageId))

    def onGroupSubjectReceived(self, messageId, gjid, jid, subject, timestamp,
                               receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        self.backend.handleSubject(self.user, room, subject, buddy)
        if receiptRequested: self.call("subject_ack", (gjid, messageId))

    # Yowsup Notifications
    def onGroupParticipantAdded(self, gjid, jid, author, timestamp, messageId,
                                receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        loggin.info("Added % to room %s", buddy, room)

        self.backend.handleParticipantChanged(
            self.user, buddy, room, protocol_pb2.PARTICIPANT_FLAG_NONE,
            protocol_pb2.STATUS_ONLINE)
        if receiptRequested: self.call("notification_ack", (gjid, messageId))

    def onGroupParticipantRemoved(self, gjid, jid, author, timestamp,
                                  messageId, receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        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("notification_ack", (gjid, messageId))

    def onContactProfilePictureUpdated(self, jid, timestamp, messageId,
                                       pictureId, receiptRequested):
        # TODO
        if receiptRequested: self.call("notification_ack", (jid, messageId))

    def onGroupPictureUpdated(self, jid, author, timestamp, messageId,
                              pictureId, receiptRequested):
        # TODO
        if receiptRequested: self.call("notification_ack", (jid, messageId))
Example #9
0
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.backend = backend
        self.user = user
        self.legacyName = legacyName

        self.status = protocol_pb2.STATUS_NONE
        self.statusMessage = ''

        self.groups = {}
        self.gotGroupList = False
        # Functions to exectute when logged in via yowsup
        self.loginQueue = []
        self.joinRoomQueue = []
        self.presenceRequested = []
        self.offlineQueue = []
        self.msgIDs = {}
        self.groupOfflineQueue = {}
        self.loggedIn = False

        self.timer = None
        self.password = None
        self.initialized = False
        self.lastMsgId = None
        self.synced = False

        self.buddies = BuddyList(self.legacyName, self.backend, self.user,
                                 self)
        self.bot = Bot(self)

        self.imgMsgId = None
        self.imgPath = ""
        self.imgBuddy = None
        self.imgType = ""

    def __del__(self):  # handleLogoutRequest
        self.logout()

    def logout(self):
        self.logger.info("%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.password = password
        self.shouldBeConncted = True
        super(Session, self).login(self.legacyName, self.password)

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

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

    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)

        self.logger.debug("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'
        #self.bot.send(message)

    def _updateGroups(self, response, request):
        self.logger.debug('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]
            subject = utils.softToUni(group.getSubject())

            if room in self.groups:
                oroom = self.groups[room]
                oroom.owner = owner
                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("@")[0])
            self.groups[room].addParticipants(group.getParticipants().keys(),
                                              self.buddies, self.legacyName)

            #self._addParticipantsToRoom(room, group.getParticipants())

            if room in self.groupOfflineQueue:
                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], msg[1], msg[2])
        self.gotGroupList = True
        for room, nick in self.joinRoomQueue:
            self.joinRoom(room, nick)
        self.joinRoomQueue = []
        self.updateRoomList()

    def joinRoom(self, room, nick):
        if not self.gotGroupList:
            self.joinRoomQueue.append((room, nick))
            return
        room = self._lengthenGroupId(room)
        if room in self.groups:
            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]
            except KeyError:
                ownerNick = group.subjectOwner

            group.sendParticipantsToSpectrum(self.legacyName)
            self.backend.handleSubject(self.user, self._shortenGroupId(room),
                                       group.subject, ownerNick)
            self.logger.debug("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)

    def leaveRoom(self, room):
        if room in self.groups:
            self.logger.info("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)

    def _lastSeen(self, number, seconds):
        self.logger.debug("Last seen %s at %s seconds" %
                          (number, str(seconds)))
        if seconds < 60:
            self.onPresenceAvailable(number)
        else:
            self.onPresenceUnavailable(number)

    # Called by superclass
    def onAuthSuccess(self, status, kind, creation, expiration, props, nonce,
                      t):
        self.logger.info("Auth success: %s", self.user)

        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
        self.sendPresence(True)
        for func in self.loginQueue:
            func()

        self.logger.debug('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.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')

    # 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, 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]

        except KeyError:
            pass

    # Called by superclass
    def onAck(self, _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('received TextMessage' + ' '.join(
            map(str, [
                _id, _from, to, notify, timestamp, participant, offline, retry,
                body
            ])))
        buddy = _from.split('@')[0]
        messageContent = utils.softToUni(body)
        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
            partname = participant.split('@')[0]
            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):
        self.logger.debug('Received image message %s', str(image))
        buddy = image._from.split('@')[0]
        participant = image.participant
        if image.caption is None:
            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:

            self.sendMessageToXMPP(buddy, message, image.timestamp)
        self.sendReceipt(image._id, image._from, None, image.participant)

    # Called by superclass
    def onAudio(self, audio):
        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('@')[0]
            self.sendGroupMessageToXMPP(buddy, partname, message,
                                        audio.timestamp)
        else:

            self.sendMessageToXMPP(buddy, message, audio.timestamp)
        self.sendReceipt(audio._id, audio._from, None, audio.participant)

    # Called by superclass
    def onVideo(self, video):
        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('@')[0]
            self.sendGroupMessageToXMPP(buddy, partname, message,
                                        video.timestamp)
        else:

            self.sendMessageToXMPP(buddy, message, video.timestamp)
        self.sendReceipt(video._id, video._from, None, video.participant)

    def onLocation(self, location):
        buddy = location._from.split('@')[0]
        latitude = location.getLatitude()
        longitude = location.getLongitude()
        url = location.getLocationUrl()
        participant = location.participant

        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)

    # Called by superclass
    def onVCard(self, _id, _from, name, card_data, to, notify, timestamp,
                participant):
        self.logger.debug('received VCard' + ' '.join(
            map(str, [
                _id, _from, name, card_data, to, notify, timestamp, participant
            ])))
        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)")
#		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.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.sendPresence(True)
            self.backend.handleBuddyTyping(self.user, buddy)

            if self.timer != None:
                self.timer.cancel()

    # Called by superclass
    def onContactPaused(self, buddy):
        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("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(
            "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.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)
        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]
        try:
            buddy = self.buddies[buddy]
        except KeyError:
            # 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):
            return

        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 == "unavailable":
            self.onPresenceUnavailable(buddy)
        else:
            self.onPresenceAvailable(buddy)

    def onPresenceAvailable(self, buddy):
        self.logger.info("Is available: %s", buddy)
        self.buddies.updateSpectrum(buddy)

    def onPresenceUnavailable(self, buddy):
        self.logger.info("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)
            self.sendTyping(buddy, True)
        # If he is typing he is present
        # I really don't know where else to put this.
        # Ideally, this should be sent if the user is looking at his client
        self.sendPresence(True)

    def sendTypingStopped(self, 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("Message sent from %s to %s: %s", self.legacyName,
                         sender, message)

        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)
        elif "-" in sender:  # group msg
            if "/" in sender:  # directed at single user
                room, nick = sender.split("/")
                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] != '\\\\':
                    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(
                            "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)
                    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"
                    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)
        else:  # private msg
            buddy = sender
            #			if message == "\\lastseen":
            #				self.call("presence_request", buddy = (buddy + "@s.whatsapp.net",))
            #			else:
            if message.split(" ").pop(0) == "\\lastseen":
                self.presenceRequested.append(buddy)
                #self.call("presence_request", (buddy + "@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",))
                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"
                elif "geo:" in message.lower():
                    self._sendLocation(buddy + "@s.whatsapp.net", message, ID)
                else:
                    waId = self.sendTextMessage(sender + '@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)

    def executeCommand(self, command, 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("@")[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)
            del self.groups[room]

    def _requestLastSeen(self, buddy):
        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)))

        def onError(errorIqEntity, originalIqEntity):
            self.sendMessageToXMPP(errorIqEntity.getFrom(), "LastSeen Error")

        self.requestLastSeen(buddy, onSuccess, onError)

    def _sendLocation(self, buddy, message, ID):
        #with open('/opt/transwhat/map.jpg', 'rb') as imageFile:
        #        raw = base64.b64encode(imageFile.read())
        latitude, longitude = message.split(':')[1].split(',')
        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)

    def sendMessageToXMPP(self,
                          buddy,
                          messageContent,
                          timestamp="",
                          nickname=""):
        if timestamp:
            timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))

        if self.initialized == False:
            self.logger.debug("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.legacyName, messageContent)
            self.backend.handleMessage(self.user, buddy, messageContent, "",
                                       "", timestamp)

    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", number,
                              room, messageContent)

            if room not in self.groupOfflineQueue:
                self.groupOfflineQueue[room] = []

            self.groupOfflineQueue[room].append(
                (number, messageContent, timestamp))
        else:
            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] == 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,
                                               nick, "", timestamp)
                else:
                    self.bot.send(
                        "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))
            except KeyError:
                self.logger.warn("Group is not in group list")
                self.backend.handleMessage(self.user,
                                           self._shortenGroupId(room),
                                           messageContent, number, "",
                                           timestamp)

    def changeStatus(self, status):
        if status != self.status:
            self.logger.info("Status changed: %s", status)
            self.status = status

            if status == protocol_pb2.STATUS_ONLINE \
              or status == protocol_pb2.STATUS_FFC:
                self.sendPresence(True)
            else:
                self.sendPresence(False)

    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)

            #if self.initialized == False:
            #	self.sendOfflineMessages()
            #	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], "", "",
                                       msg[2])

    # Called when user logs in to initialize the roster
    def loadBuddies(self, buddies):
        self.buddies.load(buddies)

    # also for adding a new buddy
    def updateBuddy(self, buddy, nick, groups, image_hash=None):
        if buddy != "bot":
            self.buddies.update(buddy, nick, groups, image_hash)

    def removeBuddy(self, buddy):
        if buddy != "bot":
            self.logger.info("Buddy removed: %s", buddy)
            self.buddies.remove(buddy)

    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
        img_thumbnail = self.imgPath + '_thumbnail'

        if width > height:
            nheight = float(height) / width * size
            nwidth = size
        else:
            nwidth = float(width) / height * size
            nheight = size

        img.thumbnail((nwidth, nheight), Image.ANTIALIAS)
        img.save(img_thumbnail, 'JPEG')

        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("@")[0]
        self.logger.info("Location received from %s: %s, %s", buddy, latitude,
                         longitude)

        url = "http://maps.google.de?%s" % urllib.urlencode(
            {"q": "%s %s" % (latitude, longitude)})
        self.sendMessageToXMPP(buddy, utils.shorten(url))
        if receiptRequested: self.call("message_ack", (jid, messageId))

    def onGroupSubjectReceived(self, messageId, gjid, jid, subject, timestamp,
                               receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        self.backend.handleSubject(self.user, room, subject, buddy)
        if receiptRequested: self.call("subject_ack", (gjid, messageId))

    # Yowsup Notifications
    def onGroupParticipantRemoved(self, gjid, jid, author, timestamp,
                                  messageId, receiptRequested):
        room = gjid.split("@")[0]
        buddy = jid.split("@")[0]

        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("notification_ack", (gjid, messageId))

    def onContactProfilePictureUpdated(self, jid, timestamp, messageId,
                                       pictureId, receiptRequested):
        # TODO
        if receiptRequested: self.call("notification_ack", (jid, messageId))

    def onGroupPictureUpdated(self, jid, author, timestamp, messageId,
                              pictureId, receiptRequested):
        # TODO
        if receiptRequested: self.call("notification_ack", (jid, messageId))