import logging import os from yowsup import env from yowsup.env import YowsupEnv from yowsup.stacks import YowStack, YowStackBuilder from yowsup.common import YowConstants from yowsup.layers import YowLayerEvent, YowParallelLayer # Layers from yowsup.layers.axolotl import AxolotlSendLayer, AxolotlControlLayer, AxolotlReceivelayer from yowsup.layers.auth import 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.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, YowNetworkLayer ) self.logger = logging.getLogger(self.__class__.__name__) stackBuilder = YowStackBuilder() self.stack = stackBuilder \ .pushDefaultLayers() \ .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.location 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)