# use unicode encoding for all literals by default (for python2.x) from __future__ import unicode_literals __author__ = "Steffen Vogel" __copyright__ = "Copyright 2015-2017, Steffen Vogel" __license__ = "GPLv3" __maintainer__ = "Steffen Vogel" __email__ = "post@steffenvogel.de" """ This file is part of transWhat transWhat is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. transwhat is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with transWhat. If not, see . """ import logging import os from yowsup import env from yowsup.env import S40YowsupEnv from yowsup.stacks import YowStack from yowsup.common import YowConstants from yowsup.layers import YowLayerEvent, YowParallelLayer from yowsup.layers.auth import AuthError from yowsup.stacks import YowStack from yowsup.stacks import YowStackBuilder from yowsup.common import YowConstants # Layers from yowsup.layers.axolotl import AxolotlSendLayer, AxolotlControlLayer, AxolotlReceivelayer from yowsup.layers.auth import YowCryptLayer, YowAuthenticationProtocolLayer from yowsup.layers.coder import YowCoderLayer from yowsup.layers.logger import YowLoggerLayer from yowsup.layers.network import YowNetworkLayer from yowsup.layers.protocol_messages import YowMessagesProtocolLayer from yowsup.layers.stanzaregulator import YowStanzaRegulator from yowsup.layers.protocol_media import YowMediaProtocolLayer from yowsup.layers.protocol_acks import YowAckProtocolLayer from yowsup.layers.protocol_receipts import YowReceiptProtocolLayer from yowsup.layers.protocol_groups import YowGroupsProtocolLayer from yowsup.layers.protocol_presence import YowPresenceProtocolLayer from yowsup.layers.protocol_ib import YowIbProtocolLayer from yowsup.layers.protocol_notifications import YowNotificationsProtocolLayer from yowsup.layers.protocol_iq import YowIqProtocolLayer from yowsup.layers.protocol_contacts import YowContactsIqProtocolLayer from yowsup.layers.protocol_chatstate import YowChatstateProtocolLayer from yowsup.layers.protocol_privacy import YowPrivacyProtocolLayer from yowsup.layers.protocol_profiles import YowProfilesProtocolLayer from yowsup.layers.protocol_calls import YowCallsProtocolLayer from yowsup.layers.axolotl.props import PROP_IDENTITY_AUTOTRUST # ProtocolEntities from yowsup.layers.protocol_acks.protocolentities import * from yowsup.layers.protocol_chatstate.protocolentities import * from yowsup.layers.protocol_contacts.protocolentities import * from yowsup.layers.protocol_groups.protocolentities import * from yowsup.layers.protocol_media.protocolentities import * from yowsup.layers.protocol_notifications.protocolentities import * from yowsup.layers.protocol_messages.protocolentities import * from yowsup.layers.protocol_presence.protocolentities import * from yowsup.layers.protocol_profiles.protocolentities import * from yowsup.layers.protocol_privacy.protocolentities import * from yowsup.layers.protocol_receipts.protocolentities import * from yowsup.layers.protocol_iq.protocolentities import * from yowsup.layers.protocol_media.mediauploader import MediaUploader from yowsup.layers.protocol_media.mediadownloader import MediaDownloader # Registration from yowsup.registration import WACodeRequest from yowsup.registration import WARegRequest from functools import partial class YowsupApp(object): def __init__(self): env.CURRENT_ENV = env.AndroidYowsupEnv() layers = (YowsupAppLayer, YowParallelLayer((YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer, YowMediaProtocolLayer, YowIbProtocolLayer, YowIqProtocolLayer, YowNotificationsProtocolLayer, YowContactsIqProtocolLayer, YowChatstateProtocolLayer, YowCallsProtocolLayer, YowPrivacyProtocolLayer, YowProfilesProtocolLayer, YowGroupsProtocolLayer, YowPresenceProtocolLayer)), AxolotlControlLayer, YowParallelLayer((AxolotlSendLayer, AxolotlReceivelayer)), YowCoderLayer, YowCryptLayer, YowStanzaRegulator, YowNetworkLayer ) self.logger = logging.getLogger(self.__class__.__name__) stackBuilder = YowStackBuilder() self.stack = stackBuilder \ .pushDefaultLayers(True) \ .push(YowsupAppLayer) \ .build() self.stack.broadcastEvent( YowLayerEvent(YowsupAppLayer.EVENT_START, caller = self) ) def login(self, username, password): """Login to yowsup Should result in onAuthSuccess or onAuthFailure to be called. Args: - username: (str) username in the form of 1239482382 (country code and cellphone number) - password: (str) base64 encoded password """ self.stack.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, (username, password)) self.stack.setProp(PROP_IDENTITY_AUTOTRUST, True) # self.stack.setProp(YowIqProtocolLayer.PROP_PING_INTERVAL, 5) try: self.stack.broadcastEvent( YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT)) except TypeError as e: # Occurs when password is not correctly formated self.onAuthFailure('password not base64 encoded') # try: # self.stack.loop(timeout=0.5, discrete=0.5) # except AuthError as e: # For some reason Yowsup throws an exception # self.onAuthFailure("%s" % e) def logout(self): """ Logout from whatsapp """ self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT)) def sendReceipt(self, _id, _from, read, participant): """ Send a receipt (delivered: double-tick, read: blue-ticks) Args: - _id: id of message received - _from: jid of person who sent the message - read: ('read' or None) None is just delivered, 'read' is read - participant """ self.logger.debug(u'Sending receipt to whatsapp: %s', [_id, _from, read, participant]) receipt = OutgoingReceiptProtocolEntity(_id, _from, read, participant) self.sendEntity(receipt) def downloadMedia(self, url, onSuccess = None, onFailure = None): downloader = MediaDownloader(onSuccess, onFailure) downloader.download(url) def sendTextMessage(self, to, message): """ Sends a text message Args: - to: (xxxxxxxxxx@s.whatsapp.net) who to send the message to - message: (str) the body of the message """ messageEntity = TextMessageProtocolEntity(message.encode('utf-8'), to = to) self.sendEntity(messageEntity) return messageEntity.getId() def sendLocation(self, to, latitude, longitude): messageEntity = LocationMediaMessageProtocolEntity(latitude,longitude, None, None, "raw", to = to) self.sendEntity(messageEntity) return messageEntity.getId() def sendImage(self, jid, path, caption = None, onSuccess = None, onFailure = None): entity = RequestUploadIqProtocolEntity(RequestUploadIqProtocolEntity.MEDIA_TYPE_IMAGE, filePath=path) successFn = lambda successEntity, originalEntity: self.onRequestUploadResult(jid, path, successEntity, originalEntity, caption, onSuccess, onFailure) errorFn = lambda errorEntity, originalEntity: self.onRequestUploadError(jid, path, errorEntity, originalEntity) self.sendIq(entity, successFn, errorFn) def onRequestUploadResult(self, jid, filePath, resultRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity, caption = None, onSuccess=None, onFailure=None): if requestUploadIqProtocolEntity.mediaType == RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO: doSendFn = self.doSendAudio else: doSendFn = self.doSendImage if resultRequestUploadIqProtocolEntity.isDuplicate(): doSendFn(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid, resultRequestUploadIqProtocolEntity.getIp(), caption, onSuccess, onFailure) else: successFn = lambda filePath, jid, url: doSendFn(filePath, url.encode('ascii','ignore'), jid, resultRequestUploadIqProtocolEntity.getIp(), caption, onSuccess, onFailure) ownNumber = self.stack.getLayerInterface(YowAuthenticationProtocolLayer).getUsername(full=False) mediaUploader = MediaUploader(jid, ownNumber, filePath, resultRequestUploadIqProtocolEntity.getUrl(), resultRequestUploadIqProtocolEntity.getResumeOffset(), successFn, self.onUploadError, self.onUploadProgress, async=False) mediaUploader.start() def onRequestUploadError(self, jid, path, errorRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity): self.logger.error("Request upload for file %s for %s failed" % (path, jid)) def onUploadError(self, filePath, jid, url): self.logger.error("Upload file %s to %s for %s failed!" % (filePath, url, jid)) def onUploadProgress(self, filePath, jid, url, progress): self.logger.info("%s => %s, %d%% \r" % (os.path.basename(filePath), jid, progress)) pass def doSendImage(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None): entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to, caption = caption) self.sendEntity(entity) #self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId()) if onSuccess is not None: onSuccess(entity.getId()) return entity.getId() def doSendAudio(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None): entity = AudioDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to) self.sendEntity(entity) #self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId()) if onSuccess is not None: onSuccess(entity.getId()) return entity.getId() def sendPresence(self, available): """ Send presence to whatsapp Args: - available: (boolean) True if available false otherwise """ if available: self.sendEntity(AvailablePresenceProtocolEntity()) else: self.sendEntity(UnavailablePresenceProtocolEntity()) def subscribePresence(self, phone_number): """ Subscribe to presence updates from phone_number Args: - phone_number: (str) The cellphone number of the person to subscribe to """ self.logger.debug("Subscribing to Presence updates from %s" % phone_number) jid = phone_number + '@s.whatsapp.net' entity = SubscribePresenceProtocolEntity(jid) self.sendEntity(entity) def unsubscribePresence(self, phone_number): """ Unsubscribe to presence updates from phone_number Args: - phone_number: (str) The cellphone number of the person to unsubscribe from """ jid = phone_number + '@s.whatsapp.net' entity = UnsubscribePresenceProtocolEntity(jid) self.sendEntity(entity) def leaveGroup(self, group): """ Permanently leave a WhatsApp group Args: - group: (str) the group id (e.g. 27831788123-144024456) """ entity = LeaveGroupsIqProtocolEntity([group + '@g.us']) self.sendEntity(entity) def setStatus(self, statusText): """ Send status to whatsapp Args: - statusTest: (str) Your whatsapp status """ iq = SetStatusIqProtocolEntity(statusText) self.sendIq(iq) def setProfilePicture(self, previewPicture, fullPicture = None): """ Requests profile picture of whatsapp user Args: - previewPicture: (bytes) The preview picture - fullPicture: (bytes) The full profile picture """ if fullPicture == None: fullPicture = previewPicture ownJid = self.stack.getLayerInterface(YowAuthenticationProtocolLayer).getUsername(full = True) iq = SetPictureIqProtocolEntity(ownJid, previewPicture, fullPicture) self.sendIq(iq) def sendTyping(self, phoneNumber, typing): """ Notify buddy using phoneNumber that you are typing to him Args: - phoneNumber: (str) cellphone number of the buddy you are typing to. - typing: (bool) True if you are typing, False if you are not """ jid = phoneNumber + '@s.whatsapp.net' if typing: state = OutgoingChatstateProtocolEntity( ChatstateProtocolEntity.STATE_TYPING, jid ) else: state = OutgoingChatstateProtocolEntity( ChatstateProtocolEntity.STATE_PAUSED, jid ) self.sendEntity(state) def sendSync(self, contacts, delta = False, interactive = True, success = None, failure = None): """ You need to sync new contacts before you interact with them, failure to do so could result in a temporary ban. Args: - contacts: ([str]) a list of phone numbers of the contacts you wish to sync - delta: (bool; default: False) If true only send new contacts to sync, if false you should send your full contact list. - interactive: (bool; default: True) Set to false if you are sure this is the first time registering - success: (func) - Callback; Takes three arguments: existing numbers, non-existing numbers, invalid numbers. """ mode = GetSyncIqProtocolEntity.MODE_DELTA if delta else GetSyncIqProtocolEntity.MODE_FULL context = GetSyncIqProtocolEntity.CONTEXT_INTERACTIVE if interactive else GetSyncIqProtocolEntity.CONTEXT_REGISTRATION # International contacts must be preceded by a plus. Other numbers are # considered local. contacts = ['+' + c for c in contacts] iq = GetSyncIqProtocolEntity(contacts, mode, context) def onSuccess(response, request): # Remove leading plus if success is not None: existing = [s[1:] for s in response.inNumbers.keys()] nonexisting = [s[1:] for s in response.outNumbers.keys()] invalid = [s[1:] for s in response.invalidNumbers] success(existing, nonexisting, invalid) self.sendIq(iq, onSuccess = onSuccess, onError = failure) def requestClientConfig(self, success = None, failure = None): """I'm not sure what this does, but it might be required on first login.""" iq = PushIqProtocolEntity() self.sendIq(iq, onSuccess = success, onError = failure) def requestPrivacyList(self, success = None, failure = None): """I'm not sure what this does, but it might be required on first login.""" iq = PrivacyListIqProtocolEntity() self.sendIq(iq, onSuccess = success, onError = failure) def requestServerProperties(self, success = None, failure = None): """I'm not sure what this does, but it might be required on first login.""" iq = PropsIqProtocolEntity() self.sendIq(iq, onSuccess = success, onError = failure) def requestStatuses(self, contacts, success = None, failure = None): """ Request the statuses of a number of users. Args: - contacts: ([str]) the phone numbers of users whose statuses you wish to request - success: (func) called when request is successful - failure: (func) called when request has failed """ iq = GetStatusesIqProtocolEntity([c + '@s.whatsapp.net' for c in contacts]) def onSuccess(response, request): if success is not None: self.logger.debug("Received Statuses %s" % response) s = {} for k, v in response.statuses.iteritems(): s[k.split('@')[0]] = v success(s) self.sendIq(iq, onSuccess = onSuccess, onError = failure) def requestLastSeen(self, phoneNumber, success = None, failure = None): """ Requests when user was last seen. Args: - phone_number: (str) the phone number of the user - success: (func) called when request is successfully processed. The first argument is the number, second argument is the seconds since last seen. - failure: (func) called when request has failed """ iq = LastseenIqProtocolEntity(phoneNumber + '@s.whatsapp.net') self.sendIq(iq, onSuccess = partial(self._lastSeenSuccess, success), onError = failure) def _lastSeenSuccess(self, success, response, request): success(response._from.split('@')[0], response.seconds) def requestProfilePicture(self, phoneNumber, onSuccess = None, onFailure = None): """ Requests profile picture of whatsapp user Args: - phoneNumber: (str) the phone number of the user - onSuccess: (func) called when request is successfully processed. - onFailure: (func) called when request has failed """ iq = GetPictureIqProtocolEntity(phoneNumber + '@s.whatsapp.net') self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def requestGroupsList(self, onSuccess = None, onFailure = None): iq = ListGroupsIqProtocolEntity() self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def requestGroupInfo(self, group, onSuccess = None, onFailure = None): """ Request info on a specific group (includes participants, subject, owner etc.) Args: - group: (str) the group id in the form of xxxxxxxxx-xxxxxxxx - onSuccess: (func) called when request is successfully processed. - onFailure: (func) called when request is has failed """ iq = InfoGroupsIqProtocolEntity(group + '@g.us') self.sendIq(iq, onSuccess = onSuccess, onError = onFailure) def requestSMSCode(self, countryCode, phoneNumber): """ Request an sms regitration code. WARNING: this function is blocking Args: countryCode: The country code of the phone you wish to register phoneNumber: phoneNumber of the phone you wish to register without the country code. """ request = WACodeRequest(countryCode, phoneNumber) return request.send() def requestPassword(self, countryCode, phoneNumber, smsCode): """ Request a password. WARNING: this function is blocking Args: countryCode: The country code of the phone you wish to register phoneNumber: phoneNumber of the phone you wish to register without the country code. smsCode: The sms code that you asked for previously """ smsCode = smsCode.replace('-', '') request = WARegRequest(countryCode, phoneNumber, smsCode) return request.send() def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t): """ Called when login is successful. Args: - status - kind - creation - expiration - props - nonce - t """ pass def onAuthFailure(self, reason): """ Called when login is a failure Args: - reason: (str) Reason for the login failure """ pass def onReceipt(self, _id, _from, timestamp, type, participant, offline, items): """ Called when a receipt is received (double tick or blue tick) Args - _id - _from - timestamp - type: Is 'read' for blue ticks and None for double-ticks - participant: (dxxxxxxxxxx@s.whatsapp.net) delivered to or read by this participant in group - offline: (True, False or None) - items """ pass def onAck(self, _id,_class, _from, timestamp): """ Called when Ack is received Args: - _id - _class: ('message', 'receipt' or something else?) - _from - timestamp """ pass def onPresenceReceived(self, _type, name, _from, last): """ Called when presence (e.g. available, unavailable) is received from whatsapp Args: - _type: (str) 'available' or 'unavailable' - _name - _from - _last """ pass def onDisconnect(self): """ Called when disconnected from whatsapp """ def onContactTyping(self, number): """ Called when contact starts to type Args: - number: (str) cellphone number of contact """ pass def onContactPaused(self, number): """ Called when contact stops typing Args: - number: (str) cellphone number of contact """ pass def onTextMessage(self, _id, _from, to, notify, timestamp, participant, offline, retry, body): """ Called when text message is received Args: - _id: - _from: (str) jid of of sender - to: - notify: (str) human readable name of _from (e.g. John Smith) - timestamp: - participant: (str) jid of user who sent the message in a groupchat - offline: - retry: - body: The content of the message """ pass def onImage(self, entity): """ Called when image message is received Args: - entity: ImageDownloadableMediaMessageProtocolEntity """ pass def onAudio(self, entity): """ Called when audio message is received Args: - entity: AudioDownloadableMediaMessageProtocolEntity """ pass def onVideo(self, entity): """ Called when video message is received Args: - entity: VideoDownloadableMediaMessageProtocolEntity """ pass def onLocation(self, entity): """ Called when location message is received Args: - entity: LocationMediaMessageProtocolEntity """ pass def onVCard(self, _id, _from, name, card_data, to, notify, timestamp, participant): """ Called when VCard message is received Args: - _id: (str) id of entity - _from: - name: - card_data: - to: - notify: - timestamp: - participant: """ pass def onAddedToGroup(self, entity): """Called when the user has been added to a new group""" pass def onParticipantsAddedToGroup(self, entity): """Called when participants have been added to a group""" pass def onParticipantsRemovedFromGroup(self, group, participants): """Called when participants have been removed from a group Args: - group: (str) id of the group (e.g. 27831788123-144024456) - participants: (list) jids of participants that are removed """ pass def onSubjectChanged(self, group, subject, subjectOwner, timestamp): """Called when someone changes the grousp subject Args: - group: (str) id of the group (e.g. 27831788123-144024456) - subject: (str) the new subject - subjectOwner: (str) the number of the person who changed the subject - timestamp: (str) time the subject was changed """ pass def onContactStatusChanged(self, number, status): """Called when a contacts changes their status Args: number: (str) the number of the contact who changed their status status: (str) the new status """ pass def onContactPictureChanged(self, number): """Called when a contact changes their profile picture Args number: (str) the number of the contact who changed their picture """ pass def onContactRemoved(self, number): """Called when a contact has been removed Args: number: (str) the number of the contact who has been removed """ pass def onContactAdded(self, number, nick): """Called when a contact has been added Args: number: (str) contacts number nick: (str) contacts nickname """ pass def onContactUpdated(self, oldNumber, newNumber): """Called when a contact has changed their number Args: oldNumber: (str) the number the contact previously used newNumber: (str) the new number of the contact """ pass def sendEntity(self, entity): """Sends an entity down the stack (as if YowsupAppLayer called toLower)""" self.stack.broadcastEvent(YowLayerEvent(YowsupAppLayer.TO_LOWER_EVENT, entity = entity )) def sendIq(self, iq, onSuccess = None, onError = None): self.stack.broadcastEvent( YowLayerEvent( YowsupAppLayer.SEND_IQ, iq = iq, success = onSuccess, failure = onError, ) ) from yowsup.layers.interface import YowInterfaceLayer, ProtocolEntityCallback class YowsupAppLayer(YowInterfaceLayer): EVENT_START = 'transwhat.event.YowsupAppLayer.start' TO_LOWER_EVENT = 'transwhat.event.YowsupAppLayer.toLower' SEND_IQ = 'transwhat.event.YowsupAppLayer.sendIq' def onEvent(self, layerEvent): # We cannot pass instance varaibles in through init, so we use an event # instead # Return False if you want the event to propogate down the stack # return True otherwise if layerEvent.getName() == YowsupAppLayer.EVENT_START: self.caller = layerEvent.getArg('caller') self.logger = logging.getLogger(self.__class__.__name__) return True elif layerEvent.getName() == YowNetworkLayer.EVENT_STATE_DISCONNECTED: self.caller.onDisconnect() return True elif layerEvent.getName() == YowsupAppLayer.TO_LOWER_EVENT: self.toLower(layerEvent.getArg('entity')) return True elif layerEvent.getName() == YowsupAppLayer.SEND_IQ: iq = layerEvent.getArg('iq') success = layerEvent.getArg('success') failure = layerEvent.getArg('failure') self._sendIq(iq, success, failure) return True return False @ProtocolEntityCallback('success') def onAuthSuccess(self, entity): # entity is SuccessProtocolEntity status = entity.status kind = entity.kind creation = entity.creation expiration = entity.expiration props = entity.props nonce = entity.nonce t = entity.t # I don't know what this is self.caller.onAuthSuccess(status, kind, creation, expiration, props, nonce, t) @ProtocolEntityCallback('failure') def onAuthFailure(self, entity): # entity is FailureProtocolEntity reason = entity.reason self.caller.onAuthFailure(reason) @ProtocolEntityCallback('receipt') def onReceipt(self, entity): """Sends ack automatically""" # entity is IncomingReceiptProtocolEntity #ack = OutgoingAckProtocolEntity(entity.getId(), # 'receipt', entity.getType(), entity.getFrom()) self.toLower(entity.ack()) _id = entity._id _from = entity._from timestamp = entity.timestamp type = entity.type participant = entity.participant offline = entity.offline items = entity.items self.caller.onReceipt(_id, _from, timestamp, type, participant, offline, items) @ProtocolEntityCallback('ack') def onAck(self, entity): # entity is IncomingAckProtocolEntity self.caller.onAck( entity._id, entity._class, entity._from, entity.timestamp ) @ProtocolEntityCallback('notification') def onNotification(self, entity): """ Sends ack automatically """ self.logger.debug("Received notification (%s): %s" % (type(entity), entity)) self.toLower(entity.ack()) if isinstance(entity, CreateGroupsNotificationProtocolEntity): self.caller.onAddedToGroup(entity) elif isinstance(entity, AddGroupsNotificationProtocolEntity): self.caller.onParticipantsAddedToGroup(entity) elif isinstance(entity, RemoveGroupsNotificationProtocolEntity): self.caller.onParticipantsRemovedFromGroup( entity.getGroupId().split('@')[0], entity.getParticipants().keys() ) elif isinstance(entity, SubjectGroupsNotificationProtocolEntity): self.caller.onSubjectChanged( entity.getGroupId().split('@')[0], entity.getSubject(), entity.getSubjectOwner(full=False), entity.getSubjectTimestamp() ) elif isinstance(entity, StatusNotificationProtocolEntity): self.caller.onContactStatusChanged( entity._from.split('@')[0], entity.status ) elif isinstance(entity, SetPictureNotificationProtocolEntity): self.caller.onContactPictureChanged(entity.setJid.split('@')[0]) elif isinstance(entity, DeletePictureNotificationProtocolEntity): self.caller.onContactPictureChanged(entity.deleteJid.split('@')[0]) elif isinstance(entity, RemoveContactNotificationProtocolEntity): self.caller.onContactRemoved(entity.contactJid.split('@')[0]) elif isinstance(entity, AddContactNotificationProtocolEntity): self.caller.onContactAdded( entity.contactJid.split('@')[0], entity.notify ) elif isinstance(entity, UpdateContactNotificationProtocolEntity): self.caller.onContactUpdated( entity._from.split('@')[0], entity.contactJid.split('@')[0], ) @ProtocolEntityCallback('message') def onMessageReceived(self, entity): self.logger.debug("Received Message: %s" % unicode(entity)) if entity.getType() == MessageProtocolEntity.MESSAGE_TYPE_TEXT: self.caller.onTextMessage( entity._id, entity._from, entity.to, entity.notify, entity.timestamp, entity.participant, entity.offline, entity.retry, entity.body ) elif entity.getType() == MessageProtocolEntity.MESSAGE_TYPE_MEDIA: if isinstance(entity, ImageDownloadableMediaMessageProtocolEntity): # There is just way too many fields to pass them into the # function self.caller.onImage(entity) elif isinstance(entity, AudioDownloadableMediaMessageProtocolEntity): self.caller.onAudio(entity) elif isinstance(entity, VideoDownloadableMediaMessageProtocolEntity): self.caller.onVideo(entity) elif isinstance(entity, VCardMediaMessageProtocolEntity): self.caller.onVCard( entity._id, entity._from, entity.name, entity.card_data, entity.to, entity.notify, entity.timestamp, entity.participant ) elif isinstance(entity, LocationMediaMessageProtocolEntity): self.caller.onLocation(entity) @ProtocolEntityCallback('presence') def onPresenceReceived(self, presence): _type = presence.getType() name = presence.getName() _from = presence.getFrom() last = presence.getLast() self.caller.onPresenceReceived(_type, name, _from, last) @ProtocolEntityCallback('chatstate') def onChatstate(self, chatstate): number = chatstate._from.split('@')[0] if chatstate.getState() == ChatstateProtocolEntity.STATE_TYPING: self.caller.onContactTyping(number) else: self.caller.onContactPaused(number)