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 """
def helper(*args2, **kwargs2):
func(*args, **kwargs)
self.then(helper)
return self.then(helper)
def __getattr__(self, method_name):
return getattr(Then(self), method_name)
@ -130,7 +130,7 @@ def call(func, *args, **kwargs):
def restfunc2(*arg2, **kwarg2):
apply_deferred = partial(normalfunc, *arg2, **kwarg2)
return call(apply_deferred, **dict(items[i + 1:]))
return c.then(restfunc2)
return v.then(restfunc2)
# No items deferred
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.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=""):
self.logger.info("Message sent from %s to %s: %s (xhtml=%s)",
self.legacyName, sender, message, xhtml)
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)
@ -616,24 +646,11 @@ class Session(YowsupApp):
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"
self.sendImage(message, ID, room + '@g.us')
elif "geo:" in message.lower():
self._sendLocation(room + "@g.us", message, ID)
else:
self.sendTextMessage(self._lengthenGroupId(room) + '@g.us', message)
self.sendTextMessage(room + '@g.us', message)
else: # private msg
buddy = sender
# if message == "\\lastseen":
@ -650,19 +667,7 @@ class Session(YowsupApp):
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"
self.sendImage(message, ID, buddy + "@s.whatsapp.net")
elif "geo:" in message.lower():
self._sendLocation(buddy + "@s.whatsapp.net", message, ID)
else:
@ -804,48 +809,6 @@ class Session(YowsupApp):
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

View file

@ -121,6 +121,9 @@ class WhatsAppBackend(SpectrumBackend):
self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, nickname)
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):
"""
Used to re-initialize the session object. Used when finished with
@ -137,10 +140,6 @@ class WhatsAppBackend(SpectrumBackend):
self.sessions[user].login(password)
# TODO
def handleBuddyBlockToggled(self, user, buddy, blocked):
pass
def handleAttentionRequest(self, user, buddy, message):
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_iq.protocolentities import *
from yowsup.layers.protocol_media.mediauploader import MediaUploader
from yowsup.layers.protocol_media.mediadownloader import MediaDownloader
# Registration
@ -136,6 +137,10 @@ class YowsupApp(object):
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
@ -153,26 +158,27 @@ class YowsupApp(object):
self.sendEntity(messageEntity)
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)
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)
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:
doSendFn = self._doSendAudio
doSendFn = self.doSendAudio
else:
doSendFn = self._doSendImage
doSendFn = self.doSendImage
if resultRequestUploadIqProtocolEntity.isDuplicate():
doSendFn(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid,
resultRequestUploadIqProtocolEntity.getIp(), caption)
else:
successFn = lambda filePath, jid, url: doSendFn(filePath, url, jid, resultRequestUploadIqProtocolEntity.getIp(), caption)
mediaUploader = MediaUploader(jid, self.legacyName, filePath,
successFn = lambda filePath, jid, url: doSendFn(filePath, url, 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)
@ -190,17 +196,21 @@ class YowsupApp(object):
#sys.stdout.flush()
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)
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):
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()
@ -310,7 +320,6 @@ class YowsupApp(object):
- success: (func) - Callback; Takes three arguments: existing numbers,
non-existing numbers, invalid numbers.
"""
# TODO: Implement callbacks
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