From 4c365e3f810fe61d79035aaaceeba312c1d7818d Mon Sep 17 00:00:00 2001 From: moyamo Date: Sat, 5 Sep 2015 22:05:34 +0200 Subject: [PATCH] Add receiveing of images, videos and sound files When a whatsapp buddy sends an image, video or sound to the spectrum user, the spectrum user will receive a URL that links to the media. FileTranfer in spectrum is not working, apparently. Thus media cannot be sent yet. --- Spectrum2/backend.py | 2 +- session.py | 82 +++++++++++++++++++++++++---------- whatsappbackend.py | 6 ++- yowsupwrapper.py | 101 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 163 insertions(+), 28 deletions(-) diff --git a/Spectrum2/backend.py b/Spectrum2/backend.py index 3bef24f..a027adc 100644 --- a/Spectrum2/backend.py +++ b/Spectrum2/backend.py @@ -204,7 +204,7 @@ class SpectrumBackend: def handleFTData(self, ftID, data): d = protocol_pb2.FileTransferData() - d.ftid = ftID + d.ftID = ftID d.data = data message = WRAP(d.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_FT_DATA); diff --git a/session.py b/session.py index b480806..ddd14f8 100644 --- a/session.py +++ b/session.py @@ -204,26 +204,69 @@ class Session(YowsupApp): ) # Called by superclass - def onMessage(self, messageEntity): - self.logger.debug(str(messageEntity)) - buddy = messageEntity.getFrom().split('@')[0] - messageContent = utils.softToUni(messageEntity.getBody()) - timestamp = messageEntity.getTimestamp() + 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) + 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 - self.sendReceipt(messageEntity.getId(), messageEntity.getFrom(), None, - messageEntity.getParticipant()) + # 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) - if messageEntity.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) + # 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) - # if receiptRequested: self.call("message_ack", (jid, messageId)) + # 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): @@ -408,11 +451,6 @@ class Session(YowsupApp): 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 onGroupGotInfo(self, gjid, owner, subject, subjectOwner, subjectTimestamp, creationTimestamp): room = gjid.split("@")[0] @@ -471,7 +509,7 @@ class Session(YowsupApp): room = gjid.split("@")[0] buddy = jid.split("@")[0] - logger.info("Added % to room %s", buddy, room) + self.logger.info("Added %s 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)) diff --git a/whatsappbackend.py b/whatsappbackend.py index 4c07fb7..95a0ae1 100644 --- a/whatsappbackend.py +++ b/whatsappbackend.py @@ -64,6 +64,9 @@ class WhatsAppBackend(SpectrumBackend): # a buddy, one to the bare jid and one to /bot. This causes duplicate # messages. Since it is unlikely a user wants to send the same message # twice, we should just ignore the second message + # + # TODO Proper fix, this work around drops all duplicate messages even + # intentional ones. usersMessage = self.lastMessage[user] if buddy not in usersMessage or usersMessage[buddy] != message: self.sessions[user].sendMessageToWA(buddy, message) @@ -115,7 +118,8 @@ class WhatsAppBackend(SpectrumBackend): pass def handleFTStartRequest(self, user, buddy, fileName, size, ftID): - pass + self.logger.debug('File send request %s, for user %s, from %s, size: %s', + fileName, user, buddy, size) def handleFTFinishRequest(self, user, buddy, fileName, size, ftID): pass diff --git a/yowsupwrapper.py b/yowsupwrapper.py index c4e497b..78b0a69 100644 --- a/yowsupwrapper.py +++ b/yowsupwrapper.py @@ -30,7 +30,8 @@ from yowsup.layers.protocol_calls import YowCallsProtocolLayer # ProtocolEntities from yowsup.layers.protocol_presence.protocolentities import * -from yowsup.layers.protocol_messages.protocolentities import TextMessageProtocolEntity +from yowsup.layers.protocol_messages.protocolentities import * +from yowsup.layers.protocol_media.protocolentities import * from yowsup.layers.protocol_chatstate.protocolentities import * from yowsup.layers.protocol_acks.protocolentities import * from yowsup.layers.protocol_receipts.protocolentities import * @@ -110,8 +111,8 @@ class YowsupApp(object): Args: - _id: id of message received - - _from - - read: ('read' or something else) + - _from: jid of person who sent the message + - read: ('read' or None) None is just delivered, 'read' is read - participant """ receipt = OutgoingReceiptProtocolEntity(_id, _from, read, participant) @@ -304,6 +305,67 @@ class YowsupApp(object): """ 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: + - 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 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 sendEntity(self, entity): """Sends an entity down the stack (as if YowsupAppLayer called toLower)""" self.stack.broadcastEvent(YowLayerEvent(YowsupAppLayer.TO_LOWER_EVENT, @@ -392,7 +454,38 @@ class YowsupAppLayer(YowInterfaceLayer): @ProtocolEntityCallback('message') def onMessageReceived(self, entity): - self.caller.onMessage(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 + ) @ProtocolEntityCallback('presence') def onPresenceReceived(self, presence):