Allow multiple pictures to be sent in a short period of time

Picture data was stored in the session object. This means that if you
send multiple pictures within a short period of time the last picture
would overwrite the data of the other pictures. This would cause
pictures to be lost. Now picture data is stored in closures
This commit is contained in:
moyamo 2016-01-09 17:12:59 +02:00
parent 4273153d84
commit df305b8491
4 changed files with 68 additions and 97 deletions

View file

@ -71,7 +71,7 @@ class Deferred(object):
""" Calls when func(*args, **kwargs) when deferred gets a value """ """ Calls when func(*args, **kwargs) when deferred gets a value """
def helper(*args2, **kwargs2): def helper(*args2, **kwargs2):
func(*args, **kwargs) func(*args, **kwargs)
self.then(helper) return self.then(helper)
def __getattr__(self, method_name): def __getattr__(self, method_name):
return getattr(Then(self), method_name) return getattr(Then(self), method_name)
@ -130,7 +130,7 @@ def call(func, *args, **kwargs):
def restfunc2(*arg2, **kwarg2): def restfunc2(*arg2, **kwarg2):
apply_deferred = partial(normalfunc, *arg2, **kwarg2) apply_deferred = partial(normalfunc, *arg2, **kwarg2)
return call(apply_deferred, **dict(items[i + 1:])) return call(apply_deferred, **dict(items[i + 1:]))
return c.then(restfunc2) return v.then(restfunc2)
# No items deferred # No items deferred
return func(*args, **kwargs) return func(*args, **kwargs)

View file

@ -570,16 +570,46 @@ class Session(YowsupApp):
self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy) self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy)
self.sendTyping(buddy, False) self.sendTyping(buddy, False)
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=""): def sendMessageToWA(self, sender, message, ID, xhtml=""):
self.logger.info("Message sent from %s to %s: %s (xhtml=%s)", self.logger.info("Message sent from %s to %s: %s (xhtml=%s)",
self.legacyName, sender, message, xhtml) self.legacyName, sender, message, xhtml)
message = message.encode("utf-8") 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": if sender == "bot":
self.bot.parse(message) self.bot.parse(message)
@ -616,24 +646,11 @@ class Session(YowsupApp):
self.logger.error('Group not found: %s', room) self.logger.error('Group not found: %s', room)
if (".jpg" in message.lower()) or (".webp" in message.lower()): if (".jpg" in message.lower()) or (".webp" in message.lower()):
if (".jpg" in message.lower()): self.sendImage(message, ID, room + '@g.us')
self.imgType = "jpg" elif "geo:" in message.lower():
if (".webp" in message.lower()): self._sendLocation(room + "@g.us", message, ID)
self.imgType = "webp" else:
self.imgMsgId = ID self.sendTextMessage(room + '@g.us', message)
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 else: # private msg
buddy = sender buddy = sender
# if message == "\\lastseen": # if message == "\\lastseen":
@ -649,20 +666,8 @@ class Session(YowsupApp):
#self.call("contact_getProfilePicture", (buddy + "@s.whatsapp.net",)) #self.call("contact_getProfilePicture", (buddy + "@s.whatsapp.net",))
self.requestVCard(buddy) self.requestVCard(buddy)
else: else:
if (".jpg" in message.lower()) or (".webp" in message.lower()): if (".jpg" in message.lower()) or (".webp" in message.lower()):
#waId = self.call("message_imageSend", (buddy + "@s.whatsapp.net", message, None, 0, None)) self.sendImage(message, ID, buddy + "@s.whatsapp.net")
#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(): elif "geo:" in message.lower():
self._sendLocation(buddy + "@s.whatsapp.net", message, ID) self._sendLocation(buddy + "@s.whatsapp.net", message, ID)
else: else:
@ -804,48 +809,6 @@ class Session(YowsupApp):
def requestVCard(self, buddy, ID=None): def requestVCard(self, buddy, ID=None):
self.buddies.requestVCard(buddy, ID) 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): def createThumb(self, size=100, raw=False):
img = Image.open(self.imgPath) img = Image.open(self.imgPath)
width, height = img.size width, height = img.size

View file

@ -121,6 +121,9 @@ class WhatsAppBackend(SpectrumBackend):
self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, nickname) self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, nickname)
self.sessions[user].setProfilePicture(photo) self.sessions[user].setProfilePicture(photo)
def handleBuddyBlockToggled(self, user, buddy, blocked):
self.logger.debug("handleBuddyBlockedToggled(user=%s, buddy=%s, blocked=%s)", user, buddy, blocked)
def relogin(self, user, legacyName, password, extra): def relogin(self, user, legacyName, password, extra):
""" """
Used to re-initialize the session object. Used when finished with Used to re-initialize the session object. Used when finished with
@ -137,10 +140,6 @@ class WhatsAppBackend(SpectrumBackend):
self.sessions[user].login(password) self.sessions[user].login(password)
# TODO # TODO
def handleBuddyBlockToggled(self, user, buddy, blocked):
pass
def handleAttentionRequest(self, user, buddy, message): def handleAttentionRequest(self, user, buddy, message):
pass pass

View file

