From 7942b170a89170e019a6104e46e22cb668167e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ricardo?= Date: Wed, 9 Sep 2015 22:10:28 -0300 Subject: [PATCH 01/18] YowParallelLayer added on demos contatcs, echoclient and sendclient --- yowsup/demos/contacts/stack.py | 7 +++---- yowsup/demos/echoclient/stack.py | 7 +++---- yowsup/demos/sendclient/stack.py | 8 ++++---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/yowsup/demos/contacts/stack.py b/yowsup/demos/contacts/stack.py index 2dbae86bb..0ecb29454 100644 --- a/yowsup/demos/contacts/stack.py +++ b/yowsup/demos/contacts/stack.py @@ -9,8 +9,7 @@ from yowsup.layers.protocol_acks import YowAckProtocolLayer from yowsup.layers.logger import YowLoggerLayer from yowsup.layers.protocol_contacts import YowContactsIqProtocolLayer -from yowsup.common import YowConstants -from yowsup import env +from yowsup.layers import YowParallelLayer class YowsupSyncStack(object): def __init__(self, credentials, contacts, encryptionEnabled = False): @@ -24,7 +23,7 @@ def __init__(self, credentials, contacts, encryptionEnabled = False): from yowsup.layers.axolotl import YowAxolotlLayer layers = ( SyncLayer, - (YowAuthenticationProtocolLayer, YowContactsIqProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer), + YowParallelLayer([YowAuthenticationProtocolLayer, YowContactsIqProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer]), YowAxolotlLayer, YowLoggerLayer, YowCoderLayer, @@ -35,7 +34,7 @@ def __init__(self, credentials, contacts, encryptionEnabled = False): else: layers = ( SyncLayer, - (YowAuthenticationProtocolLayer, YowContactsIqProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer), + YowParallelLayer([YowAuthenticationProtocolLayer, YowContactsIqProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer]), YowLoggerLayer, YowCoderLayer, YowCryptLayer, diff --git a/yowsup/demos/echoclient/stack.py b/yowsup/demos/echoclient/stack.py index d8448d291..af14f5d4d 100644 --- a/yowsup/demos/echoclient/stack.py +++ b/yowsup/demos/echoclient/stack.py @@ -12,8 +12,7 @@ from yowsup.layers.logger import YowLoggerLayer from yowsup.layers.protocol_iq import YowIqProtocolLayer from yowsup.layers.protocol_calls import YowCallsProtocolLayer -from yowsup.common import YowConstants -from yowsup import env +from yowsup.layers import YowParallelLayer class YowsupEchoStack(object): def __init__(self, credentials, encryptionEnabled = False): @@ -21,7 +20,7 @@ def __init__(self, credentials, encryptionEnabled = False): from yowsup.layers.axolotl import YowAxolotlLayer layers = ( EchoLayer, - (YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer, YowMediaProtocolLayer, YowIqProtocolLayer, YowCallsProtocolLayer), + YowParallelLayer([YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer, YowMediaProtocolLayer, YowIqProtocolLayer, YowCallsProtocolLayer]), YowAxolotlLayer, YowLoggerLayer, YowCoderLayer, @@ -32,7 +31,7 @@ def __init__(self, credentials, encryptionEnabled = False): else: layers = ( EchoLayer, - (YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer, YowMediaProtocolLayer, YowIqProtocolLayer, YowCallsProtocolLayer), + YowParallelLayer([YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer, YowMediaProtocolLayer, YowIqProtocolLayer, YowCallsProtocolLayer]), YowLoggerLayer, YowCoderLayer, YowCryptLayer, diff --git a/yowsup/demos/sendclient/stack.py b/yowsup/demos/sendclient/stack.py index 5d78aac90..8d63ee116 100644 --- a/yowsup/demos/sendclient/stack.py +++ b/yowsup/demos/sendclient/stack.py @@ -9,8 +9,8 @@ from yowsup.layers.protocol_receipts import YowReceiptProtocolLayer from yowsup.layers.protocol_acks import YowAckProtocolLayer from yowsup.layers.logger import YowLoggerLayer -from yowsup.common import YowConstants -from yowsup import env +from yowsup.layers import YowParallelLayer + class YowsupSendStack(object): def __init__(self, credentials, messages, encryptionEnabled = False): @@ -24,7 +24,7 @@ def __init__(self, credentials, messages, encryptionEnabled = False): from yowsup.layers.axolotl import YowAxolotlLayer layers = ( SendLayer, - (YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer), + YowParallelLayer([YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer]), YowAxolotlLayer, YowLoggerLayer, YowCoderLayer, @@ -35,7 +35,7 @@ def __init__(self, credentials, messages, encryptionEnabled = False): else: layers = ( SendLayer, - (YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer), + YowParallelLayer([YowAuthenticationProtocolLayer, YowMessagesProtocolLayer, YowReceiptProtocolLayer, YowAckProtocolLayer]), YowLoggerLayer, YowCoderLayer, YowCryptLayer, From e22a769aba041d4d37d9685b166d8eb5f1b753cf Mon Sep 17 00:00:00 2001 From: Jose Luis Guardiola Date: Wed, 30 Sep 2015 11:34:51 +0200 Subject: [PATCH 02/18] yowsup-cli sends read onMessage --- yowsup/demos/cli/layer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yowsup/demos/cli/layer.py b/yowsup/demos/cli/layer.py index 186eb2e38..3b2e1045b 100644 --- a/yowsup/demos/cli/layer.py +++ b/yowsup/demos/cli/layer.py @@ -47,6 +47,7 @@ def __init__(self): self.connected = False self.username = None self.sendReceipts = True + self.sendRead = True self.disconnectAction = self.__class__.DISCONNECT_ACTION_PROMPT self.credentials = None @@ -492,8 +493,8 @@ def onMessage(self, message): self.output(output, tag = None, prompt = not self.sendReceipts) if self.sendReceipts: - self.toLower(message.ack()) - self.output("Sent delivered receipt", tag = "Message %s" % message.getId()) + self.toLower(message.ack(self.sendRead)) + self.output("Sent delivered receipt"+" and Read" if self.sendRead else "", tag = "Message %s" % message.getId()) def getTextMessageBody(self, message): From 7f917562766a4bd98671163d8977a1917d6ae958 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Makhija Date: Sun, 18 Oct 2015 02:26:15 +0530 Subject: [PATCH 03/18] Image preview fix when calling from yowsup-cli Fix for ImageDownloadableMediaMessageProtocolEntity.fromFilePath preview functionality --- yowsup/common/tools.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/yowsup/common/tools.py b/yowsup/common/tools.py index d414de260..be0f24319 100644 --- a/yowsup/common/tools.py +++ b/yowsup/common/tools.py @@ -148,10 +148,11 @@ def getImageDimensions(imageFile): @staticmethod def generatePreviewFromImage(image): fd, path = tempfile.mkstemp() - fileObj = os.fdopen(fd, "rb+") + preview = None - if ImageTools.scaleImage(image, fileObj, "JPEG", YowConstants.PREVIEW_WIDTH, YowConstants.PREVIEW_HEIGHT): + if ImageTools.scaleImage(image, path, "JPEG", YowConstants.PREVIEW_WIDTH, YowConstants.PREVIEW_HEIGHT): + fileObj = os.fdopen(fd, "rb+") fileObj.seek(0) preview = fileObj.read() - fileObj.close() - return preview \ No newline at end of file + fileObj.close() + return preview From 311756f628a290e88c060dc5d7c9c18ea3488aca Mon Sep 17 00:00:00 2001 From: Jose Luis Guardiola Date: Mon, 19 Oct 2015 13:56:21 +0200 Subject: [PATCH 04/18] profile privacy options --- yowsup/demos/cli/layer.py | 29 ++++++++ yowsup/layers/protocol_profiles/layer.py | 8 +++ .../protocolentities/__init__.py | 3 + .../protocolentities/iq_privacy_get.py | 27 ++++++++ .../protocolentities/iq_privacy_set.py | 69 +++++++++++++++++++ .../protocolentities/iq_result_privacy_get.py | 50 ++++++++++++++ .../protocolentities/test_iq_privacy_get.py | 11 +++ .../protocolentities/test_iq_privacy_set.py | 11 +++ .../test_iq_result_privacy_get.py | 11 +++ 9 files changed, 219 insertions(+) create mode 100644 yowsup/layers/protocol_profiles/protocolentities/iq_privacy_get.py create mode 100644 yowsup/layers/protocol_profiles/protocolentities/iq_privacy_set.py create mode 100644 yowsup/layers/protocol_profiles/protocolentities/iq_result_privacy_get.py create mode 100644 yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_get.py create mode 100644 yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_set.py create mode 100644 yowsup/layers/protocol_profiles/protocolentities/test_iq_result_privacy_get.py diff --git a/yowsup/demos/cli/layer.py b/yowsup/demos/cli/layer.py index 186eb2e38..c81248f7a 100644 --- a/yowsup/demos/cli/layer.py +++ b/yowsup/demos/cli/layer.py @@ -223,6 +223,35 @@ def onError(errorIqEntity, originalIqEntity): else: logger.error("Python PIL library is not installed, can't set profile picture") + @clicmd("Get profile privacy") + def profile_getPrivacy(self): + if self.assertConnected(): + def onSuccess(resultIqEntity, originalIqEntity): + self.output("Profile privacy is: %s" %(resultIqEntity)) + + def onError(errorIqEntity, originalIqEntity): + logger.error("Error getting profile privacy") + + iq = GetPrivacyIqProtocolEntity() + self._sendIq(iq, onSuccess, onError) + + @clicmd("Set profile privacy. Supported values are 'all', 'contacts' or 'none' ('all' by default) and names is a comma separated list of 'profile', 'status' or 'last' ('all' by default)") + def profile_setPrivacy(self, value="all", names=None): + if self.assertConnected(): + def onSuccess(resultIqEntity, originalIqEntity): + self.output("Profile privacy set to: %s" %(resultIqEntity)) + + def onError(errorIqEntity, originalIqEntity): + logger.error("Error setting profile privacy") + try: + names = [name for name in names.split(',')] if names else None + iq = SetPrivacyIqProtocolEntity(value, names) + self._sendIq(iq, onSuccess, onError) + except Exception as inst: + self.output(inst.message) + return self.print_usage() + + ########### groups @clicmd("List all groups you belong to", 5) diff --git a/yowsup/layers/protocol_profiles/layer.py b/yowsup/layers/protocol_profiles/layer.py index d86d5414a..f41b3e181 100644 --- a/yowsup/layers/protocol_profiles/layer.py +++ b/yowsup/layers/protocol_profiles/layer.py @@ -21,10 +21,18 @@ def sendIq(self, entity): self._sendIq(entity, self.onDeletePictureResult, self.onDeletePictureError) elif entity.getXmlns() == "status": self._sendIq(entity, self.onSetStatusResult, self.onSetStatusError) + elif entity.getXmlns() == "privacy": + self._sendIq(entity, self.onSetPrivacyResult, self.onSetPrivacyError) def recvIq(self, node): pass + def onSetPrivacyResult(self, resultNode, originIqRequestEntity): + self.toUpper(ResultPrivacyIqProtocolEntity.fromProtocolTreeNode(resultNode)) + + def onSetPrivacyError(self, errorNode, originalIqRequestEntity): + self.toUpper(ErrorIqProtocolEntity.fromProtocolTreeNode(errorNode)) + def onSetStatusResult(self, resultNode, originIqRequestEntity): self.toUpper(ResultIqProtocolEntity.fromProtocolTreeNode(resultNode)) diff --git a/yowsup/layers/protocol_profiles/protocolentities/__init__.py b/yowsup/layers/protocol_profiles/protocolentities/__init__.py index 9d29af033..7e3d1ef29 100644 --- a/yowsup/layers/protocol_profiles/protocolentities/__init__.py +++ b/yowsup/layers/protocol_profiles/protocolentities/__init__.py @@ -4,3 +4,6 @@ from .iq_picture_get_result import ResultGetPictureIqProtocolEntity from .iq_pictures_list import ListPicturesIqProtocolEntity from .iq_picture_set import SetPictureIqProtocolEntity +from .iq_privacy_set import SetPrivacyIqProtocolEntity +from .iq_privacy_get import GetPrivacyIqProtocolEntity +from .iq_result_privacy_get import ResultPrivacyIqProtocolEntity \ No newline at end of file diff --git a/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_get.py b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_get.py new file mode 100644 index 000000000..a4f4ea17d --- /dev/null +++ b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_get.py @@ -0,0 +1,27 @@ +from yowsup.layers.protocol_iq.protocolentities import IqProtocolEntity +from yowsup.structs import ProtocolTreeNode + +''' + + + + +''' + +class GetPrivacyIqProtocolEntity(IqProtocolEntity): + XMLNS = "privacy" + def __init__(self): + super(GetPrivacyIqProtocolEntity, self).__init__(self.__class__.XMLNS, _type="get") + + def toProtocolTreeNode(self): + node = super(GetPrivacyIqProtocolEntity, self).toProtocolTreeNode() + queryNode = ProtocolTreeNode(self.__class__.XMLNS) + node.addChild(queryNode) + return node + + @staticmethod + def fromProtocolTreeNode(node): + assert node.getChild(GetPrivacyIqProtocolEntity.XMLNS) is not None, "Not a get privacy iq node %s" % node + entity = IqProtocolEntity.fromProtocolTreeNode(node) + entity.__class__ = GetPrivacyIqProtocolEntity + return entity diff --git a/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_set.py b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_set.py new file mode 100644 index 000000000..a3574644d --- /dev/null +++ b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_set.py @@ -0,0 +1,69 @@ +from yowsup.layers.protocol_iq.protocolentities import IqProtocolEntity +from yowsup.structs import ProtocolTreeNode + +''' + + + + + + + + +''' + +class SetPrivacyIqProtocolEntity(IqProtocolEntity): + NAMES = ["status", "profile", "last"] + VALUES = ["all", "contacts", "none"] + XMLNS = "privacy" + + def __init__(self, value="all", names = None): + # names can be a string with some element in VALUES or an array with strings with elements in VALUES + # by default, all names are used + super(SetPrivacyIqProtocolEntity, self).__init__(self.__class__.XMLNS, _type="set") + self.setNames(names) + self.setValue(value) + + @staticmethod + def checkValidNames(names): + names = names if names else SetPrivacyIqProtocolEntity.NAMES + if not type(names) is list: + names = [names] + + for name in names: + if not name in SetPrivacyIqProtocolEntity.NAMES: + raise Exception("Name should be in: '" + "', '".join(SetPrivacyIqProtocolEntity.NAMES) + "' but is '" + name + "'") + return names + + @staticmethod + def checkValidValue(value): + if not value in SetPrivacyIqProtocolEntity.VALUES: + raise Exception("Value should be in: '" + "', '".join(SetPrivacyIqProtocolEntity.VALUES) + "' but is '" + value + "'") + return value + + def setNames(self, names): + self.names = SetPrivacyIqProtocolEntity.checkValidNames(names) + + def setValue(self, value): + self.value = SetPrivacyIqProtocolEntity.checkValidValue(value) + + def toProtocolTreeNode(self): + node = super(SetPrivacyIqProtocolEntity, self).toProtocolTreeNode() + queryNode = ProtocolTreeNode(self.__class__.XMLNS) + for name in self.names: + listNode = ProtocolTreeNode("category", {"name": name, "value": self.value}) + queryNode.addChild(listNode) + node.addChild(queryNode) + return node + + @staticmethod + def fromProtocolTreeNode(node): + entity = IqProtocolEntity.fromProtocolTreeNode(node) + entity.__class__ = SetPrivacyIqProtocolEntity + privacyNode = node.getChild(SetPrivacyIqProtocolEntity.XMLNS) + names = [] + for categoryNode in privacyNode.getAllChildren(): + names.append(categoryNode["name"]) + entity.setNames(names) + entity.setValue("all") + return entity diff --git a/yowsup/layers/protocol_profiles/protocolentities/iq_result_privacy_get.py b/yowsup/layers/protocol_profiles/protocolentities/iq_result_privacy_get.py new file mode 100644 index 000000000..88d6337e9 --- /dev/null +++ b/yowsup/layers/protocol_profiles/protocolentities/iq_result_privacy_get.py @@ -0,0 +1,50 @@ +from yowsup.structs import ProtocolTreeNode +from yowsup.layers.protocol_iq.protocolentities import ResultIqProtocolEntity + +''' + + + + + + + + + + +''' + +class ResultPrivacyIqProtocolEntity(ResultIqProtocolEntity): + XMLNS="privacy" + + def __init__(self, privacy): + super(ResultPrivacyIqProtocolEntity, self).__init__() + self.setProps(privacy) + + def setProps(self, privacy): + assert type(privacy) is dict, "Privacy must be a dict {name => value}" + self.privacy = privacy + + def __str__(self): + out = super(ResultPrivacyIqProtocolEntity, self).__str__() + out += "Privacy settings\n" + for name, value in self.privacy.iteritems(): + out += "Category %s --> %s\n" % (name, value) + return out + + def toProtocolTreeNode(self): + node = super(ResultPrivacyIqProtocolEntity, self).toProtocolTreeNode() + queryNode = ProtocolTreeNode(self.__class__.XMLNS) + node.addChild(queryNode) + return node + + @staticmethod + def fromProtocolTreeNode(node): + entity = super(ResultPrivacyIqProtocolEntity, ResultPrivacyIqProtocolEntity).fromProtocolTreeNode(node) + entity.__class__ = ResultPrivacyIqProtocolEntity + privacyNode = node.getChild(ResultPrivacyIqProtocolEntity.XMLNS) + privacy = {} + for categoryNode in privacyNode.getAllChildren(): + privacy[categoryNode["name"]] = categoryNode["value"] + entity.setProps(privacy) + return entity diff --git a/yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_get.py b/yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_get.py new file mode 100644 index 000000000..073ee441a --- /dev/null +++ b/yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_get.py @@ -0,0 +1,11 @@ +from yowsup.layers.protocol_iq.protocolentities.test_iq import IqProtocolEntityTest +from yowsup.layers.protocol_profiles.protocolentities import GetPrivacyIqProtocolEntity +from yowsup.structs import ProtocolTreeNode + +entity = GetPrivacyIqProtocolEntity() + +class GetPrivacyIqProtocolEntityTest(IqProtocolEntityTest): + def setUp(self): + super(GetPrivacyIqProtocolEntityTest, self).setUp() + self.ProtocolEntity = GetPrivacyIqProtocolEntity + self.node = entity.toProtocolTreeNode() diff --git a/yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_set.py b/yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_set.py new file mode 100644 index 000000000..b1d0dda6e --- /dev/null +++ b/yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_set.py @@ -0,0 +1,11 @@ +from yowsup.layers.protocol_iq.protocolentities.test_iq import IqProtocolEntityTest +from yowsup.layers.protocol_profiles.protocolentities import SetPrivacyIqProtocolEntity +from yowsup.structs import ProtocolTreeNode + +entity = SetPrivacyIqProtocolEntity("all", ["profile","last","status"]) + +class SetPrivacyIqProtocolEntityTest(IqProtocolEntityTest): + def setUp(self): + super(SetPrivacyIqProtocolEntityTest, self).setUp() + self.ProtocolEntity = SetPrivacyIqProtocolEntity + self.node = entity.toProtocolTreeNode() diff --git a/yowsup/layers/protocol_profiles/protocolentities/test_iq_result_privacy_get.py b/yowsup/layers/protocol_profiles/protocolentities/test_iq_result_privacy_get.py new file mode 100644 index 000000000..5da45b054 --- /dev/null +++ b/yowsup/layers/protocol_profiles/protocolentities/test_iq_result_privacy_get.py @@ -0,0 +1,11 @@ +from yowsup.layers.protocol_iq.protocolentities.test_iq import IqProtocolEntityTest +from yowsup.layers.protocol_profiles.protocolentities import ResultPrivacyIqProtocolEntity +from yowsup.structs import ProtocolTreeNode + +entity = ResultPrivacyIqProtocolEntity({"profile":"all","last":"none","status":"contacts"}) + +class ResultPrivacyIqProtocolEntityTest(IqProtocolEntityTest): + def setUp(self): + super(ResultPrivacyIqProtocolEntityTest, self).setUp() + self.ProtocolEntity = ResultPrivacyIqProtocolEntity + self.node = entity.toProtocolTreeNode() From 925389474579f654e9b18bdc06f500c2e4ab4c6c Mon Sep 17 00:00:00 2001 From: Jose Luis Guardiola Date: Mon, 19 Oct 2015 14:05:39 +0200 Subject: [PATCH 05/18] Fixed incorrect indentation due to tab --- .../layers/protocol_profiles/protocolentities/iq_privacy_set.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_set.py b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_set.py index a3574644d..597989fda 100644 --- a/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_set.py +++ b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_set.py @@ -26,7 +26,7 @@ def __init__(self, value="all", names = None): @staticmethod def checkValidNames(names): - names = names if names else SetPrivacyIqProtocolEntity.NAMES + names = names if names else SetPrivacyIqProtocolEntity.NAMES if not type(names) is list: names = [names] From a28e173a34430ee6642cfbf24be3d4099fea5b50 Mon Sep 17 00:00:00 2001 From: Jose Luis Guardiola Date: Mon, 19 Oct 2015 14:07:58 +0200 Subject: [PATCH 06/18] Fixed incorrect indentation due to tab --- .../layers/protocol_profiles/protocolentities/iq_privacy_get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_get.py b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_get.py index a4f4ea17d..ed6446101 100644 --- a/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_get.py +++ b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_get.py @@ -21,7 +21,7 @@ def toProtocolTreeNode(self): @staticmethod def fromProtocolTreeNode(node): - assert node.getChild(GetPrivacyIqProtocolEntity.XMLNS) is not None, "Not a get privacy iq node %s" % node + assert node.getChild(GetPrivacyIqProtocolEntity.XMLNS) is not None, "Not a get privacy iq node %s" % node entity = IqProtocolEntity.fromProtocolTreeNode(node) entity.__class__ = GetPrivacyIqProtocolEntity return entity From 0544f2702068486744bb9f958a9dbee47cf44939 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Makhija Date: Sun, 25 Oct 2015 03:51:33 +0530 Subject: [PATCH 07/18] Fixing multiprocessing issue. --- yowsup/structs/protocoltreenode.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/yowsup/structs/protocoltreenode.py b/yowsup/structs/protocoltreenode.py index c63446d43..f6f1ce9da 100644 --- a/yowsup/structs/protocoltreenode.py +++ b/yowsup/structs/protocoltreenode.py @@ -60,8 +60,13 @@ def toString(self): except UnicodeDecodeError: out += binascii.hexlify(self.data) else: - out += "%s" % self.data - + try: + out += "%s" % self.data + except UnicodeDecodeError: + try: + out += "%s" % self.data.decode() + except UnicodeDecodeError: + out += binascii.hexlify(self.data) if type(self.data) is str and sys.version_info >= (3,0): out += "\nHEX3:%s\n" % binascii.hexlify(self.data.encode('latin-1')) From 2570b9da62a70210bf76ebc0706d23b47083d8fb Mon Sep 17 00:00:00 2001 From: Tarek Galal Date: Sun, 25 Oct 2015 06:44:56 +0100 Subject: [PATCH 08/18] Fix image scaling in Palette mode Closes #1078, thanks to @janboll and @Salmon-Bard --- yowsup/common/tools.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/yowsup/common/tools.py b/yowsup/common/tools.py index d414de260..0903ce612 100644 --- a/yowsup/common/tools.py +++ b/yowsup/common/tools.py @@ -85,13 +85,13 @@ class TimeTools: def parseIso(iso): d=datetime.datetime(*map(int, re.split('[^\d]', iso)[:-1])) return d - - @staticmethod + + @staticmethod def utcToLocal(dt): utc = tz.gettz('UTC') local = tz.tzlocal() dtUtc = dt.replace(tzinfo=utc) - + return dtUtc.astimezone(local) @staticmethod @@ -128,6 +128,9 @@ def scaleImage(infile, outfile, imageFormat, width, height): if ModuleTools.INSTALLED_PIL(): from PIL import Image im = Image.open(infile) + #Convert P mode images + if im.mode != "RGB": + im = im.convert("RGB") im.thumbnail((width, height)) im.save(outfile, imageFormat) return True @@ -154,4 +157,4 @@ def generatePreviewFromImage(image): fileObj.seek(0) preview = fileObj.read() fileObj.close() - return preview \ No newline at end of file + return preview From 2fbf88fe5d975e4a8b5f43f71338349da44b0791 Mon Sep 17 00:00:00 2001 From: Tarek Galal Date: Sun, 25 Oct 2015 08:18:06 +0100 Subject: [PATCH 09/18] More compact decription for /profile setPrivacy --- yowsup/demos/cli/layer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/yowsup/demos/cli/layer.py b/yowsup/demos/cli/layer.py index a20565b0c..c6497472d 100644 --- a/yowsup/demos/cli/layer.py +++ b/yowsup/demos/cli/layer.py @@ -236,7 +236,7 @@ def onError(errorIqEntity, originalIqEntity): iq = GetPrivacyIqProtocolEntity() self._sendIq(iq, onSuccess, onError) - @clicmd("Set profile privacy. Supported values are 'all', 'contacts' or 'none' ('all' by default) and names is a comma separated list of 'profile', 'status' or 'last' ('all' by default)") + @clicmd("Profile privacy. value=all|contacts|none names=profile|status|last. Names are comma separated, defaults to all.") def profile_setPrivacy(self, value="all", names=None): if self.assertConnected(): def onSuccess(resultIqEntity, originalIqEntity): @@ -534,7 +534,7 @@ def getMediaMessageBody(self, message): return self.getDownloadableMediaMessageBody(message) else: return "[Media Type: %s]" % message.getMediaType() - + def getDownloadableMediaMessageBody(self, message): return "[Media Type:{media_type}, Size:{media_size}, URL:{media_url}]".format( @@ -600,4 +600,3 @@ def __str__(self): @clicmd("Print this message") def help(self): self.print_usage() - From d89e5deb0665a98d0d668dfddecb71253ddbd601 Mon Sep 17 00:00:00 2001 From: Tarek Galal Date: Sun, 25 Oct 2015 08:23:59 +0100 Subject: [PATCH 10/18] Few adjustments Fixed error in python3 due to use of dict iteritems Renamed to iq_privacy_result as it's handling set and get results Renamed class var XMLNS to NODE_PRIVACY --- yowsup/layers/protocol_profiles/layer.py | 7 +++---- .../layers/protocol_profiles/protocolentities/__init__.py | 2 +- .../{iq_result_privacy_get.py => iq_privacy_result.py} | 8 ++++---- ...iq_result_privacy_get.py => test_iq_privacy_result.py} | 0 4 files changed, 8 insertions(+), 9 deletions(-) rename yowsup/layers/protocol_profiles/protocolentities/{iq_result_privacy_get.py => iq_privacy_result.py} (90%) rename yowsup/layers/protocol_profiles/protocolentities/{test_iq_result_privacy_get.py => test_iq_privacy_result.py} (100%) diff --git a/yowsup/layers/protocol_profiles/layer.py b/yowsup/layers/protocol_profiles/layer.py index f41b3e181..00aad53c5 100644 --- a/yowsup/layers/protocol_profiles/layer.py +++ b/yowsup/layers/protocol_profiles/layer.py @@ -22,15 +22,15 @@ def sendIq(self, entity): elif entity.getXmlns() == "status": self._sendIq(entity, self.onSetStatusResult, self.onSetStatusError) elif entity.getXmlns() == "privacy": - self._sendIq(entity, self.onSetPrivacyResult, self.onSetPrivacyError) + self._sendIq(entity, self.onPrivacyResult, self.onPrivacyError) def recvIq(self, node): pass - def onSetPrivacyResult(self, resultNode, originIqRequestEntity): + def onPrivacyResult(self, resultNode, originIqRequestEntity): self.toUpper(ResultPrivacyIqProtocolEntity.fromProtocolTreeNode(resultNode)) - def onSetPrivacyError(self, errorNode, originalIqRequestEntity): + def onPrivacyError(self, errorNode, originalIqRequestEntity): self.toUpper(ErrorIqProtocolEntity.fromProtocolTreeNode(errorNode)) def onSetStatusResult(self, resultNode, originIqRequestEntity): @@ -56,4 +56,3 @@ def onDeletePictureResult(self, resultNode, originalIqRequestEntity): def onDeletePictureError(self, errorNode, originalIqRequestEntity): self.toUpper(ErrorIqProtocolEntity.fromProtocolTreeNode(errorNode)) - diff --git a/yowsup/layers/protocol_profiles/protocolentities/__init__.py b/yowsup/layers/protocol_profiles/protocolentities/__init__.py index 7e3d1ef29..427ef6b6c 100644 --- a/yowsup/layers/protocol_profiles/protocolentities/__init__.py +++ b/yowsup/layers/protocol_profiles/protocolentities/__init__.py @@ -6,4 +6,4 @@ from .iq_picture_set import SetPictureIqProtocolEntity from .iq_privacy_set import SetPrivacyIqProtocolEntity from .iq_privacy_get import GetPrivacyIqProtocolEntity -from .iq_result_privacy_get import ResultPrivacyIqProtocolEntity \ No newline at end of file +from .iq_privacy_result import ResultPrivacyIqProtocolEntity diff --git a/yowsup/layers/protocol_profiles/protocolentities/iq_result_privacy_get.py b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_result.py similarity index 90% rename from yowsup/layers/protocol_profiles/protocolentities/iq_result_privacy_get.py rename to yowsup/layers/protocol_profiles/protocolentities/iq_privacy_result.py index 88d6337e9..1b2a86008 100644 --- a/yowsup/layers/protocol_profiles/protocolentities/iq_result_privacy_get.py +++ b/yowsup/layers/protocol_profiles/protocolentities/iq_privacy_result.py @@ -15,7 +15,7 @@ ''' class ResultPrivacyIqProtocolEntity(ResultIqProtocolEntity): - XMLNS="privacy" + NODE_PRIVACY="privacy" def __init__(self, privacy): super(ResultPrivacyIqProtocolEntity, self).__init__() @@ -28,13 +28,13 @@ def setProps(self, privacy): def __str__(self): out = super(ResultPrivacyIqProtocolEntity, self).__str__() out += "Privacy settings\n" - for name, value in self.privacy.iteritems(): + for name, value in self.privacy.items(): out += "Category %s --> %s\n" % (name, value) return out def toProtocolTreeNode(self): node = super(ResultPrivacyIqProtocolEntity, self).toProtocolTreeNode() - queryNode = ProtocolTreeNode(self.__class__.XMLNS) + queryNode = ProtocolTreeNode(self.__class__.NODE_PRIVACY) node.addChild(queryNode) return node @@ -42,7 +42,7 @@ def toProtocolTreeNode(self): def fromProtocolTreeNode(node): entity = super(ResultPrivacyIqProtocolEntity, ResultPrivacyIqProtocolEntity).fromProtocolTreeNode(node) entity.__class__ = ResultPrivacyIqProtocolEntity - privacyNode = node.getChild(ResultPrivacyIqProtocolEntity.XMLNS) + privacyNode = node.getChild(ResultPrivacyIqProtocolEntity.NODE_PRIVACY) privacy = {} for categoryNode in privacyNode.getAllChildren(): privacy[categoryNode["name"]] = categoryNode["value"] diff --git a/yowsup/layers/protocol_profiles/protocolentities/test_iq_result_privacy_get.py b/yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_result.py similarity index 100% rename from yowsup/layers/protocol_profiles/protocolentities/test_iq_result_privacy_get.py rename to yowsup/layers/protocol_profiles/protocolentities/test_iq_privacy_result.py From 4f5c2320a21be0cfef9d69d1f0e4ca6bb45234cc Mon Sep 17 00:00:00 2001 From: Tarek Galal Date: Sun, 25 Oct 2015 08:48:05 +0100 Subject: [PATCH 11/18] Fixed sid value in sync node, closes #889 Thanks to @Wilm0r --- .../protocol_contacts/protocolentities/iq_sync.py | 10 +++++----- .../protocol_contacts/protocolentities/iq_sync_get.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/yowsup/layers/protocol_contacts/protocolentities/iq_sync.py b/yowsup/layers/protocol_contacts/protocolentities/iq_sync.py index 1678962e3..174c6f8f4 100644 --- a/yowsup/layers/protocol_contacts/protocolentities/iq_sync.py +++ b/yowsup/layers/protocol_contacts/protocolentities/iq_sync.py @@ -7,7 +7,7 @@ class SyncIqProtocolEntity(IqProtocolEntity): ''' @@ -20,7 +20,7 @@ def __init__(self, _type, _id = None, sid = None, index = 0, last = True): self.setSyncProps(sid, index, last) def setSyncProps(self, sid, index, last): - self.sid = sid if sid else str((time.time() + 11644477200) * 10000000) + self.sid = sid if sid else str((int(time.time()) + 11644477200) * 10000000) self.index = int(index) self.last = last @@ -33,7 +33,7 @@ def __str__(self): return out def toProtocolTreeNode(self): - + syncNodeAttrs = { "sid": self.sid, "index": str(self.index), @@ -58,6 +58,6 @@ def fromProtocolTreeNode(node): syncNode.getAttributeValue("index"), syncNode.getAttributeValue("last") ) - - return entity + + return entity diff --git a/yowsup/layers/protocol_contacts/protocolentities/iq_sync_get.py b/yowsup/layers/protocol_contacts/protocolentities/iq_sync_get.py index bbdb3914c..95c18128e 100644 --- a/yowsup/layers/protocol_contacts/protocolentities/iq_sync_get.py +++ b/yowsup/layers/protocol_contacts/protocolentities/iq_sync_get.py @@ -17,7 +17,7 @@ class GetSyncIqProtocolEntity(SyncIqProtocolEntity): @@ -53,7 +53,7 @@ def __str__(self): return out def toProtocolTreeNode(self): - + users = [ProtocolTreeNode("user", {}, None, number) for number in self.numbers] node = super(GetSyncIqProtocolEntity, self).toProtocolTreeNode() @@ -76,5 +76,5 @@ def fromProtocolTreeNode(node): syncNode.getAttributeValue("mode"), syncNode.getAttributeValue("context"), ) - + return entity From ee039a2544ebf3b1ca6dd8271cc4a8ba3e6f9837 Mon Sep 17 00:00:00 2001 From: Holi0317 Date: Tue, 17 Nov 2015 22:29:09 +0800 Subject: [PATCH 12/18] Update android data --- yowsup/env/env_android.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/yowsup/env/env_android.py b/yowsup/env/env_android.py index 2dfd424e6..cc859773a 100644 --- a/yowsup/env/env_android.py +++ b/yowsup/env/env_android.py @@ -1,25 +1,27 @@ from .env import YowsupEnv import base64 import hashlib + + class AndroidYowsupEnv(YowsupEnv): _SIGNATURE = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNV" \ - "BAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJ" \ - "pYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5" \ - "pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEg" \ - "YDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEm" \ - "aUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCN" \ - "VQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jr" \ - "qgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO" \ - "8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaS" \ - "HBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRW" \ - "YHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXd" \ - "KtOrNTQcc0e+t" - - _MD5_CLASSES = "MYFrqqEFgD/DG5Z9E+zaSA==" + "BAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJ" \ + "pYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5" \ + "pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEg" \ + "YDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEm" \ + "aUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCN" \ + "VQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jr" \ + "qgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO" \ + "8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaS" \ + "HBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRW" \ + "YHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXd" \ + "KtOrNTQcc0e+t" + + _MD5_CLASSES = "vIDGGxIcIxeBP0GoG8yL8g==" _KEY = "/UIGKU1FVQa+ATM2A0za7G2KI9S/CwPYjgAbc67v7ep42eO/WeTLx1lb1cHwxpsEgF4+PmYpLd2YpGUdX/A2JQitsHzDwgcdBpUf7psX1BU=" - _VERSION = "2.12.287" - _OS_NAME= "Android" + _VERSION = "2.12.357" + _OS_NAME = "Android" _OS_VERSION = "4.3" _DEVICE_NAME = "GalaxyS3" _AXOLOTL = True From 80915ab904020677c87e610fec84367b7782f163 Mon Sep 17 00:00:00 2001 From: Menachem Shapira Date: Wed, 2 Dec 2015 11:37:29 +0200 Subject: [PATCH 13/18] check fields in video message before parsing --- .../protocolentities/message_media_downloadable_video.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/yowsup/layers/protocol_media/protocolentities/message_media_downloadable_video.py b/yowsup/layers/protocol_media/protocolentities/message_media_downloadable_video.py index ef1ee74fe..71d847d1e 100644 --- a/yowsup/layers/protocol_media/protocolentities/message_media_downloadable_video.py +++ b/yowsup/layers/protocol_media/protocolentities/message_media_downloadable_video.py @@ -80,8 +80,10 @@ def toProtocolTreeNode(self): mediaNode.setAttribute("encoding", self.encoding) mediaNode.setAttribute("fps", self.fps) mediaNode.setAttribute("height", self.height) - mediaNode.setAttribute("seconds", self.seconds) - mediaNode.setAttribute("vbitrate", self.vbitrate) + if self.seconds is not None: + mediaNode.setAttribute("seconds", self.seconds) + if self.vbitrate is not None: + mediaNode.setAttribute("vbitrate", self.vbitrate) mediaNode.setAttribute("vcodec", self.vcodec) mediaNode.setAttribute("width", self.width) if self.caption is not None: From 599c08ca974075e69d4db0c2fa631ac67f7613c5 Mon Sep 17 00:00:00 2001 From: moyamo Date: Fri, 11 Dec 2015 23:10:12 +0200 Subject: [PATCH 14/18] Add iq_statuses_get iq_status_get is needed to initially get the status of contacts. --- yowsup/demos/cli/layer.py | 6 +++ yowsup/layers/protocol_contacts/layer.py | 4 ++ .../protocolentities/__init__.py | 2 + .../protocolentities/iq_statuses_get.py | 46 ++++++++++++++++ .../protocolentities/iq_statuses_result.py | 52 +++++++++++++++++++ 5 files changed, 110 insertions(+) create mode 100644 yowsup/layers/protocol_contacts/protocolentities/iq_statuses_get.py create mode 100644 yowsup/layers/protocol_contacts/protocolentities/iq_statuses_result.py diff --git a/yowsup/demos/cli/layer.py b/yowsup/demos/cli/layer.py index c6497472d..6e70f5228 100644 --- a/yowsup/demos/cli/layer.py +++ b/yowsup/demos/cli/layer.py @@ -425,6 +425,12 @@ def state_typing(self, jid): entity = OutgoingChatstateProtocolEntity(ChatstateProtocolEntity.STATE_TYPING, self.aliasToJid(jid)) self.toLower(entity) + @clicmd("Request contacts statuses") + def statuses_get(self, contacts): + if self.assertConnected(): + entity = GetStatusesIqProtocolEntity([self.aliasToJid(c) for c in contacts.split(',')]) + self.toLower(entity) + @clicmd("Send paused state") def state_paused(self, jid): if self.assertConnected(): diff --git a/yowsup/layers/protocol_contacts/layer.py b/yowsup/layers/protocol_contacts/layer.py index 3c566ba76..4685597f4 100644 --- a/yowsup/layers/protocol_contacts/layer.py +++ b/yowsup/layers/protocol_contacts/layer.py @@ -28,7 +28,11 @@ def recvNotification(self, node): def recvIq(self, node): if node["type"] == "result" and node.getChild("sync"): self.toUpper(ResultSyncIqProtocolEntity.fromProtocolTreeNode(node)) + elif node["type"] == "result" and node.getChild("status"): + self.toUpper(ResultStatusesIqProtocolEntity.fromProtocolTreeNode(node)) def sendIq(self, entity): if entity.getXmlns() == "urn:xmpp:whatsapp:sync": self.toLower(entity.toProtocolTreeNode()) + elif entity.getXmlns() == GetStatusesIqProtocolEntity.XMLNS: + self.toLower(entity.toProtocolTreeNode()) diff --git a/yowsup/layers/protocol_contacts/protocolentities/__init__.py b/yowsup/layers/protocol_contacts/protocolentities/__init__.py index b9e5d352e..ca6f461e9 100644 --- a/yowsup/layers/protocol_contacts/protocolentities/__init__.py +++ b/yowsup/layers/protocol_contacts/protocolentities/__init__.py @@ -5,3 +5,5 @@ from .notification_contact_remove import RemoveContactNotificationProtocolEntity from .notification_contact_update import UpdateContactNotificationProtocolEntity from .notificiation_contacts_sync import ContactsSyncNotificationProtocolEntity +from .iq_statuses_get import GetStatusesIqProtocolEntity +from .iq_statuses_result import ResultStatusesIqProtocolEntity diff --git a/yowsup/layers/protocol_contacts/protocolentities/iq_statuses_get.py b/yowsup/layers/protocol_contacts/protocolentities/iq_statuses_get.py new file mode 100644 index 000000000..34bc8f5df --- /dev/null +++ b/yowsup/layers/protocol_contacts/protocolentities/iq_statuses_get.py @@ -0,0 +1,46 @@ +from yowsup.layers.protocol_iq.protocolentities import IqProtocolEntity +from yowsup.structs import ProtocolTreeNode + +class GetStatusesIqProtocolEntity(IqProtocolEntity): + XMLNS = "status" + + def __init__(self, jids, _id = None): + """ + Request the statuses of users. Should be sent once after login. + + Args: + - jids: A list of jids representing the users whose statuses you are + trying to get. + """ + super(GetStatusesIqProtocolEntity, self).__init__(self.__class__.XMLNS, _id, _type = "get", to = "s.whatsapp.net") + self.setGetStatusesProps(jids) + + def setGetStatusesProps(self, jids): + assert type(jids) is list, "jids must be a list of jids" + self.jids = jids + + def __str__(self): + out = super(GetStatusesIqProtocolEntity, self).__str__() + out += "Numbers: %s\n" % (",".join(self.numbers)) + return out + + def toProtocolTreeNode(self): + users = [ProtocolTreeNode("user", {'jid': jid}) for jid in self.jids] + + node = super(GetStatusesIqProtocolEntity, self).toProtocolTreeNode() + statusNode = ProtocolTreeNode("status", None, users) + node.addChild(statusNode) + + return node + + @staticmethod + def fromProtocolTreeNode(node): + entity = IqProtocolEntity.fromProtocolTreeNode(node) + entity.__class__ = GetStatusesIqProtocolEntity + statusNode = node.getChild("status") + userNodes = statusNode.getAllChildren() + jids = [user['jid'] for user in userNodes] + + entity.setGetStatusesProps(jids) + + return entity diff --git a/yowsup/layers/protocol_contacts/protocolentities/iq_statuses_result.py b/yowsup/layers/protocol_contacts/protocolentities/iq_statuses_result.py new file mode 100644 index 000000000..75d2ab410 --- /dev/null +++ b/yowsup/layers/protocol_contacts/protocolentities/iq_statuses_result.py @@ -0,0 +1,52 @@ +from yowsup.structs import ProtocolTreeNode +from .iq_sync import SyncIqProtocolEntity +from yowsup.layers.protocol_iq.protocolentities import IqProtocolEntity + +class ResultStatusesIqProtocolEntity(IqProtocolEntity): + ''' + + + + {status message} + HEX:{status message in hex} + + + {status message} + HEX:{status message in hex} + + + + ''' + XMLNS = 'status' + def __init__(self, _id, _from, statuses): + super(ResultStatusesIqProtocolEntity, self).__init__(self.__class__.XMLNS, _id, 'result', _from=_from) + setResultStatusesProps(statusesp) + + def setResultStatusesProps(self, statuses): + assert type(statuses) is dict, "statuses must be dict" + self.statuses = statuses + + def __str__(self): + out = super(ResultStatusesIqProtocolEntity, self).__str__() + out += "Statuses: %s\n" % ','.join(jid + '(' + str(v) + ')' for jid, v in self.statuses.iteritems()) + return out + + def toProtocolTreeNode(self): + node = super(ResultStatusesIqProtocolEntity, self).toProtcolTreeNode() + users = [ProtocolTreeNode('user', {'jid': jid, 't': t}, None, status) for jid, (status, t) in self.statuses.iteritems()] + statusNode = ProtocolTreeNode('status', None, users) + node.addChild(statusNode) + return node + + @staticmethod + def fromProtocolTreeNode(node): + statusNode = node.getChild('status') + users = statusNode.getAllChildren() + statuses = dict() + for user in users: + statuses[user['jid']] = (user.getData(), user['t']) + + entity = IqProtocolEntity.fromProtocolTreeNode(node) + entity.__class__ = ResultStatusesIqProtocolEntity + entity.setResultStatusesProps(statuses) + return entity From 03e30d1492ea029eda8a2213438c7ad2fa429c18 Mon Sep 17 00:00:00 2001 From: t0rr3sp3dr0 Date: Mon, 12 Oct 2015 14:51:40 -0300 Subject: [PATCH 15/18] Fix bug that avoid some acks from being sent #1090 setGroupId function was setting the _id variable to group JID, avoiding group notifications acks from being sent. What resulted in always receive the same notification again and again each time yowsup was restarted. --- .../protocol_groups/protocolentities/notification_groups.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yowsup/layers/protocol_groups/protocolentities/notification_groups.py b/yowsup/layers/protocol_groups/protocolentities/notification_groups.py index d0c7c32d9..4e6398f69 100644 --- a/yowsup/layers/protocol_groups/protocolentities/notification_groups.py +++ b/yowsup/layers/protocol_groups/protocolentities/notification_groups.py @@ -21,10 +21,10 @@ def getParticipant(self, full = True): return self._participant if full else self._participant.split('@')[0] def getGroupId(self): - return self._id + return self.groupId def setGroupId(self, groupId): - self._id = groupId + self.groupId = groupId def __str__(self): From 0c753ef614af92df951fa2e23b85790025cdafff Mon Sep 17 00:00:00 2001 From: Tarek Galal Date: Sun, 13 Dec 2015 20:16:31 +0100 Subject: [PATCH 16/18] Some code clean up, closes #1028 --- yowsup/layers/coder/encoder.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/yowsup/layers/coder/encoder.py b/yowsup/layers/coder/encoder.py index d80225962..70c067194 100644 --- a/yowsup/layers/coder/encoder.py +++ b/yowsup/layers/coder/encoder.py @@ -30,9 +30,12 @@ def protocolTreeNodeToBytes(self, node): def writeInternal(self, node, data): - x = 1 + (0 if node.attributes is None else len(node.attributes) * 2) + (0 if not node.hasChildren() else 1) + (0 if node.data is None else 1) + x = 1 + \ + (0 if node.attributes is None else len(node.attributes) * 2) + \ + (0 if not node.hasChildren() else 1) + \ + (0 if node.data is None else 1) - self.writeListStart(1 + (0 if node.attributes is None else len(node.attributes) * 2) + (0 if not node.hasChildren() else 1) + (0 if node.data is None else 1), data) + self.writeListStart(x, data) self.writeString(node.tag, data) self.writeAttributes(node.attributes, data); @@ -140,4 +143,3 @@ def writeJid(self, user, server, data): else: self.writeToken(0, data) self.writeString(server, data) - From 4353d33e96f6ab3d7dfceb157712eb8e7279d26e Mon Sep 17 00:00:00 2001 From: Tarek Galal Date: Sun, 13 Dec 2015 20:51:23 +0100 Subject: [PATCH 17/18] Fixed YowParallelLayer returning layer instead of its interface Fixes #1061, can't login in cli via /login --- yowsup/layers/__init__.py | 2 +- yowsup/layers/auth/layer_interface_authentication.py | 4 ++-- yowsup/stacks/yowstack.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/yowsup/layers/__init__.py b/yowsup/layers/__init__.py index a59954761..c36e0369e 100644 --- a/yowsup/layers/__init__.py +++ b/yowsup/layers/__init__.py @@ -156,7 +156,7 @@ def __init__(self, sublayers = None): def getLayerInterface(self, YowLayerClass): for s in self.sublayers: if s.__class__ == YowLayerClass: - return s + return s.getLayerInterface() def setStack(self, stack): super(YowParallelLayer, self).setStack(stack) diff --git a/yowsup/layers/auth/layer_interface_authentication.py b/yowsup/layers/auth/layer_interface_authentication.py index 49d72f668..ae3b29fde 100644 --- a/yowsup/layers/auth/layer_interface_authentication.py +++ b/yowsup/layers/auth/layer_interface_authentication.py @@ -2,7 +2,7 @@ class YowAuthenticationProtocolLayerInterface(YowLayerInterface): def setCredentials(self, phone, password): - self._layer.setCredentials(phone, password) + self._layer.setCredentials((phone, password)) def getUsername(self, full = False): - return self._layer.getUsername(full) \ No newline at end of file + return self._layer.getUsername(full) diff --git a/yowsup/stacks/yowstack.py b/yowsup/stacks/yowstack.py index c17a0850c..b360fe623 100644 --- a/yowsup/stacks/yowstack.py +++ b/yowsup/stacks/yowstack.py @@ -152,7 +152,7 @@ def receive(self, data): self.__stackInstances[0].receive(data) def setCredentials(self, credentials): - self.getLayerInterface(YowAuthenticationProtocolLayer).setCredentials(credentials) + self.getLayerInterface(YowAuthenticationProtocolLayer).setCredentials(*credentials) def addLayer(self, layerClass): self.__stack.push(layerClass) From f64e6f4a5a6ccb094f7dd99b8dc2ef0161b29e24 Mon Sep 17 00:00:00 2001 From: Tarek Galal Date: Mon, 14 Dec 2015 08:02:44 +0100 Subject: [PATCH 18/18] bumped to 2.4.48, updated readme --- README.md | 91 ++++++---------------------------------------- yowsup/__init__.py | 2 +- 2 files changed, 13 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 387c9d039..4e7b0035a 100644 --- a/README.md +++ b/README.md @@ -2,93 +2,26 @@ -## Updates (September 07, 2015) -Yowsup v2.4 is out, See [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.4) +## Updates (December 14, 2015) +Yowsup v2.4.48 is out, See [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.4.48) -### Updates (August 01, 2015) -Yowsup v2.3.185 is out, contains fixes in axolotl integration. See [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.185) - -### Updates (July 27, 2015) -Yowsup v2.3.183, see [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.183) - -### Updates (July 21, 2015) -Yowsup v2.3.167, see [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.167) - -### Updates (June 23, 2015) -Yowsup v2.3.123, see [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.123) - -### Updates (May 29, 2015) - -Yowsup v2.3.84, see [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.3.84) - -### Updates (February 17, 2015) - -Yowsup 2.2.78 is out, see [release notes](https://github.com/tgalal/yowsup/releases/tag/v2.2.78). - -### Updates (January 12, 2015) - -Yowsup 2.2.15 is out. - -Image upload and send is now implemented. [Read here](https://github.com/tgalal/yowsup/wiki/Upload-and-send-Media) how to integrate in your code, or try it out with yowsup-cli - -[Read full release notes](https://github.com/tgalal/yowsup/releases/tag/v2.2.15) - -### Updates (January 1, 2015) - -Happy new year! - -P.S: Yowsup's [license changed](https://github.com/tgalal/yowsup#license) to GPLv3 (previously the MIT License was used). - -### (December 31, 2014) - -New features land in yowsup. You will need to re-install to pull new dependencies. Follow the [updated install instructions for your OS](#installation-updated-dec-31-2014). - -A couple of highlights: - -### End-to-End encryption - -Yowsup now implements the new end-to-end encryption recently introduced in WhatsApp (AKA axolotl) (AKA textsecure). To activate in demos pass in '--moxie' switch. -Example: - - > yowsup-cli demos --config /path/to/config --yowsup --moxie - -This will make messages communication with WhatsApp platforms which support this feature encrypted. At the moment it's only Android, which means yowsup got this feature even before official WhatsApp clients on other platforms. - -For platforms which do not support encryption, they will get plaintext messages as usual. - -More technical details about axolotl in yowsup [here](https://github.com/tgalal/yowsup/wiki/End-to-End-encryption) - -### New Send client demo - -Use the send client demo in yowsup-cli to login, send a message and exit. - - > yowsup-cli demos --config /path/to/config --send NUMBER "Hello world" - ========================================================== ## Yowsup opened WhatsApp service under platforms! Yowsup is a python library that enables you build application which use WhatsApp service. Yowsup has been used to create an unofficial WhatsApp client Nokia N9 through the Wazapp project which was in use by 200K + users as well as another fully featured unofficial client for Blackberry 10 -## What's new in Yowsup 2 - -Everything! The old library code was so messed up that I was disgusted just by looking at it. I rewrote the library from ground up with a much more robust, extensible architecture and a much simpler and easier to read code. - -__For devs, the update is breaking for any old code. While old code will stay in "legacy" branch for a while, it's advised that you upgrade your code. Unless your code is a full fledged WhatsApp application, migrating won't be a hard task.__ +## Quickstart -Here is what you need to know about Yowsup 2.0 to get started: (Or quickly [jump to installation](#installation)): - - * **[The new architecture](https://github.com/tgalal/yowsup/wiki/Architecture)** + * **[yowsup's architecture](https://github.com/tgalal/yowsup/wiki/Architecture)** * **[Create a sample app](https://github.com/tgalal/yowsup/wiki/Sample-Application)** - * **[yowsup-cli 2.0](https://github.com/tgalal/yowsup/wiki/yowsup-cli-2.0)** + * **[yowsup-cli](https://github.com/tgalal/yowsup/wiki/yowsup-cli-2.0)** * **[Yowsup development, debugging, maintainance and sanity](https://github.com/tgalal/yowsup/wiki/Yowsup-development,-debugging,-maintainance-and-sanity)** - -## Installation -(Updated Jan 12, 2015) +## Installation - Requires python2.6+, or python3.0 + - - Required python packages: python-dateutil, + - Required python packages: python-dateutil, - Required python packages for end-to-end encryption: protobuf, pycrypto, python-axolotl-curve25519 - Required python packages for yowsup-cli: argparse, readline (or pyreadline for windows), pillow (for sending images) @@ -110,14 +43,14 @@ Because of a bug with python-dateutil package you might get permission error for ``` python setup.py install ``` -Administrators privileges might be required, if so then run with 'sudo' +Administrators privileges might be required, if so then run with 'sudo' ### Windows - Install [mingw](http://www.mingw.org/) compiler - Add mingw to your PATH - In PYTHONPATH\Lib\distutils create a file called distutils.cfg and add these lines: - + ``` [build] compiler=mingw32 @@ -126,7 +59,7 @@ compiler=mingw32 - Install [zlib](http://www.zlib.net/) - ```python setup.py install``` -If pycrypto fails to install with some "chmod error". You can install it separately using something like +If pycrypto fails to install with some "chmod error". You can install it separately using something like ```easy_install http://www.voidspace.org.uk/downloads/pycrypto26/pycrypto-2.6.win32-py2.7.exe``` or for python3 from: @@ -139,8 +72,8 @@ and then rerun the install command again Special thanks to: -- [CODeRUS](https://github.com/CODeRUS) -- [mgp25](https://github.com/mgp25) +- [CODeRUS](https://github.com/CODeRUS) +- [mgp25](https://github.com/mgp25) - [SikiFn](https://github.com/SikiFn) - [0xTryCatch](https://github.com/0xTryCatch) - [shirioko](https://github.com/shirioko) diff --git a/yowsup/__init__.py b/yowsup/__init__.py index c9bd9263a..efb69c560 100644 --- a/yowsup/__init__.py +++ b/yowsup/__init__.py @@ -1,2 +1,2 @@ -__version__ = "2.4" +__version__ = "2.4.48" __author__ = "Tarek Galal"