@ -43,6 +43,7 @@ from yowsup.layers.protocol_privacy.protocolentities import *
from yowsup.layers.protocol_receipts.protocolentities import * from yowsup.layers.protocol_receipts.protocolentities import *
from yowsup.layers.protocol_iq.protocolentities import * from yowsup.layers.protocol_iq.protocolentities import *
from yowsup.layers.protocol_media.mediauploader import MediaUploader from yowsup.layers.protocol_media.mediauploader import MediaUploader
from yowsup.layers.protocol_media.mediadownloader import MediaDownloader
# Registration # Registration
@ -136,6 +137,10 @@ class YowsupApp(object):
receipt = OutgoingReceiptProtocolEntity(_id, _from, read, participant) receipt = OutgoingReceiptProtocolEntity(_id, _from, read, participant)
self.sendEntity(receipt) self.sendEntity(receipt)
def downloadMedia(self, url, onSuccess = None, onFailure = None):
downloader = MediaDownloader(onSuccess, onFailure)
downloader.download(url)
def sendTextMessage(self, to, message): def sendTextMessage(self, to, message):
""" """
Sends a text message Sends a text message
@ -153,26 +158,27 @@ class YowsupApp(object):
self.sendEntity(messageEntity) self.sendEntity(messageEntity)
return messageEntity.getId() return messageEntity.getId()
def image_send(self, jid, path, caption = None): def sendImage(self, jid, path, caption = None, onSuccess = None, onFailure = None):
entity = RequestUploadIqProtocolEntity(RequestUploadIqProtocolEntity.MEDIA_TYPE_IMAGE, filePath=path) entity = RequestUploadIqProtocolEntity(RequestUploadIqProtocolEntity.MEDIA_TYPE_IMAGE, filePath=path)
successFn = lambda successEntity, originalEntity: self.onRequestUploadResult(jid, path, successEntity, originalEntity, caption) successFn = lambda successEntity, originalEntity: self.onRequestUploadResult(jid, path, successEntity, originalEntity, caption, onSuccess, onFailure)
errorFn = lambda errorEntity, originalEntity: self.onRequestUploadError(jid, path, errorEntity, originalEntity) errorFn = lambda errorEntity, originalEntity: self.onRequestUploadError(jid, path, errorEntity, originalEntity)
self.sendIq(entity, successFn, errorFn) self.sendIq(entity, successFn, errorFn)
def onRequestUploadResult(self, jid, filePath, resultRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity, caption = None): def onRequestUploadResult(self, jid, filePath, resultRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity, caption = None, onSuccess=None, onFailure=None):
if requestUploadIqProtocolEntity.mediaType == RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO: if requestUploadIqProtocolEntity.mediaType == RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO:
doSendFn = self._doSendAudio doSendFn = self.doSendAudio
else: else:
doSendFn = self._doSendImage doSendFn = self.doSendImage
if resultRequestUploadIqProtocolEntity.isDuplicate(): if resultRequestUploadIqProtocolEntity.isDuplicate():
doSendFn(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid, doSendFn(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid,
resultRequestUploadIqProtocolEntity.getIp(), caption) resultRequestUploadIqProtocolEntity.getIp(), caption)
else: else:
successFn = lambda filePath, jid, url: doSendFn(filePath, url, jid, resultRequestUploadIqProtocolEntity.getIp(), caption) successFn = lambda filePath, jid, url: doSendFn(filePath, url, jid, resultRequestUploadIqProtocolEntity.getIp(), caption, onSuccess, onFailure)
mediaUploader = MediaUploader(jid, self.legacyName, filePath, ownNumber = self.stack.getLayerInterface(YowAuthenticationProtocolLayer).getUsername(full=False)
mediaUploader = MediaUploader(jid, ownNumber, filePath,
resultRequestUploadIqProtocolEntity.getUrl(), resultRequestUploadIqProtocolEntity.getUrl(),
resultRequestUploadIqProtocolEntity.getResumeOffset(), resultRequestUploadIqProtocolEntity.getResumeOffset(),
successFn, self.onUploadError, self.onUploadProgress, async=False) successFn, self.onUploadError, self.onUploadProgress, async=False)
@ -190,17 +196,21 @@ class YowsupApp(object):
#sys.stdout.flush() #sys.stdout.flush()
pass pass
def doSendImage(self, filePath, url, to, ip = None, caption = None): def doSendImage(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None):
entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to, caption = caption) entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to, caption = caption)
self.sendEntity(entity) self.sendEntity(entity)
#self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId()) #self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId())
if onSuccess is not None:
onSuccess(entity.getId())
return entity.getId() return entity.getId()
def doSendAudio(self, filePath, url, to, ip = None, caption = None): def doSendAudio(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None):
entity = AudioDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to) entity = AudioDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to)
self.sendEntity(entity) self.sendEntity(entity)
#self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId()) #self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId())
if onSuccess is not None:
onSuccess(entity.getId())
return entity.getId() return entity.getId()
@ -310,7 +320,6 @@ class YowsupApp(object):
- success: (func) - Callback; Takes three arguments: existing numbers, - success: (func) - Callback; Takes three arguments: existing numbers,
non-existing numbers, invalid numbers. non-existing numbers, invalid numbers.
""" """
# TODO: Implement callbacks
mode = GetSyncIqProtocolEntity.MODE_DELTA if delta else GetSyncIqProtocolEntity.MODE_FULL mode = GetSyncIqProtocolEntity.MODE_DELTA if delta else GetSyncIqProtocolEntity.MODE_FULL
context = GetSyncIqProtocolEntity.CONTEXT_INTERACTIVE if interactive else GetSyncIqProtocolEntity.CONTEXT_REGISTRATION context = GetSyncIqProtocolEntity.CONTEXT_INTERACTIVE if interactive else GetSyncIqProtocolEntity.CONTEXT_REGISTRATION
# International contacts must be preceded by a plus. Other numbers are # International contacts must be preceded by a plus. Other numbers are