├── .gitignore ├── LICENSE.md ├── License.txt ├── README.md ├── pytest.ini ├── sample_bot.py ├── setup.cfg ├── setup.py ├── tests ├── __init__.py └── api │ ├── __init__.py │ ├── messages │ ├── __init__.py │ ├── test_contact_message.py │ ├── test_file_message.py │ ├── test_get_message.py │ ├── test_keyboard_message.py │ ├── test_location_message.py │ ├── test_picture_message.py │ ├── test_rich_media_message.py │ ├── test_sticker_message.py │ ├── test_text_message.py │ ├── test_url_message.py │ └── test_video_message.py │ ├── test_api.py │ ├── test_api_request_sender.py │ ├── test_data │ └── unicode_request │ ├── test_message_sender.py │ └── viber_requests │ ├── __init__.py │ ├── test_create_request.py │ ├── test_viber_conversation_started_request.py │ ├── test_viber_delivered_request.py │ ├── test_viber_failed_request.py │ ├── test_viber_message_request.py │ ├── test_viber_seen_request.py │ ├── test_viber_subscribed_request.py │ └── test_viber_unsubscribed_request.py └── viberbot ├── __init__.py ├── api ├── __init__.py ├── api.py ├── api_request_sender.py ├── bot_configuration.py ├── consts.py ├── event_type.py ├── message_sender.py ├── messages │ ├── __init__.py │ ├── contact_message.py │ ├── data_types │ │ ├── __init__.py │ │ ├── contact.py │ │ └── location.py │ ├── file_message.py │ ├── keyboard_message.py │ ├── location_message.py │ ├── message.py │ ├── message_type.py │ ├── picture_message.py │ ├── rich_media_message.py │ ├── sticker_message.py │ ├── text_message.py │ ├── typed_message.py │ ├── url_message.py │ └── video_message.py ├── user_profile.py └── viber_requests │ ├── __init__.py │ ├── viber_conversation_started_request.py │ ├── viber_delivered_request.py │ ├── viber_failed_request.py │ ├── viber_message_request.py │ ├── viber_request.py │ ├── viber_seen_request.py │ ├── viber_subscribed_request.py │ └── viber_unsubscribed_request.py └── version.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .project 3 | *.pyc 4 | *.log 5 | *.user 6 | 7 | 8 | *.jar 9 | *.class 10 | .classpath 11 | *.prefs 12 | git.properties 13 | .idea 14 | *.iml 15 | .gradle 16 | 17 | # Byte-compiled / optimized / DLL files 18 | __pycache__/ 19 | *.py[cod] 20 | *$py.class 21 | 22 | # C extensions 23 | *.so 24 | 25 | # Distribution / packaging 26 | .Python 27 | build/ 28 | develop-eggs/ 29 | dist/ 30 | downloads/ 31 | eggs/ 32 | .eggs/ 33 | lib/ 34 | lib64/ 35 | parts/ 36 | sdist/ 37 | var/ 38 | wheels/ 39 | *.egg-info/ 40 | .installed.cfg 41 | *.egg 42 | 43 | # PyInstaller 44 | # Usually these files are written by a python script from a template 45 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 46 | *.manifest 47 | *.spec 48 | 49 | # Installer logs 50 | pip-log.txt 51 | pip-delete-this-directory.txt 52 | 53 | # Unit test / coverage reports 54 | htmlcov/ 55 | .tox/ 56 | .coverage 57 | .coverage.* 58 | .cache 59 | nosetests.xml 60 | coverage.xml 61 | *.cover 62 | .hypothesis/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # celery beat schedule file 92 | celerybeat-schedule 93 | 94 | # SageMath parsed files 95 | *.sage.py 96 | 97 | # Environments 98 | .env 99 | .venv 100 | env/ 101 | venv/ 102 | ENV/ 103 | 104 | # Spyder project settings 105 | .spyderproject 106 | .spyproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # mkdocs documentation 112 | /site 113 | 114 | # mypy 115 | .mypy_cache/ 116 | 117 | .DS_Store 118 | .idea/ -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | Copyright 2016 Viber Media S.à r.l. 178 | 179 | Licensed under the Apache License, Version 2.0 (the "License"); 180 | you may not use this file except in compliance with the License. 181 | You may obtain a copy of the License at 182 | 183 | http://www.apache.org/licenses/LICENSE-2.0 184 | 185 | Unless required by applicable law or agreed to in writing, software 186 | distributed under the License is distributed on an "AS IS" BASIS, 187 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 188 | See the License for the specific language governing permissions and 189 | limitations under the License. 190 | 191 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | Copyright 2016 Viber Media S.à r.l. 178 | 179 | Licensed under the Apache License, Version 2.0 (the "License"); 180 | you may not use this file except in compliance with the License. 181 | You may obtain a copy of the License at 182 | 183 | http://www.apache.org/licenses/LICENSE-2.0 184 | 185 | Unless required by applicable law or agreed to in writing, software 186 | distributed under the License is distributed on an "AS IS" BASIS, 187 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 188 | See the License for the specific language governing permissions and 189 | limitations under the License. 190 | 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Viber Python Bot API 2 | 3 | Use this library to develop a bot for Viber platform. 4 | The library is available on **[GitHub](https://github.com/Viber/viber-bot-python)** as well as a package on [PyPI](https://pypi.python.org/pypi/viberbot/). 5 | 6 | This package can be imported using pip by adding the following to your `requirements.txt`: 7 | 8 | ```python 9 | viberbot==1.0.11 10 | ``` 11 | 12 | ## License 13 | 14 | This library is released under the terms of the Apache 2.0 license. See [License](https://github.com/Viber/viber-bot-python/blob/master/LICENSE.md) for more information. 15 | 16 | ## Library Prerequisites 17 | 18 | 1. python >= 2.7.0 19 | 1. An Active Viber account on a platform which supports Public Accounts/ bots (iOS/Android). This account will automatically be set as the account administrator during the account creation process. 20 | 1. Active Public Account/ bot - Create an account [here](https://developers.viber.com/docs/general/get-started). 21 | 1. Account authentication token - unique account identifier used to validate your account in all API requests. Once your account is created your authentication token will appear in the account’s “edit info” screen (for admins only). Each request posted to Viber by the account will need to contain the token. 22 | 1. Webhook - Please use a server endpoint URL that supports HTTPS. If you deploy on your own custom server, you'll need a trusted (ca.pem) certificate, not self-signed. Read our [blog post](https://developers.viber.com/blog/2017/05/24/test-your-bots-locally) on how to test your bot locally. 23 | 24 | 25 | ## Contributing 26 | 27 | If you think that there's a bug or there's anything else needed to be changed and you want to change it yourself, you can always create a new Pull request. 28 | Please make sure that your change doesn't break anything and all the unit tests passes. 29 | Also, please make sure that the current tests cover your change, if not please add tests. 30 | 31 | We are using pytest, so if you want to run the tests from the commandline just follow the relevant steps after cloning the repo and creating your branch: 32 | 33 | 34 | ``` 35 | # installing the dependencies: 36 | python setup.py develop 37 | 38 | # run the unit tests 39 | python -m pytest 40 | ``` 41 | 42 | ## Let's get started! 43 | 44 | 45 | ### Installing 46 | 47 | Creating a basic Viber bot is simple: 48 | 49 | 1. Install the library with pip `pip install viberbot` 50 | 2. Import `viberbot.api` library to your project 51 | 3. Create a Public Account or bot and use the API key from [https://developers.viber.com](https://developers.viber.com) 52 | 4. Configure your bot as described in the documentation below 53 | 5. Start your web server 54 | 6. Call `set_webhook(url)` with your web server url 55 | 56 | ## A simple Echo Bot 57 | 58 | ### Firstly, let's import and configure our bot 59 | 60 | ```python 61 | from viberbot import Api 62 | from viberbot.api.bot_configuration import BotConfiguration 63 | 64 | bot_configuration = BotConfiguration( 65 | name='PythonSampleBot', 66 | avatar='http://viber.com/avatar.jpg', 67 | auth_token='YOUR_AUTH_TOKEN_HERE' 68 | ) 69 | viber = Api(bot_configuration) 70 | ``` 71 | 72 | ### Create an HTTPS server 73 | 74 | Next thing you should do is starting a https server. 75 | and yes, as we said in the prerequisites it has to be https server. Create a server however you like, for example with `Flask`: 76 | 77 | ```python 78 | from flask import Flask, request, Response 79 | 80 | app = Flask(__name__) 81 | 82 | @app.route('/incoming', methods=['POST']) 83 | def incoming(): 84 | logger.debug("received request. post data: {0}".format(request.get_data())) 85 | # handle the request here 86 | return Response(status=200) 87 | 88 | context = ('server.crt', 'server.key') 89 | app.run(host='0.0.0.0', port=443, debug=True, ssl_context=context) 90 | ``` 91 | 92 | ### Setting a webhook 93 | 94 | After the server is up and running you can set a webhook. 95 | Viber will push messages sent to this URL. web server should be internet-facing. 96 | 97 | ```python 98 | viber.set_webhook('https://mybotwebserver.com:443/') 99 | ``` 100 | 101 | ### Logging 102 | 103 | This library uses the standard python logger. If you want to see its logs you can configure the logger: 104 | 105 | ```python 106 | logger = logging.getLogger() 107 | logger.setLevel(logging.DEBUG) 108 | handler = logging.StreamHandler() 109 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 110 | handler.setFormatter(formatter) 111 | logger.addHandler(handler) 112 | ``` 113 | 114 | ### Do you supply a basic types of messages? 115 | Well, funny you ask. Yes we do. All the Message types are located in `viberbot.api.messages` package. Here's some examples: 116 | 117 | ```python 118 | from viberbot.api.messages import ( 119 | TextMessage, 120 | ContactMessage, 121 | PictureMessage, 122 | VideoMessage 123 | ) 124 | from viberbot.api.messages.data_types.contact import Contact 125 | 126 | # creation of text message 127 | text_message = TextMessage(text="sample text message!") 128 | 129 | # creation of contact message 130 | contact = Contact(name="Viber user", phone_number="0123456789") 131 | contact_message = ContactMessage(contact=contact) 132 | 133 | # creation of picture message 134 | picture_message = PictureMessage(text="Check this", media="http://site.com/img.jpg") 135 | 136 | # creation of video message 137 | video_message = VideoMessage(media="http://mediaserver.com/video.mp4", size=4324) 138 | ``` 139 | 140 | Have you noticed how we created the `TextMessage`? There's a all bunch of message types you should get familiar with. 141 | 142 | * [Text Message](#TextMessage) 143 | * [Url Message](#UrlMessage) 144 | * [Contact Message](#ContactMessage) 145 | * [Picture Message](#PictureMessage) 146 | * [Video Message](#VideoMessage) 147 | * [Location Message](#LocationMessage) 148 | * [Sticker Message](#StickerMessage) 149 | * [Rich Media Message](#RichMediaMessage) 150 | * [Keyboard Message](#KeyboardMessage) 151 | 152 | Creating them is easy! Every message object has it's own unique constructor corresponding to it's API implementation, click on them to see it! 153 | Check out the full API documentation for more advanced uses. 154 | 155 | ### Let's add it all up and reply with a message! 156 | 157 | ```python 158 | from flask import Flask, request, Response 159 | from viberbot import Api 160 | from viberbot.api.bot_configuration import BotConfiguration 161 | from viberbot.api.messages import VideoMessage 162 | from viberbot.api.messages.text_message import TextMessage 163 | import logging 164 | 165 | from viberbot.api.viber_requests import ViberConversationStartedRequest 166 | from viberbot.api.viber_requests import ViberFailedRequest 167 | from viberbot.api.viber_requests import ViberMessageRequest 168 | from viberbot.api.viber_requests import ViberSubscribedRequest 169 | from viberbot.api.viber_requests import ViberUnsubscribedRequest 170 | 171 | app = Flask(__name__) 172 | viber = Api(BotConfiguration( 173 | name='PythonSampleBot', 174 | avatar='http://site.com/avatar.jpg', 175 | auth_token='445da6az1s345z78-dazcczb2542zv51a-e0vc5fva17480im9' 176 | )) 177 | 178 | 179 | @app.route('/', methods=['POST']) 180 | def incoming(): 181 | logger.debug("received request. post data: {0}".format(request.get_data())) 182 | # every viber message is signed, you can verify the signature using this method 183 | if not viber.verify_signature(request.get_data(), request.headers.get('X-Viber-Content-Signature')): 184 | return Response(status=403) 185 | 186 | # this library supplies a simple way to receive a request object 187 | viber_request = viber.parse_request(request.get_data()) 188 | 189 | if isinstance(viber_request, ViberMessageRequest): 190 | message = viber_request.message 191 | # lets echo back 192 | viber.send_messages(viber_request.sender.id, [ 193 | message 194 | ]) 195 | elif isinstance(viber_request, ViberSubscribedRequest): 196 | viber.send_messages(viber_request.get_user.id, [ 197 | TextMessage(text="thanks for subscribing!") 198 | ]) 199 | elif isinstance(viber_request, ViberFailedRequest): 200 | logger.warn("client failed receiving message. failure: {0}".format(viber_request)) 201 | 202 | return Response(status=200) 203 | 204 | if __name__ == "__main__": 205 | context = ('server.crt', 'server.key') 206 | app.run(host='0.0.0.0', port=443, debug=True, ssl_context=context) 207 | ``` 208 | 209 | As you can see there's a bunch of `Request` types here's a list of them. 210 | 211 | ## Viber API 212 | 213 | ### Api class 214 | 215 | `from viberbot import Api` 216 | 217 | * Api 218 | * [init(bot\_configuration)](#new-Api()) 219 | * [.set\_webhook(url, webhook_events)](#set_webhook) ⇒ `List of registered event_types` 220 | * [.unset\_webhook()](#unset_webhook) ⇒ `None` 221 | * [.get\_account_info()](#get_account_info) ⇒ `object` 222 | * [.verify\_signature(request\_data, signature)](#verify_signature) ⇒ `boolean` 223 | * [.parse\_request(request\_data)](#parse_request) ⇒ `ViberRequest` 224 | * [.send\_messages(to, messages)](#send_messages) ⇒ `list of message tokens sent` 225 | * [.get\_online(viber\_user\_ids)](#get_online) ⇒ `dictionary of users status` 226 | * [.get\_user_details(viber\_user\_id)](#get_user_details) ⇒ `dictionary of user's data` 227 | * [.post\_messages\_to\_public\_account(to, messages)](#post_to_pa) ⇒ `list of message tokens sent` 228 | 229 | 230 | 231 | ### New Api() 232 | 233 | | Param | Type | Description | 234 | | --- | --- | --- | 235 | | bot\_configuration | `object` | `BotConfiguration` | 236 | 237 | 238 | 239 | ### Api.set\_webhook(url) 240 | 241 | | Param | Type | Description | 242 | | --- | --- | --- | 243 | | url | `string` | Your web server url | 244 | | webhook\_events | `list` | optional list of subscribed events | 245 | 246 | Returns `List of registered event_types`. 247 | 248 | ```python 249 | event_types = viber.set_webhook('https://example.com/incoming') 250 | ``` 251 | 252 | 253 | 254 | ### Api.unset\_webhook() 255 | 256 | Returns `None`. 257 | 258 | ```python 259 | viber.unset_webhook() 260 | ``` 261 | 262 | 263 | 264 | ### Api.get\_account\_info() 265 | 266 | Returns an `object` [with the following JSON](https://developers.viber.com/docs/api/rest-bot-api/#get-account-info). 267 | 268 | ```python 269 | account_info = viber.get_account_info() 270 | ``` 271 | 272 | 273 | 274 | ### Api.verify\_signature(request\_data, signature) 275 | 276 | | Param | Type | Description | 277 | | --- | --- | --- | 278 | | request\_data | `string` | the post data from request | 279 | | signature | `string` | sent as header `X-Viber-Content-Signature` | 280 | 281 | 282 | Returns a `boolean` suggesting if the signature is valid. 283 | 284 | ```python 285 | if not viber.verify_signature(request.get_data(), request.headers.get('X-Viber-Content-Signature')): 286 | return Response(status=403) 287 | ``` 288 | 289 | 290 | 291 | ### Api.parse\_request(request\_data) 292 | 293 | | Param | Type | Description | 294 | | --- | --- | --- | 295 | | request\_data | `string` | the post data from request | 296 | 297 | Returns a `ViberRequest` object. 298 | 299 | There's a list of [ViberRequest objects](#ViberRequest) 300 | 301 | ```python 302 | viber_request = viber.parse_request(request.get_data()) 303 | ``` 304 | 305 | 306 | 307 | ### Api.send\_messages(to, messages) 308 | 309 | | Param | Type | Description | 310 | | --- | --- | --- | 311 | | to | `string` | Viber user id | 312 | | messages | `list` | list of `Message` objects | 313 | 314 | Returns `list` of message tokens of the messages sent. 315 | 316 | ```python 317 | tokens = viber.send_messages(to=viber_request.get_sender().get_id(), 318 | messages=[TextMessage(text="sample message")]) 319 | ``` 320 | 321 | 322 | 323 | ### Api.post\_messages\_to\_public\_account(to, messages) 324 | 325 | | Param | Type | Description | 326 | | --- | --- | --- | 327 | | sender | `string` | Viber user id | 328 | | messages | `list` | list of `Message` objects | 329 | 330 | Returns `list` of message tokens of the messages sent. 331 | 332 | ```python 333 | tokens = viber.post_messages_to_public_account(sender=viber_request.get_sender().get_id(), 334 | messages=[TextMessage(text="sample message")]) 335 | ``` 336 | 337 | 338 | 339 | ### Api.get\_online(viber\_user\_ids) 340 | 341 | | Param | Type | Description | 342 | | --- | --- | --- | 343 | | viber\_user\_ids | `array of strings` | Array of Viber user ids | 344 | 345 | Returns a `dictionary of users`. 346 | 347 | ```python 348 | users = Api.get_online(["user1id", "user2id"]) 349 | ``` 350 | 351 | 352 | 353 | ### Api.get\_user\_details(viber\_user\_id) 354 | 355 | | Param | Type | Description | 356 | | --- | --- | --- | 357 | | viber\_user\_ids | `string` | Viber user id | 358 | 359 | The `get_user_details` function will fetch the details of a specific Viber user based on his unique user ID. The user ID can be obtained from the callbacks sent to the account regarding user's actions. This request can be sent twice during a 12 hours period for each user ID. 360 | 361 | ```python 362 | user_data = Api.get_user_details("userId") 363 | ``` 364 | 365 | 366 | 367 | ### Request object 368 | 369 | | Param | Type | Notes | 370 | | --- | --- | --- | 371 | | event\_type | `string` | according to `EventTypes` enum | 372 | | timestamp | `long` | Epoch of request time | 373 | 374 | * ViberRequest 375 | * .event\_type ⇒ `string ` 376 | * .timestamp ⇒ `long` 377 | 378 | 379 | 380 | #### ViberConversationStartedRequest object 381 | 382 | Inherits from [ViberRequest](#ViberRequest) 383 | 384 | Conversation started event fires when a user opens a conversation with the Public Account/ bot using the “message” button (found on the account’s info screen) or using a [deep link](https://developers.viber.com/docs/tools/deep-links). 385 | 386 | This event is **not** considered a subscribe event and doesn't allow the account to send messages to the user; however, it will allow sending one "welcome message" to the user. See [sending a welcome message](#SendingWelcomeMessage) below for more information. 387 | 388 | | Param | Type | Notes | 389 | | --- | --- | --- | 390 | | event\_type | `string` | always equals to the value of `EventType.CONVERSATION_STARTED` | 391 | | message\_token | `string` | Unique ID of the message | 392 | | type | `string` | The specific type of `conversation_started` event. | 393 | | context | `string` | Any additional parameters added to the deep link used to access the conversation passed as a string | 394 | | user | `UserProfile` | the user started the conversation [UserProfile](#UserProfile) | 395 | | subscribed | `boolean` | Indicates whether a user is already subscribed | 396 | 397 | * ViberConversationStartedRequest 398 | * message\_token ⇒ `string` 399 | * type ⇒ `string` 400 | * context ⇒ `string` 401 | * user ⇒ `UserProfile` 402 | 403 | #### ViberDeliveredRequest object 404 | 405 | Inherits from [ViberRequest](#ViberRequest) 406 | 407 | | Param | Type | Notes | 408 | | --- | --- | --- | 409 | | event\_type | `string` | always equals to the value of `EventType.DELIVERED` | 410 | | message\_token | `string` | Unique ID of the message | 411 | | user\_id | `string` | Unique Viber user id | 412 | 413 | * ViberDeliveredRequest 414 | * message\_token ⇒ `string` 415 | * user\_id ⇒ `string` 416 | 417 | #### ViberFailedRequest object 418 | 419 | Inherits from [ViberRequest](#ViberRequest) 420 | 421 | | Param | Type | Notes | 422 | | --- | --- | --- | 423 | | event\_type | `string` | always equals to the value of `EventType.FAILED` | 424 | | message\_token | `string` | Unique ID of the message | 425 | | user\_id | `string` | Unique Viber user id | 426 | | desc | `string` | Failure description | 427 | 428 | * ViberFailedRequest 429 | * message\_token ⇒ `string` 430 | * user\_id ⇒ `string` 431 | * desc ⇒ `string` 432 | 433 | #### ViberMessageRequest object 434 | 435 | Inherits from [ViberRequest](#ViberRequest) 436 | 437 | | Param | Type | Notes | 438 | | --- | --- | --- | 439 | | event\_type | `string` | always equals to the value of `EventType.MESSAGE` | 440 | | message\_token | `string` | Unique ID of the message | 441 | | message | `Message` | `Message` object | 442 | | sender | `UserProfile` | the user started the conversation [UserProfile](#UserProfile) | 443 | 444 | * ViberMessageRequest 445 | * message\_token ⇒ `string` 446 | * message ⇒ `Message` 447 | * sender ⇒ `UserProfile` 448 | 449 | #### ViberSeenRequest object 450 | 451 | Inherits from [ViberRequest](#ViberRequest) 452 | 453 | | Param | Type | Notes | 454 | | --- | --- | --- | 455 | | event\_type | `string` | always equals to the value of `EventType.SEEN` | 456 | | message\_token | `string` | Unique ID of the message | 457 | | user\_id | `string` | Unique Viber user id | 458 | 459 | * ViberSeenRequest 460 | * message\_token ⇒ `string` 461 | * user\_id ⇒ `string` 462 | 463 | #### ViberSubscribedRequest object 464 | 465 | Inherits from [ViberRequest](#ViberRequest) 466 | 467 | | Param | Type | Notes | 468 | | --- | --- | --- | 469 | | event\_type | `string` | always equals to the value of `EventType.SUBSCRIBED` | 470 | | user | `UserProfile` | the user started the conversation [UserProfile](#UserProfile) | 471 | 472 | * ViberSubscribedRequest 473 | * user ⇒ `UserProfile` 474 | 475 | #### ViberUnsubscribedRequest object 476 | 477 | Inherits from [ViberRequest](#ViberRequest) 478 | 479 | | Param | Type | Notes | 480 | | --- | --- | --- | 481 | | event\_type | `string` | always equals to the value of `EventType.UNSUBSCRIBED` | 482 | | user\_id | `string` | Unique Viber user id | 483 | 484 | * ViberUnsubscribedRequest 485 | * get\_user\_id() ⇒ `string` 486 | 487 | 488 | 489 | ### UserProfile object 490 | 491 | | Param | Type | Notes | 492 | | --- | --- | --- | 493 | | id | `string` | --- | 494 | | name | `string` | --- | 495 | | avatar | `string` | Avatar URL | 496 | | country | `string` | **currently set in `CONVERSATION_STARTED` event only** | 497 | | language | `string` | **currently set in `CONVERSATION_STARTED` event only** | 498 | 499 | 500 | 501 | ### Message Object 502 | 503 | **Common Members for `Message` interface**: 504 | 505 | | Param | Type | Description | 506 | | --- | --- | --- | 507 | | timestamp | `long` | Epoch time | 508 | | keyboard | `JSON` | keyboard JSON | 509 | | trackingData | `JSON` | JSON Tracking Data from Viber Client | 510 | 511 | **Common Constructor Arguments `Message` interface**: 512 | 513 | | Param | Type | Description | 514 | | --- | --- | --- | 515 | | optionalKeyboard | `JSON` | [Writing Custom Keyboards](https://developers.viber.com/docs/tools/keyboards) | 516 | | optionalTrackingData | `JSON` | Data to be saved on Viber Client device, and sent back each time message is received | 517 | 518 | 519 | 520 | #### TextMessage object 521 | 522 | | Member | Type 523 | | --- | --- | 524 | | text | `string` | 525 | 526 | ```python 527 | message = TextMessage(text="my text message") 528 | ``` 529 | 530 | 531 | 532 | #### URLMessage object 533 | 534 | | Member | Type | Description | 535 | | --- | --- | --- | 536 | | media | `string` | URL string | 537 | 538 | ```python 539 | message = URLMessage(media="http://my.siteurl.com") 540 | ``` 541 | 542 | 543 | 544 | #### ContactMessage object 545 | 546 | | Member | Type 547 | | --- | --- | 548 | | contact | `Contact` | 549 | 550 | ```python 551 | from viberbot.api.messages.data_types.contact import Contact 552 | 553 | contact = Contact(name="Viber user", phone_number="+0015648979", avatar="http://link.to.avatar") 554 | contact_message = ContactMessage(contact=contact) 555 | ``` 556 | 557 | 558 | 559 | #### PictureMessage object 560 | 561 | | Member | Type | Description | 562 | | --- | --- | --- | 563 | | media | `string` | url of the message (jpeg only) | 564 | | text | `string` | | 565 | | thumbnail | `string` | | 566 | 567 | ```python 568 | message = PictureMessage(media="http://www.thehindubusinessline.com/multimedia/dynamic/01458/viber_logo_JPG_1458024f.jpg", text="Viber logo") 569 | ``` 570 | 571 | 572 | 573 | #### VideoMessage object 574 | 575 | | Member | Type | Description | 576 | | --- | --- | --- | 577 | | media | `string` | url of the video | 578 | | size | `int` | | 579 | | thumbnail | `string` | | 580 | | duration | `int` | | 581 | 582 | ```python 583 | message = VideoMessage(media="http://site.com/video.mp4", size=21499) 584 | ``` 585 | 586 | 587 | 588 | #### LocationMessage object 589 | 590 | | Member | Type 591 | | --- | --- | 592 | | location | `Location` | 593 | 594 | ```python 595 | from viberbot.api.messages.data_types.location import Location 596 | 597 | location = Location(lat=0.0, lon=0.0) 598 | location_message = LocationMessage(location=location) 599 | ``` 600 | 601 | 602 | 603 | #### StickerMessage object 604 | 605 | | Member | Type 606 | | --- | --- | 607 | | sticker\_id | `int` | 608 | 609 | ```python 610 | message = StickerMessage(sticker_id=40100) 611 | ``` 612 | 613 | 614 | 615 | #### FileMessage object 616 | 617 | | Member | Type 618 | | --- | --- | 619 | | media | `string` | 620 | | size | `long` | 621 | | file\_name | `string` | 622 | 623 | ```python 624 | message = FileMessage(media=url, size=sizeInBytes, file_name=file_name) 625 | ``` 626 | 627 | 628 | 629 | #### RichMediaMessage object 630 | 631 | | Member | Type 632 | | --- | --- | 633 | | rich\_media | `string` (JSON) | 634 | 635 | ```python 636 | SAMPLE_RICH_MEDIA = """{ 637 | "BgColor": "#69C48A", 638 | "Buttons": [ 639 | { 640 | "Columns": 6, 641 | "Rows": 1, 642 | "BgColor": "#454545", 643 | "BgMediaType": "gif", 644 | "BgMedia": "http://www.url.by/test.gif", 645 | "BgLoop": true, 646 | "ActionType": "open-url", 647 | "Silent": true, 648 | "ActionBody": "www.tut.by", 649 | "Image": "www.tut.by/img.jpg", 650 | "TextVAlign": "middle", 651 | "TextHAlign": "left", 652 | "Text": "example button", 653 | "TextOpacity": 10, 654 | "TextSize": "regular" 655 | } 656 | ] 657 | }""" 658 | 659 | SAMPLE_ALT_TEXT = "upgrade now!" 660 | 661 | message = RichMediaMessage(rich_media=SAMPLE_RICH_MEDIA, alt_text=SAMPLE_ALT_TEXT) 662 | ``` 663 | 664 | 665 | 666 | #### KeyboardMessage object 667 | 668 | | Member | Type 669 | | --- | --- | 670 | | keyboard | `JSON` | 671 | | tracking_data | `JSON` | 672 | 673 | ```python 674 | message = KeyboardMessage(tracking_data=tracking_data, keyboard=keyboard) 675 | ``` 676 | 677 | 678 | 679 | ### Sending a welcome message 680 | 681 | The Viber API allows sending messages to users only after they subscribe to the account. However, Viber will allow the account to send one “welcome message” to a user as the user opens the conversation, before the user subscribes. 682 | 683 | The welcome message will be sent as a response to a `conversation_started` callback, which will be received from Viber once the user opens the conversation with the account. To learn more about this event and when is it triggered see [`Conversation started`](#ConversationStarted) in the callbacks section. 684 | 685 | #### Welcome message flow 686 | 687 | Sending a welcome message will be done according to the following flow: 688 | 689 | 1. User opens 1-on-1 conversation with account. 690 | 2. Viber server send `conversation_started` even to PA’s webhook. 691 | 3. The account receives the `conversation_started` and responds with an HTTP response which includes the welcome message as the response body. 692 | 693 | The welcome message will be a JSON constructed according to the send_message requests structure, but without the `receiver` parameter. An example welcome message would look like this: 694 | 695 | ```python 696 | @app.route('/', methods=['POST']) 697 | def incoming(): 698 | viber_request = viber.parse_request(request.get_data()) 699 | 700 | if isinstance(viber_request, ViberConversationStartedRequest) : 701 | viber.send_messages(viber_request.get_user().get_id(), [ 702 | TextMessage(text="Welcome!") 703 | ]) 704 | 705 | return Response(status=200) 706 | ``` 707 | 708 | ## Community 709 | 710 | Join the conversation on **[Gitter](https://gitter.im/viber/bot-python)**. 711 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = tests 3 | python_files = *_test.py test_*.py *_tests.py -------------------------------------------------------------------------------- /sample_bot.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, Response 2 | from viberbot import Api 3 | from viberbot.api.bot_configuration import BotConfiguration 4 | from viberbot.api.messages.text_message import TextMessage 5 | from viberbot.api.viber_requests import ViberConversationStartedRequest 6 | from viberbot.api.viber_requests import ViberFailedRequest 7 | from viberbot.api.viber_requests import ViberMessageRequest 8 | from viberbot.api.viber_requests import ViberSubscribedRequest 9 | from viberbot.api.viber_requests import ViberUnsubscribedRequest 10 | 11 | import time 12 | import logging 13 | import sched 14 | import threading 15 | 16 | logger = logging.getLogger() 17 | logger.setLevel(logging.DEBUG) 18 | handler = logging.StreamHandler() 19 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 20 | handler.setFormatter(formatter) 21 | logger.addHandler(handler) 22 | 23 | app = Flask(__name__) 24 | viber = Api(BotConfiguration( 25 | name='PythonSampleBot', 26 | avatar='http://viber.com/avatar.jpg', 27 | auth_token='YOUR_AUTH_TOKEN_HERE' 28 | )) 29 | 30 | @app.route('/', methods=['POST']) 31 | def incoming(): 32 | logger.debug("received request. post data: {0}".format(request.get_data())) 33 | 34 | viber_request = viber.parse_request(request.get_data().decode('utf8')) 35 | 36 | if isinstance(viber_request, ViberMessageRequest): 37 | message = viber_request.message 38 | viber.send_messages(viber_request.sender.id, [ 39 | message 40 | ]) 41 | elif isinstance(viber_request, ViberConversationStartedRequest) \ 42 | or isinstance(viber_request, ViberSubscribedRequest) \ 43 | or isinstance(viber_request, ViberUnsubscribedRequest): 44 | viber.send_messages(viber_request.sender.id, [ 45 | TextMessage(None, None, viber_request.get_event_type()) 46 | ]) 47 | elif isinstance(viber_request, ViberFailedRequest): 48 | logger.warn("client failed receiving message. failure: {0}".format(viber_request)) 49 | 50 | return Response(status=200) 51 | 52 | def set_webhook(viber): 53 | viber.set_webhook('https://mybotwebserver.com:8443/') 54 | 55 | if __name__ == "__main__": 56 | scheduler = sched.scheduler(time.time, time.sleep) 57 | scheduler.enter(5, 1, set_webhook, (viber,)) 58 | t = threading.Thread(target=scheduler.run) 59 | t.start() 60 | 61 | context = ('server.crt', 'server.key') 62 | app.run(host='0.0.0.0', port=8443, debug=True, ssl_context=context) 63 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | exec(open('viberbot/version.py').read()) 3 | setup( 4 | name='viberbot', 5 | version=__version__, 6 | packages=['viberbot', 'viberbot.api', 'viberbot.api.viber_requests', 7 | 'viberbot.api.messages', 'viberbot.api.messages.data_types'], 8 | install_requires=['future', 'requests'], 9 | tests_require=['pytest'], 10 | url='https://github.com/Viber/viber-bot-python', 11 | ) 12 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-python/5cfe34e3b7d1c98afdf5d2c09a4c5722a61f008f/tests/__init__.py -------------------------------------------------------------------------------- /tests/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-python/5cfe34e3b7d1c98afdf5d2c09a4c5722a61f008f/tests/api/__init__.py -------------------------------------------------------------------------------- /tests/api/messages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-python/5cfe34e3b7d1c98afdf5d2c09a4c5722a61f008f/tests/api/messages/__init__.py -------------------------------------------------------------------------------- /tests/api/messages/test_contact_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import ContactMessage 2 | from viberbot.api.messages import MessageType 3 | from viberbot.api.messages.data_types.contact import Contact 4 | 5 | SAMPLE_TRACKING_DATA = "tracking data!" 6 | SAMPLE_KEYBOARD = """{ 7 | "Type": "keyboard", 8 | "DefaultHeight": true, 9 | "BgColor": "#FFFFFF", 10 | "Buttons": [ 11 | { 12 | "Columns": 6, 13 | "Rows": 1, 14 | "BgColor": "#2db9b9", 15 | "BgMediaType": "gif", 16 | "BgMedia": "http://www.url.by/test.gif", 17 | "BgLoop": true, 18 | "ActionType": "open-url", 19 | "ActionBody": "www.tut.by", 20 | "Image": "www.tut.by/img.jpg", 21 | "Text": "Key text", 22 | "TextVAlign": "middle", 23 | "TextHAlign": "center", 24 | "TextOpacity": 60, 25 | "TextSize": "regular" 26 | } 27 | ] 28 | }""" 29 | SAMPLE_CONTACT_NAME = "contact name" 30 | SAMPLE_PHONE_NUMBER = "052947378493" 31 | SAMPLE_AVATAR = "awesomeavatar" 32 | SAMPLE_CONTACT = Contact(SAMPLE_CONTACT_NAME, SAMPLE_PHONE_NUMBER, SAMPLE_AVATAR) 33 | 34 | 35 | def test_creation(): 36 | contact_message = ContactMessage( 37 | tracking_data=SAMPLE_TRACKING_DATA, 38 | keyboard=SAMPLE_KEYBOARD, 39 | contact=SAMPLE_CONTACT) 40 | 41 | assert contact_message.keyboard == SAMPLE_KEYBOARD 42 | assert contact_message.tracking_data == SAMPLE_TRACKING_DATA 43 | assert contact_message.contact == SAMPLE_CONTACT 44 | 45 | 46 | def test_from_dict(): 47 | message_dict = dict( 48 | tracking_data=SAMPLE_TRACKING_DATA, 49 | keyboard=SAMPLE_KEYBOARD, 50 | contact=dict(name=SAMPLE_CONTACT_NAME, phone_number=SAMPLE_PHONE_NUMBER) 51 | ) 52 | 53 | contact_message = ContactMessage().from_dict(message_dict) 54 | 55 | assert contact_message.keyboard == SAMPLE_KEYBOARD 56 | assert contact_message.tracking_data == SAMPLE_TRACKING_DATA 57 | assert contact_message.contact == SAMPLE_CONTACT 58 | 59 | 60 | def test_to_dict(): 61 | message_dict = dict( 62 | type=MessageType.CONTACT, 63 | tracking_data=SAMPLE_TRACKING_DATA, 64 | keyboard=SAMPLE_KEYBOARD, 65 | contact=dict(name=SAMPLE_CONTACT_NAME, phone_number=SAMPLE_PHONE_NUMBER, avatar=SAMPLE_AVATAR)) 66 | 67 | contact_message_dict = ContactMessage(tracking_data=SAMPLE_TRACKING_DATA, 68 | keyboard=SAMPLE_KEYBOARD, 69 | contact=SAMPLE_CONTACT).to_dict() 70 | 71 | assert message_dict == contact_message_dict 72 | 73 | 74 | def test_validate_no_contact(): 75 | contact_message = ContactMessage(tracking_data=SAMPLE_TRACKING_DATA) 76 | assert not contact_message.validate() 77 | 78 | 79 | def test_validate_success(): 80 | contact_message = ContactMessage(tracking_data=SAMPLE_TRACKING_DATA, contact=SAMPLE_CONTACT) 81 | assert contact_message.validate() 82 | 83 | 84 | def test_validate_missing_contact_param(): 85 | contact_message = ContactMessage(tracking_data=SAMPLE_TRACKING_DATA, contact=Contact(name=SAMPLE_CONTACT_NAME)) 86 | assert not contact_message.validate() 87 | 88 | contact_message = ContactMessage(tracking_data=SAMPLE_TRACKING_DATA, contact=Contact(phone_number=SAMPLE_PHONE_NUMBER)) 89 | assert not contact_message.validate() -------------------------------------------------------------------------------- /tests/api/messages/test_file_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import FileMessage 2 | from viberbot.api.messages import MessageType 3 | 4 | SAMPLE_TRACKING_DATA = "some tracking data" 5 | SAMPLE_KEYBOARD = """{ 6 | "Type": "keyboard", 7 | "DefaultHeight": true, 8 | "BgColor": "#FFFFFF", 9 | "Buttons": [ 10 | { 11 | "Columns": 6, 12 | "Rows": 1, 13 | "BgColor": "#2db9b9", 14 | "BgMediaType": "gif", 15 | "BgMedia": "http://www.url.by/test.gif", 16 | "BgLoop": true, 17 | "ActionType": "open-url", 18 | "ActionBody": "www.tut.by", 19 | "Image": "www.tut.by/img.jpg", 20 | "Text": "Key text", 21 | "TextVAlign": "middle", 22 | "TextHAlign": "center", 23 | "TextOpacity": 60, 24 | "TextSize": "regular" 25 | } 26 | ] 27 | }""" 28 | SAMPLE_MEDIA = "http://www.site.com/research.pdf" 29 | SAMPLE_SIZE = 1000 30 | SAMPLE_FILE_NAME = "research.pdf" 31 | 32 | 33 | def test_creation(): 34 | file_message = FileMessage( 35 | tracking_data=SAMPLE_TRACKING_DATA, 36 | keyboard=SAMPLE_KEYBOARD, 37 | media=SAMPLE_MEDIA, 38 | size=SAMPLE_SIZE, 39 | file_name=SAMPLE_FILE_NAME 40 | ) 41 | 42 | assert file_message.tracking_data == SAMPLE_TRACKING_DATA 43 | assert file_message.keyboard == SAMPLE_KEYBOARD 44 | assert file_message.media == SAMPLE_MEDIA 45 | assert file_message.size == SAMPLE_SIZE 46 | assert file_message.file_name == SAMPLE_FILE_NAME 47 | 48 | 49 | def test_from_dict(): 50 | message_dict = dict( 51 | tracking_data=SAMPLE_TRACKING_DATA, 52 | keyboard=SAMPLE_KEYBOARD, 53 | media=SAMPLE_MEDIA, 54 | size=SAMPLE_SIZE, 55 | file_name=SAMPLE_FILE_NAME 56 | ) 57 | 58 | file_message = FileMessage().from_dict(message_dict) 59 | 60 | assert file_message.tracking_data == SAMPLE_TRACKING_DATA 61 | assert file_message.keyboard == SAMPLE_KEYBOARD 62 | assert file_message.media == SAMPLE_MEDIA 63 | assert file_message.size == SAMPLE_SIZE 64 | assert file_message.file_name == SAMPLE_FILE_NAME 65 | 66 | 67 | def test_to_dict(): 68 | message_dict = dict( 69 | type=MessageType.FILE, 70 | tracking_data=SAMPLE_TRACKING_DATA, 71 | keyboard=SAMPLE_KEYBOARD, 72 | media=SAMPLE_MEDIA, 73 | size=SAMPLE_SIZE, 74 | file_name=SAMPLE_FILE_NAME 75 | ) 76 | 77 | file_message_dict = FileMessage( 78 | tracking_data=SAMPLE_TRACKING_DATA, 79 | keyboard=SAMPLE_KEYBOARD, 80 | media=SAMPLE_MEDIA, 81 | size=SAMPLE_SIZE, 82 | file_name=SAMPLE_FILE_NAME 83 | ).to_dict() 84 | 85 | assert message_dict == file_message_dict 86 | 87 | 88 | def test_validate_success(): 89 | file_message = FileMessage( 90 | media=SAMPLE_MEDIA, 91 | size=SAMPLE_SIZE, 92 | file_name=SAMPLE_FILE_NAME 93 | ) 94 | 95 | assert file_message.validate() 96 | 97 | 98 | def test_validate_missing_params(): 99 | file_message = FileMessage( 100 | size=SAMPLE_SIZE, 101 | file_name=SAMPLE_FILE_NAME 102 | ) 103 | 104 | assert not file_message.validate() 105 | 106 | file_message = FileMessage( 107 | media=SAMPLE_MEDIA, 108 | file_name=SAMPLE_FILE_NAME 109 | ) 110 | 111 | assert not file_message.validate() 112 | 113 | file_message = FileMessage( 114 | media=SAMPLE_MEDIA, 115 | size=SAMPLE_SIZE 116 | ) 117 | 118 | assert not file_message.validate() 119 | 120 | 121 | -------------------------------------------------------------------------------- /tests/api/messages/test_get_message.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import json 3 | 4 | import pytest 5 | 6 | from viberbot.api.messages import ContactMessage 7 | from viberbot.api.messages import FileMessage 8 | from viberbot.api.messages import LocationMessage 9 | from viberbot.api.messages import PictureMessage 10 | from viberbot.api.messages import TextMessage 11 | from viberbot.api.messages import URLMessage 12 | from viberbot.api.messages import VideoMessage 13 | from viberbot.api.messages import get_message 14 | 15 | 16 | def test_contact_message(): 17 | contact_message_data = """ 18 | { 19 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 20 | "receiver": "01234567890A=", 21 | "sender": 22 | { 23 | "name": "yarden from the pa", 24 | "avatar": "http://avatar_url" 25 | }, 26 | "tracking_data": "tracking data", 27 | "type": "contact", 28 | "contact": { 29 | "name": "Alex", 30 | "phone_number": "+972511123123" 31 | } 32 | } 33 | """ 34 | 35 | contact_message = get_message(json.loads(contact_message_data)) 36 | assert isinstance(contact_message, ContactMessage) 37 | 38 | 39 | def test_file_message(): 40 | file_message_data = """ 41 | { 42 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 43 | "receiver": "01234567890A=", 44 | "sender": 45 | { 46 | "name": "yarden from the pa", 47 | "avatar": "http://avatar_url" 48 | }, 49 | "tracking_data": "tracking data", 50 | "type": "file", 51 | "media": "http://www.images.com/file.doc", 52 | "size": 10000, 53 | "file_name": "name_of_file.doc" 54 | } 55 | """ 56 | 57 | file_message = get_message(json.loads(file_message_data)) 58 | assert isinstance(file_message, FileMessage) 59 | 60 | 61 | def test_location_message(): 62 | location_message_data = """ 63 | { 64 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 65 | "receiver": "01234567890A=", 66 | "sender": 67 | { 68 | "name": "yarden from the pa", 69 | "avatar": "http://avatar_url" 70 | }, 71 | "tracking_data": "tracking data", 72 | "type": "location", 73 | "location": {"lat": "37.7898", "lon": "-122.3942"} 74 | } 75 | """ 76 | 77 | location_message = get_message(json.loads(location_message_data)) 78 | assert isinstance(location_message, LocationMessage) 79 | 80 | 81 | def test_picture_message(): 82 | picture_message_data = """ 83 | { 84 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 85 | "receiver": "01234567890A=", 86 | "sender": 87 | { 88 | "name": "yarden from the pa", 89 | "avatar": "http://avatar_url" 90 | }, 91 | "tracking_data": "tracking data", 92 | "type": "picture", 93 | "text": "Photo description", 94 | "media": "http://www.images.com/img.jpg", 95 | "thumbnail": "http://www.images.com/thumb.jpg" 96 | } 97 | """ 98 | 99 | picture_message = get_message(json.loads(picture_message_data)) 100 | assert isinstance(picture_message, PictureMessage) 101 | 102 | 103 | def test_text_message(): 104 | text_message_data = """ 105 | { 106 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 107 | "receiver": "01234567890A=", 108 | "sender": 109 | { 110 | "name": "yarden from the pa", 111 | "avatar": "http://avatar_url" 112 | }, 113 | "tracking_data": "tracking data", 114 | "type": "text", 115 | "text": "a message from pa" 116 | } 117 | """ 118 | 119 | text_message = get_message(json.loads(text_message_data)) 120 | assert isinstance(text_message, TextMessage) 121 | 122 | 123 | def test_url_message(): 124 | url_message_data = """ 125 | { 126 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 127 | "receiver": "01234567890A=", 128 | "sender": 129 | { 130 | "name": "yarden from the pa", 131 | "avatar": "http://avatar_url" 132 | }, 133 | "tracking_data": "tracking data", 134 | "type": "url", 135 | "media": "http://www.website.com/go_here" 136 | } 137 | """ 138 | 139 | url_message = get_message(json.loads(url_message_data)) 140 | assert isinstance(url_message, URLMessage) 141 | 142 | 143 | def test_video_message(): 144 | video_message_data = """ 145 | { 146 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 147 | "receiver": "01234567890A=", 148 | "sender": 149 | { 150 | "name": "yarden from the pa", 151 | "avatar": "http://avatar_url" 152 | }, 153 | "tracking_data": "tracking data", 154 | "type": "video", 155 | "media": "http://www.images.com/video.mp4", 156 | "thumbnail": "http://www.images.com/thumb.jpg", 157 | "size": 10000, 158 | "duration": 10 159 | } 160 | """ 161 | 162 | video_message = get_message(json.loads(video_message_data)) 163 | assert isinstance(video_message, VideoMessage) 164 | 165 | 166 | def test_unknown_type(): 167 | with pytest.raises(Exception) as exc: 168 | message_data = """ 169 | { 170 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 171 | "receiver": "01234567890A=", 172 | "sender": 173 | { 174 | "name": "yarden from the pa", 175 | "avatar": "http://avatar_url" 176 | }, 177 | "tracking_data": "tracking data", 178 | "type": "NotExists" 179 | } 180 | """ 181 | 182 | get_message(json.loads(message_data)) 183 | assert exc.value.message.startswith("message type 'NotExists' is not supported") 184 | 185 | 186 | def test_get_text_message_unicode(): 187 | text_message_data = u""" 188 | { 189 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 190 | "receiver": "01234567890A=", 191 | "sender": 192 | { 193 | "name": "שם בעברית", 194 | "avatar": "http://avatar_url" 195 | }, 196 | "tracking_data": "へぶる。塞末タヨハ協責ウワ格子しむ", 197 | "type": "text", 198 | "text": "הודעה יפה" 199 | } 200 | """ 201 | 202 | text_message = get_message(json.loads(text_message_data)) 203 | assert isinstance(text_message, TextMessage) 204 | 205 | 206 | def test_get_message_missing_type(): 207 | with pytest.raises(Exception) as exc: 208 | message_data = """ 209 | { 210 | "auth_token": "4453b6ac1s345678-e02c5f12174805f9-daec9cbb5448c51r", 211 | "receiver": "01234567890A=", 212 | "sender": 213 | { 214 | "name": "yarden from the pa", 215 | "avatar": "http://avatar_url" 216 | } 217 | } 218 | """ 219 | 220 | get_message(json.loads(message_data)) 221 | assert exc.value.message.startswith("message data doesn't contain a type") 222 | -------------------------------------------------------------------------------- /tests/api/messages/test_keyboard_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import KeyboardMessage 2 | 3 | SAMPLE_TRACKING_DATA = "some tracking data" 4 | SAMPLE_KEYBOARD = { 5 | "Type": "keyboard", 6 | "Revision": 1, 7 | "Buttons": [ 8 | { 9 | "Columns": 3, 10 | "Rows": 2, 11 | "BgColor": "#e6f5ff", 12 | "BgMedia": "http://www.jqueryscript.net/images/Simplest-Responsive-jQuery-Image-Lightbox-Plugin-simple-lightbox.jpg", 13 | "BgMediaType": "picture", 14 | "BgLoop": True, 15 | "ActionType": "reply", 16 | "ActionBody": "whatwhatwhatwhatwhatwhat" 17 | } 18 | ] 19 | } 20 | 21 | 22 | def test_creation(): 23 | keyboard_message = KeyboardMessage( 24 | tracking_data=SAMPLE_TRACKING_DATA, 25 | keyboard=SAMPLE_KEYBOARD 26 | ) 27 | 28 | assert keyboard_message.tracking_data == SAMPLE_TRACKING_DATA 29 | assert keyboard_message.keyboard == SAMPLE_KEYBOARD 30 | 31 | 32 | def test_to_dict(): 33 | message_dict = dict( 34 | tracking_data=SAMPLE_TRACKING_DATA, 35 | keyboard=SAMPLE_KEYBOARD 36 | ) 37 | 38 | keyboard_message_dict = KeyboardMessage( 39 | tracking_data=SAMPLE_TRACKING_DATA, 40 | keyboard=SAMPLE_KEYBOARD 41 | ).to_dict() 42 | 43 | assert message_dict == keyboard_message_dict 44 | 45 | 46 | def test_from_dict(): 47 | message_dict = dict( 48 | tracking_data=SAMPLE_TRACKING_DATA, 49 | keyboard=SAMPLE_KEYBOARD 50 | ) 51 | 52 | keyboard_message = KeyboardMessage().from_dict(message_dict) 53 | 54 | assert keyboard_message.tracking_data == SAMPLE_TRACKING_DATA 55 | assert keyboard_message.keyboard == SAMPLE_KEYBOARD 56 | 57 | 58 | def test_validate_success(): 59 | keyboard_message = KeyboardMessage( 60 | tracking_data=SAMPLE_TRACKING_DATA, 61 | keyboard=SAMPLE_KEYBOARD 62 | ) 63 | 64 | assert keyboard_message.validate() 65 | 66 | 67 | def test_validate_failure(): 68 | keyboard_message = KeyboardMessage( 69 | tracking_data=SAMPLE_TRACKING_DATA 70 | ) 71 | 72 | assert not keyboard_message.validate() 73 | -------------------------------------------------------------------------------- /tests/api/messages/test_location_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import LocationMessage 2 | from viberbot.api.messages import MessageType 3 | from viberbot.api.messages.data_types.location import Location 4 | 5 | SAMPLE_TRACKING_DATA = "some tracking data" 6 | SAMPLE_KEYBOARD = """{ 7 | "Type": "keyboard", 8 | "DefaultHeight": true, 9 | "BgColor": "#FFFFFF", 10 | "Buttons": [ 11 | { 12 | "Columns": 6, 13 | "Rows": 1, 14 | "BgColor": "#2db9b9", 15 | "BgMediaType": "gif", 16 | "BgMedia": "http://www.url.by/test.gif", 17 | "BgLoop": true, 18 | "ActionType": "open-url", 19 | "ActionBody": "www.tut.by", 20 | "Image": "www.tut.by/img.jpg", 21 | "Text": "Key text", 22 | "TextVAlign": "middle", 23 | "TextHAlign": "center", 24 | "TextOpacity": 60, 25 | "TextSize": "regular" 26 | } 27 | ] 28 | }""" 29 | SAMPLE_LATITUDE = 10.7 30 | SAMPLE_LONGITUDE = -10.1 31 | SAMPLE_LOCATION = Location(SAMPLE_LATITUDE, SAMPLE_LONGITUDE) 32 | 33 | 34 | def test_creation(): 35 | location_message = LocationMessage( 36 | tracking_data=SAMPLE_TRACKING_DATA, 37 | keyboard=SAMPLE_KEYBOARD, 38 | location=SAMPLE_LOCATION 39 | ) 40 | 41 | assert location_message.tracking_data == SAMPLE_TRACKING_DATA 42 | assert location_message.keyboard == SAMPLE_KEYBOARD 43 | assert location_message.location == SAMPLE_LOCATION 44 | 45 | 46 | def test_from_dict(): 47 | message_dict = dict( 48 | tracking_data=SAMPLE_TRACKING_DATA, 49 | keyboard=SAMPLE_KEYBOARD, 50 | location=dict(lat=SAMPLE_LATITUDE, lon=SAMPLE_LONGITUDE) 51 | ) 52 | 53 | location_message = LocationMessage().from_dict(message_dict) 54 | 55 | assert location_message.tracking_data == SAMPLE_TRACKING_DATA 56 | assert location_message.keyboard == SAMPLE_KEYBOARD 57 | assert location_message.location == SAMPLE_LOCATION 58 | 59 | 60 | def test_to_dict(): 61 | message_dict = dict( 62 | type=MessageType.LOCATION, 63 | tracking_data=SAMPLE_TRACKING_DATA, 64 | keyboard=SAMPLE_KEYBOARD, 65 | location=dict(lat=SAMPLE_LATITUDE, lon=SAMPLE_LONGITUDE) 66 | ) 67 | 68 | location_message_dict = LocationMessage( 69 | tracking_data=SAMPLE_TRACKING_DATA, 70 | keyboard=SAMPLE_KEYBOARD, 71 | location=SAMPLE_LOCATION 72 | ).to_dict() 73 | 74 | assert message_dict == location_message_dict 75 | 76 | 77 | def test_validate_success(): 78 | location_message = LocationMessage( 79 | tracking_data=SAMPLE_TRACKING_DATA, 80 | keyboard=SAMPLE_KEYBOARD, 81 | location=SAMPLE_LOCATION 82 | ) 83 | 84 | assert location_message.validate() 85 | 86 | 87 | def test_validate_failure(): 88 | location_message = LocationMessage( 89 | tracking_data=SAMPLE_TRACKING_DATA, 90 | keyboard=SAMPLE_KEYBOARD 91 | ) 92 | assert not location_message.validate() 93 | 94 | location_message = LocationMessage( 95 | tracking_data=SAMPLE_TRACKING_DATA, 96 | keyboard=SAMPLE_KEYBOARD, 97 | location=Location() 98 | ) 99 | assert not location_message.validate() 100 | 101 | location_message = LocationMessage( 102 | tracking_data=SAMPLE_TRACKING_DATA, 103 | keyboard=SAMPLE_KEYBOARD, 104 | location=Location(lat=SAMPLE_LATITUDE) 105 | ) 106 | assert not location_message.validate() 107 | 108 | location_message = LocationMessage( 109 | tracking_data=SAMPLE_TRACKING_DATA, 110 | keyboard=SAMPLE_KEYBOARD, 111 | location=Location(lon=SAMPLE_LONGITUDE) 112 | ) 113 | assert not location_message.validate() 114 | 115 | 116 | def test_validate_out_of_range(): 117 | OUT_OF_RANGE_LATITUDE = 200 118 | OUT_OF_RANGE_LONGITUDE = 200 119 | 120 | location_message = LocationMessage( 121 | tracking_data=SAMPLE_TRACKING_DATA, 122 | keyboard=SAMPLE_KEYBOARD, 123 | location=Location(lat=SAMPLE_LATITUDE, lon=OUT_OF_RANGE_LONGITUDE) 124 | ) 125 | assert not location_message.validate() 126 | 127 | location_message = LocationMessage( 128 | tracking_data=SAMPLE_TRACKING_DATA, 129 | keyboard=SAMPLE_KEYBOARD, 130 | location=Location(lat=OUT_OF_RANGE_LATITUDE, lon=SAMPLE_LONGITUDE) 131 | ) 132 | assert not location_message.validate() 133 | 134 | 135 | -------------------------------------------------------------------------------- /tests/api/messages/test_picture_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import MessageType 2 | from viberbot.api.messages import PictureMessage 3 | 4 | SAMPLE_TRACKING_DATA = "some tracking data" 5 | SAMPLE_KEYBOARD = """{ 6 | "Type": "keyboard", 7 | "DefaultHeight": true, 8 | "BgColor": "#FFFFFF", 9 | "Buttons": [ 10 | { 11 | "Columns": 6, 12 | "Rows": 1, 13 | "BgColor": "#2db9b9", 14 | "BgMediaType": "gif", 15 | "BgMedia": "http://www.url.by/test.gif", 16 | "BgLoop": true, 17 | "ActionType": "open-url", 18 | "ActionBody": "www.tut.by", 19 | "Image": "www.tut.by/img.jpg", 20 | "Text": "Key text", 21 | "TextVAlign": "middle", 22 | "TextHAlign": "center", 23 | "TextOpacity": 60, 24 | "TextSize": "regular" 25 | } 26 | ] 27 | }""" 28 | SAMPLE_TEXT = "bridge" 29 | SAMPLE_MEDIA = "http://www.site.com/bridge.jpg" 30 | SAMPLE_THUMBNAIL = "http://www.site.com/bridgethumb.jpg" 31 | 32 | 33 | def test_creation(): 34 | picture_message = PictureMessage( 35 | tracking_data=SAMPLE_TRACKING_DATA, 36 | keyboard=SAMPLE_KEYBOARD, 37 | text=SAMPLE_TEXT, 38 | media=SAMPLE_MEDIA, 39 | thumbnail=SAMPLE_THUMBNAIL 40 | ) 41 | 42 | assert picture_message.tracking_data == SAMPLE_TRACKING_DATA 43 | assert picture_message.keyboard == SAMPLE_KEYBOARD 44 | assert picture_message.media == SAMPLE_MEDIA 45 | assert picture_message.text == SAMPLE_TEXT 46 | assert picture_message.thumbnail == SAMPLE_THUMBNAIL 47 | 48 | 49 | def test_from_dict(): 50 | message_dict = dict( 51 | tracking_data=SAMPLE_TRACKING_DATA, 52 | keyboard=SAMPLE_KEYBOARD, 53 | text=SAMPLE_TEXT, 54 | media=SAMPLE_MEDIA, 55 | thumbnail=SAMPLE_THUMBNAIL 56 | ) 57 | 58 | picture_message = PictureMessage().from_dict(message_dict) 59 | 60 | assert picture_message.tracking_data == SAMPLE_TRACKING_DATA 61 | assert picture_message.keyboard == SAMPLE_KEYBOARD 62 | assert picture_message.media == SAMPLE_MEDIA 63 | assert picture_message.text == SAMPLE_TEXT 64 | assert picture_message.thumbnail == SAMPLE_THUMBNAIL 65 | 66 | 67 | def test_to_dict(): 68 | message_dict = dict( 69 | type=MessageType.PICTURE, 70 | tracking_data=SAMPLE_TRACKING_DATA, 71 | keyboard=SAMPLE_KEYBOARD, 72 | text=SAMPLE_TEXT, 73 | media=SAMPLE_MEDIA, 74 | thumbnail=SAMPLE_THUMBNAIL 75 | ) 76 | 77 | picture_message_dict = PictureMessage( 78 | tracking_data=SAMPLE_TRACKING_DATA, 79 | keyboard=SAMPLE_KEYBOARD, 80 | text=SAMPLE_TEXT, 81 | media=SAMPLE_MEDIA, 82 | thumbnail=SAMPLE_THUMBNAIL 83 | ).to_dict() 84 | 85 | assert message_dict == picture_message_dict 86 | 87 | 88 | def test_validate_success(): 89 | picture_message = PictureMessage( 90 | tracking_data=SAMPLE_TRACKING_DATA, 91 | keyboard=SAMPLE_KEYBOARD, 92 | text=SAMPLE_TEXT, 93 | media=SAMPLE_MEDIA, 94 | thumbnail=SAMPLE_THUMBNAIL 95 | ) 96 | 97 | assert picture_message.validate() 98 | 99 | 100 | def test_validate_failure(): 101 | picture_message = PictureMessage( 102 | tracking_data=SAMPLE_TRACKING_DATA, 103 | keyboard=SAMPLE_KEYBOARD, 104 | text=SAMPLE_TEXT, 105 | thumbnail=SAMPLE_THUMBNAIL 106 | ) 107 | 108 | assert not picture_message.validate() 109 | -------------------------------------------------------------------------------- /tests/api/messages/test_rich_media_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import MessageType 2 | from viberbot.api.messages import RichMediaMessage 3 | 4 | SAMPLE_TRACKING_DATA = "some tracking data" 5 | SAMPLE_KEYBOARD = """{ 6 | "Type": "keyboard", 7 | "DefaultHeight": true, 8 | "BgColor": "#FFFFFF", 9 | "Buttons": [ 10 | { 11 | "Columns": 6, 12 | "Rows": 1, 13 | "BgColor": "#2db9b9", 14 | "BgMediaType": "gif", 15 | "BgMedia": "http://www.url.by/test.gif", 16 | "BgLoop": true, 17 | "ActionType": "open-url", 18 | "ActionBody": "www.tut.by", 19 | "Image": "www.tut.by/img.jpg", 20 | "Text": "Key text", 21 | "TextVAlign": "middle", 22 | "TextHAlign": "center", 23 | "TextOpacity": 60, 24 | "TextSize": "regular" 25 | } 26 | ] 27 | }""" 28 | SAMPLE_RICH_MEDIA = """{ 29 | "DefaultHeight": true, 30 | "BgColor": "#69C48A", 31 | "Buttons": [ 32 | { 33 | "Columns": 6, 34 | "Rows": 1, 35 | "BgColor": "#454545", 36 | "BgMediaType": "gif", 37 | "BgMedia": "http://www.url.by/test.gif", 38 | "BgLoop": true, 39 | "ActionType": "open-url", 40 | "Silent": true, 41 | "ActionBody": "www.tut.by", 42 | "Image": "www.tut.by/img.jpg", 43 | "TextVAlign": "middle", 44 | "TextHAlign": "left", 45 | "Text": "Viber is the best company", 46 | "TextOpacity": 10, 47 | "TextSize": "regular" 48 | } 49 | ] 50 | }""" 51 | SAMPLE_ALT_TEXT = "upgrade now!" 52 | 53 | 54 | def test_creation(): 55 | text_message = RichMediaMessage( 56 | tracking_data=SAMPLE_TRACKING_DATA, 57 | keyboard=SAMPLE_KEYBOARD, 58 | rich_media=SAMPLE_RICH_MEDIA, 59 | alt_text=SAMPLE_ALT_TEXT 60 | ) 61 | 62 | assert text_message.tracking_data == SAMPLE_TRACKING_DATA 63 | assert text_message.keyboard == SAMPLE_KEYBOARD 64 | assert text_message.rich_media == SAMPLE_RICH_MEDIA 65 | assert text_message.alt_text == SAMPLE_ALT_TEXT 66 | 67 | 68 | def test_to_dict(): 69 | message_dict = dict( 70 | type=MessageType.RICH_MEDIA, 71 | tracking_data=SAMPLE_TRACKING_DATA, 72 | keyboard=SAMPLE_KEYBOARD, 73 | rich_media=SAMPLE_RICH_MEDIA, 74 | alt_text=SAMPLE_ALT_TEXT 75 | ) 76 | 77 | rich_media_message_dict = RichMediaMessage( 78 | tracking_data=SAMPLE_TRACKING_DATA, 79 | keyboard=SAMPLE_KEYBOARD, 80 | rich_media=SAMPLE_RICH_MEDIA, 81 | alt_text=SAMPLE_ALT_TEXT 82 | ).to_dict() 83 | 84 | assert message_dict == rich_media_message_dict 85 | 86 | 87 | def test_from_dict(): 88 | message_dict = dict( 89 | tracking_data=SAMPLE_TRACKING_DATA, 90 | keyboard=SAMPLE_KEYBOARD, 91 | rich_media=SAMPLE_RICH_MEDIA, 92 | alt_text=SAMPLE_ALT_TEXT 93 | ) 94 | 95 | text_message = RichMediaMessage().from_dict(message_dict) 96 | 97 | assert text_message.tracking_data == SAMPLE_TRACKING_DATA 98 | assert text_message.keyboard == SAMPLE_KEYBOARD 99 | assert text_message.rich_media == SAMPLE_RICH_MEDIA 100 | assert text_message.alt_text == SAMPLE_ALT_TEXT 101 | 102 | 103 | def test_validate_success(): 104 | text_message = RichMediaMessage( 105 | tracking_data=SAMPLE_TRACKING_DATA, 106 | keyboard=SAMPLE_KEYBOARD, 107 | rich_media=SAMPLE_RICH_MEDIA 108 | ) 109 | 110 | assert text_message.validate() 111 | 112 | 113 | def test_validate_failure(): 114 | text_message = RichMediaMessage( 115 | tracking_data=SAMPLE_TRACKING_DATA, 116 | keyboard=SAMPLE_KEYBOARD 117 | ) 118 | 119 | assert not text_message.validate() 120 | -------------------------------------------------------------------------------- /tests/api/messages/test_sticker_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import MessageType 2 | from viberbot.api.messages import StickerMessage 3 | 4 | SAMPLE_TRACKING_DATA = "some tracking data" 5 | SAMPLE_KEYBOARD = """{ 6 | "Type": "keyboard", 7 | "DefaultHeight": true, 8 | "BgColor": "#FFFFFF", 9 | "Buttons": [ 10 | { 11 | "Columns": 6, 12 | "Rows": 1, 13 | "BgColor": "#2db9b9", 14 | "BgMediaType": "gif", 15 | "BgMedia": "http://www.url.by/test.gif", 16 | "BgLoop": true, 17 | "ActionType": "open-url", 18 | "ActionBody": "www.tut.by", 19 | "Image": "www.tut.by/img.jpg", 20 | "Text": "Key text", 21 | "TextVAlign": "middle", 22 | "TextHAlign": "center", 23 | "TextOpacity": 60, 24 | "TextSize": "regular" 25 | } 26 | ] 27 | }""" 28 | SAMPLE_STICKER_ID = 40100 29 | 30 | 31 | def test_creation(): 32 | sticker_message = StickerMessage( 33 | tracking_data=SAMPLE_TRACKING_DATA, 34 | keyboard=SAMPLE_KEYBOARD, 35 | sticker_id=SAMPLE_STICKER_ID 36 | ) 37 | 38 | assert sticker_message.tracking_data == SAMPLE_TRACKING_DATA 39 | assert sticker_message.keyboard == SAMPLE_KEYBOARD 40 | assert sticker_message.sticker_id == SAMPLE_STICKER_ID 41 | 42 | 43 | def test_to_dict(): 44 | message_dict = dict( 45 | type=MessageType.STICKER, 46 | tracking_data=SAMPLE_TRACKING_DATA, 47 | keyboard=SAMPLE_KEYBOARD, 48 | sticker_id=SAMPLE_STICKER_ID 49 | ) 50 | 51 | sticker_message_dict = StickerMessage( 52 | tracking_data=SAMPLE_TRACKING_DATA, 53 | keyboard=SAMPLE_KEYBOARD, 54 | sticker_id=SAMPLE_STICKER_ID 55 | ).to_dict() 56 | 57 | assert message_dict == sticker_message_dict 58 | 59 | 60 | def test_from_dict(): 61 | message_dict = dict( 62 | tracking_data=SAMPLE_TRACKING_DATA, 63 | keyboard=SAMPLE_KEYBOARD, 64 | sticker_id=SAMPLE_STICKER_ID 65 | ) 66 | 67 | sticker_message = StickerMessage().from_dict(message_dict) 68 | 69 | assert sticker_message.tracking_data == SAMPLE_TRACKING_DATA 70 | assert sticker_message.keyboard == SAMPLE_KEYBOARD 71 | assert sticker_message.sticker_id == SAMPLE_STICKER_ID 72 | 73 | 74 | def test_validate_success(): 75 | sticker_message = StickerMessage( 76 | tracking_data=SAMPLE_TRACKING_DATA, 77 | keyboard=SAMPLE_KEYBOARD, 78 | sticker_id=SAMPLE_STICKER_ID 79 | ) 80 | 81 | assert sticker_message.validate() 82 | 83 | 84 | def test_validate_failure(): 85 | sticker_message = StickerMessage( 86 | tracking_data=SAMPLE_TRACKING_DATA, 87 | keyboard=SAMPLE_KEYBOARD 88 | ) 89 | 90 | assert not sticker_message.validate() 91 | -------------------------------------------------------------------------------- /tests/api/messages/test_text_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import MessageType 2 | from viberbot.api.messages import TextMessage 3 | 4 | SAMPLE_TRACKING_DATA = "some tracking data" 5 | SAMPLE_KEYBOARD = """{ 6 | "Type": "keyboard", 7 | "DefaultHeight": true, 8 | "BgColor": "#FFFFFF", 9 | "Buttons": [ 10 | { 11 | "Columns": 6, 12 | "Rows": 1, 13 | "BgColor": "#2db9b9", 14 | "BgMediaType": "gif", 15 | "BgMedia": "http://www.url.by/test.gif", 16 | "BgLoop": true, 17 | "ActionType": "open-url", 18 | "ActionBody": "www.tut.by", 19 | "Image": "www.tut.by/img.jpg", 20 | "Text": "Key text", 21 | "TextVAlign": "middle", 22 | "TextHAlign": "center", 23 | "TextOpacity": 60, 24 | "TextSize": "regular" 25 | } 26 | ] 27 | }""" 28 | SAMPLE_TEXT = "sample text" 29 | 30 | 31 | def test_creation(): 32 | text_message = TextMessage( 33 | tracking_data=SAMPLE_TRACKING_DATA, 34 | keyboard=SAMPLE_KEYBOARD, 35 | text=SAMPLE_TEXT 36 | ) 37 | 38 | assert text_message.tracking_data == SAMPLE_TRACKING_DATA 39 | assert text_message.keyboard == SAMPLE_KEYBOARD 40 | assert text_message.text == SAMPLE_TEXT 41 | 42 | 43 | def test_to_dict(): 44 | message_dict = dict( 45 | type=MessageType.TEXT, 46 | tracking_data=SAMPLE_TRACKING_DATA, 47 | keyboard=SAMPLE_KEYBOARD, 48 | text=SAMPLE_TEXT 49 | ) 50 | 51 | text_message_dict = TextMessage( 52 | tracking_data=SAMPLE_TRACKING_DATA, 53 | keyboard=SAMPLE_KEYBOARD, 54 | text=SAMPLE_TEXT 55 | ).to_dict() 56 | 57 | assert message_dict == text_message_dict 58 | 59 | 60 | def test_from_dict(): 61 | message_dict = dict( 62 | tracking_data=SAMPLE_TRACKING_DATA, 63 | keyboard=SAMPLE_KEYBOARD, 64 | text=SAMPLE_TEXT 65 | ) 66 | 67 | text_message = TextMessage().from_dict(message_dict) 68 | 69 | assert text_message.tracking_data == SAMPLE_TRACKING_DATA 70 | assert text_message.keyboard == SAMPLE_KEYBOARD 71 | assert text_message.text == SAMPLE_TEXT 72 | 73 | 74 | def test_validate_success(): 75 | text_message = TextMessage( 76 | tracking_data=SAMPLE_TRACKING_DATA, 77 | keyboard=SAMPLE_KEYBOARD, 78 | text=SAMPLE_TEXT 79 | ) 80 | 81 | assert text_message.validate() 82 | 83 | 84 | def test_validate_failure(): 85 | text_message = TextMessage( 86 | tracking_data=SAMPLE_TRACKING_DATA, 87 | keyboard=SAMPLE_KEYBOARD 88 | ) 89 | 90 | assert not text_message.validate() 91 | -------------------------------------------------------------------------------- /tests/api/messages/test_url_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import MessageType 2 | from viberbot.api.messages import URLMessage 3 | 4 | SAMPLE_TRACKING_DATA = "some tracking data" 5 | SAMPLE_KEYBOARD = """{ 6 | "Type": "keyboard", 7 | "DefaultHeight": true, 8 | "BgColor": "#FFFFFF", 9 | "Buttons": [ 10 | { 11 | "Columns": 6, 12 | "Rows": 1, 13 | "BgColor": "#2db9b9", 14 | "BgMediaType": "gif", 15 | "BgMedia": "http://www.url.by/test.gif", 16 | "BgLoop": true, 17 | "ActionType": "open-url", 18 | "ActionBody": "www.tut.by", 19 | "Image": "www.tut.by/img.jpg", 20 | "Text": "Key text", 21 | "TextVAlign": "middle", 22 | "TextHAlign": "center", 23 | "TextOpacity": 60, 24 | "TextSize": "regular" 25 | } 26 | ] 27 | }""" 28 | SAMPLE_URL = "http://www.google.com" 29 | 30 | 31 | def test_creation(): 32 | url_message = URLMessage( 33 | tracking_data=SAMPLE_TRACKING_DATA, 34 | keyboard=SAMPLE_KEYBOARD, 35 | media=SAMPLE_URL 36 | ) 37 | 38 | assert url_message.tracking_data == SAMPLE_TRACKING_DATA 39 | assert url_message.keyboard == SAMPLE_KEYBOARD 40 | assert url_message.media == SAMPLE_URL 41 | 42 | 43 | def test_to_dict(): 44 | message_dict = dict( 45 | type=MessageType.URL, 46 | tracking_data=SAMPLE_TRACKING_DATA, 47 | keyboard=SAMPLE_KEYBOARD, 48 | media=SAMPLE_URL 49 | ) 50 | 51 | url_message_dict = URLMessage( 52 | tracking_data=SAMPLE_TRACKING_DATA, 53 | keyboard=SAMPLE_KEYBOARD, 54 | media=SAMPLE_URL 55 | ).to_dict() 56 | 57 | assert message_dict == url_message_dict 58 | 59 | 60 | def test_from_dict(): 61 | message_dict = dict( 62 | tracking_data=SAMPLE_TRACKING_DATA, 63 | keyboard=SAMPLE_KEYBOARD, 64 | media=SAMPLE_URL 65 | ) 66 | 67 | url_message = URLMessage().from_dict(message_dict) 68 | 69 | assert url_message.tracking_data == SAMPLE_TRACKING_DATA 70 | assert url_message.keyboard == SAMPLE_KEYBOARD 71 | assert url_message.media == SAMPLE_URL 72 | 73 | 74 | def test_validate_success(): 75 | url_message = URLMessage( 76 | tracking_data=SAMPLE_TRACKING_DATA, 77 | keyboard=SAMPLE_KEYBOARD, 78 | media=SAMPLE_URL 79 | ) 80 | 81 | assert url_message.validate() 82 | 83 | 84 | def test_validate_failure(): 85 | url_message = URLMessage( 86 | tracking_data=SAMPLE_TRACKING_DATA, 87 | keyboard=SAMPLE_KEYBOARD 88 | ) 89 | 90 | assert not url_message.validate() 91 | -------------------------------------------------------------------------------- /tests/api/messages/test_video_message.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages import MessageType 2 | from viberbot.api.messages import VideoMessage 3 | 4 | SAMPLE_TRACKING_DATA = "some tracking data" 5 | SAMPLE_KEYBOARD = """{ 6 | "Type": "keyboard", 7 | "DefaultHeight": true, 8 | "BgColor": "#FFFFFF", 9 | "Buttons": [ 10 | { 11 | "Columns": 6, 12 | "Rows": 1, 13 | "BgColor": "#2db9b9", 14 | "BgMediaType": "gif", 15 | "BgMedia": "http://www.url.by/test.gif", 16 | "BgLoop": true, 17 | "ActionType": "open-url", 18 | "ActionBody": "www.tut.by", 19 | "Image": "www.tut.by/img.jpg", 20 | "Text": "Key text", 21 | "TextVAlign": "middle", 22 | "TextHAlign": "center", 23 | "TextOpacity": 60, 24 | "TextSize": "regular" 25 | } 26 | ] 27 | }""" 28 | SAMPLE_MEDIA = "http://www.site.com/bridge.mp4" 29 | SAMPLE_THUMBNAIL = "http://www.site.com/bridgethumb.jpg" 30 | SAMPLE_SIZE = 1000 31 | SAMPLE_DURATION = 10 32 | SAMPLE_TEXT = "see my video!" 33 | 34 | 35 | def test_creation(): 36 | video_message = VideoMessage( 37 | tracking_data=SAMPLE_TRACKING_DATA, 38 | keyboard=SAMPLE_KEYBOARD, 39 | media=SAMPLE_MEDIA, 40 | thumbnail=SAMPLE_THUMBNAIL, 41 | size=SAMPLE_SIZE, 42 | duration=SAMPLE_DURATION, 43 | text=SAMPLE_TEXT 44 | ) 45 | 46 | assert video_message.tracking_data == SAMPLE_TRACKING_DATA 47 | assert video_message.keyboard == SAMPLE_KEYBOARD 48 | assert video_message.media == SAMPLE_MEDIA 49 | assert video_message.size == SAMPLE_SIZE 50 | assert video_message.thumbnail == SAMPLE_THUMBNAIL 51 | assert video_message.duration == SAMPLE_DURATION 52 | assert video_message.text == SAMPLE_TEXT 53 | 54 | 55 | def test_from_dict(): 56 | message_dict = dict( 57 | tracking_data=SAMPLE_TRACKING_DATA, 58 | keyboard=SAMPLE_KEYBOARD, 59 | media=SAMPLE_MEDIA, 60 | thumbnail=SAMPLE_THUMBNAIL, 61 | size=SAMPLE_SIZE, 62 | duration=SAMPLE_DURATION, 63 | text=SAMPLE_TEXT 64 | ) 65 | 66 | video_message = VideoMessage().from_dict(message_dict) 67 | 68 | assert video_message.tracking_data == SAMPLE_TRACKING_DATA 69 | assert video_message.keyboard == SAMPLE_KEYBOARD 70 | assert video_message.media == SAMPLE_MEDIA 71 | assert video_message.size == SAMPLE_SIZE 72 | assert video_message.thumbnail == SAMPLE_THUMBNAIL 73 | assert video_message.duration == SAMPLE_DURATION 74 | assert video_message.text == SAMPLE_TEXT 75 | 76 | 77 | def test_to_dict(): 78 | message_dict = dict( 79 | type=MessageType.VIDEO, 80 | tracking_data=SAMPLE_TRACKING_DATA, 81 | keyboard=SAMPLE_KEYBOARD, 82 | media=SAMPLE_MEDIA, 83 | thumbnail=SAMPLE_THUMBNAIL, 84 | size=SAMPLE_SIZE, 85 | duration=SAMPLE_DURATION, 86 | text=SAMPLE_TEXT 87 | ) 88 | 89 | video_message_dict = VideoMessage( 90 | tracking_data=SAMPLE_TRACKING_DATA, 91 | keyboard=SAMPLE_KEYBOARD, 92 | media=SAMPLE_MEDIA, 93 | thumbnail=SAMPLE_THUMBNAIL, 94 | size=SAMPLE_SIZE, 95 | duration=SAMPLE_DURATION, 96 | text=SAMPLE_TEXT 97 | ).to_dict() 98 | 99 | assert message_dict == video_message_dict 100 | 101 | 102 | def test_validate_success(): 103 | video_message = VideoMessage( 104 | media=SAMPLE_MEDIA, 105 | size=SAMPLE_SIZE 106 | ) 107 | 108 | assert video_message.validate() 109 | 110 | 111 | def test_validate_failure(): 112 | video_message = VideoMessage( 113 | tracking_data=SAMPLE_TRACKING_DATA, 114 | keyboard=SAMPLE_KEYBOARD, 115 | thumbnail=SAMPLE_THUMBNAIL, 116 | size=SAMPLE_SIZE, 117 | duration=SAMPLE_DURATION 118 | ) 119 | 120 | assert not video_message.validate() 121 | 122 | video_message = VideoMessage( 123 | tracking_data=SAMPLE_TRACKING_DATA, 124 | keyboard=SAMPLE_KEYBOARD, 125 | media=SAMPLE_MEDIA, 126 | thumbnail=SAMPLE_THUMBNAIL, 127 | duration=SAMPLE_DURATION 128 | ) 129 | 130 | assert not video_message.validate() 131 | -------------------------------------------------------------------------------- /tests/api/test_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | from viberbot import Api 6 | from viberbot import BotConfiguration 7 | from viberbot.api.viber_requests import ViberMessageRequest 8 | 9 | VIBER_BOT_CONFIGURATION = BotConfiguration("44dafb7e0f40021e-61a47a1e6778d187-f2c5a676a07050b3", "testbot", "http://avatars.com/") 10 | 11 | 12 | def test_verify_signature(): 13 | valid_signature = 'd21b343448c8aee33b8e93768ef6ceb64a6ba6163099973a2b8bd028fea510ef' 14 | message = "{\"event\":\"webhook\",\"timestamp\":4977069964384421269,\"message_token\":1478683725125}".encode("utf-8") 15 | 16 | viber = Api(VIBER_BOT_CONFIGURATION) 17 | assert viber.verify_signature(message, valid_signature) 18 | 19 | 20 | def test_verify_signature_failure(): 21 | invalid_signature = 'aaaaaaaaaaaaaaaaaaaaaa' 22 | message = "{\"event\":\"webhook\"}".encode("utf-8") 23 | 24 | viber = Api(VIBER_BOT_CONFIGURATION) 25 | assert not viber.verify_signature(message, invalid_signature) 26 | 27 | 28 | def test_parse_request_not_json(): 29 | viber = Api(VIBER_BOT_CONFIGURATION) 30 | 31 | with pytest.raises(ValueError) as exc: 32 | viber.parse_request("dsfdfdsf\#") 33 | 34 | 35 | def test_parse_request_unicode(): 36 | viber = Api(VIBER_BOT_CONFIGURATION) 37 | 38 | with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_data', 'unicode_request')) as f: 39 | req = f.read() 40 | viber_request = viber.parse_request(req) 41 | assert isinstance(viber_request, ViberMessageRequest) 42 | -------------------------------------------------------------------------------- /tests/api/test_api_request_sender.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | 4 | import pytest 5 | 6 | from viberbot.api.api_request_sender import ApiRequestSender 7 | from viberbot.api.bot_configuration import BotConfiguration 8 | from viberbot.api.consts import BOT_API_ENDPOINT, VIBER_BOT_USER_AGENT 9 | from viberbot.api.event_type import EventType 10 | 11 | 12 | class Stub(object): pass 13 | 14 | 15 | def stub(*args): pass 16 | 17 | 18 | VIBER_BOT_API_URL = "http://site.com" 19 | VIBER_BOT_CONFIGURATION = BotConfiguration("auth-token-sample", "testbot", "http://avatars.com/") 20 | 21 | 22 | def test_set_webhook_sanity(): 23 | webhook_events = [EventType.DELIVERED, EventType.UNSUBSCRIBED, EventType.SEEN] 24 | url = "http://sample.url.com" 25 | 26 | def post_request(endpoint, payload): 27 | request = json.loads(payload) 28 | assert endpoint == BOT_API_ENDPOINT.SET_WEBHOOK 29 | assert request['auth_token'] == VIBER_BOT_CONFIGURATION.auth_token 30 | assert request['event_types'] == webhook_events 31 | assert request['url'] == url 32 | return dict(status=0, event_types=webhook_events) 33 | 34 | request_sender = ApiRequestSender(logging.getLogger(), VIBER_BOT_API_URL, VIBER_BOT_CONFIGURATION, VIBER_BOT_USER_AGENT) 35 | request_sender.post_request = post_request 36 | 37 | request_sender.set_webhook(url=url, webhook_events=webhook_events) 38 | 39 | 40 | def test_set_webhook_failure(): 41 | webhook_events = [EventType.DELIVERED, EventType.UNSUBSCRIBED, EventType.SEEN] 42 | url = "http://sample.url.com" 43 | 44 | def post_request(endpoint, payload): 45 | return dict(status=1, status_message="failed") 46 | 47 | request_sender = ApiRequestSender(logging.getLogger(), VIBER_BOT_API_URL, VIBER_BOT_CONFIGURATION, VIBER_BOT_USER_AGENT) 48 | request_sender.post_request = post_request 49 | with pytest.raises(Exception) as exc: 50 | request_sender.set_webhook(url=url, webhook_events=webhook_events) 51 | 52 | assert exc.value.message.startswith("failed with status: 1, message: failed") 53 | 54 | 55 | def test_post_request_success(monkeypatch): 56 | def callback(endpoint, data, headers): 57 | request = json.loads(data) 58 | assert endpoint == VIBER_BOT_API_URL + "/" + BOT_API_ENDPOINT.GET_ACCOUNT_INFO 59 | assert request['auth_token'] == VIBER_BOT_CONFIGURATION.auth_token 60 | assert headers['User-Agent'] == VIBER_BOT_USER_AGENT 61 | response = Stub() 62 | response.raise_for_status = stub 63 | response.text = "{}" 64 | 65 | return response 66 | 67 | monkeypatch.setattr("requests.post", callback) 68 | 69 | request_sender = ApiRequestSender(logging.getLogger(), VIBER_BOT_API_URL, VIBER_BOT_CONFIGURATION, VIBER_BOT_USER_AGENT) 70 | request_sender.get_account_info() 71 | 72 | 73 | def test_post_request_json_exception(monkeypatch): 74 | def callback(endpoint, data, headers): 75 | request = json.loads(data) 76 | assert endpoint == VIBER_BOT_API_URL + "/" + BOT_API_ENDPOINT.GET_ACCOUNT_INFO 77 | assert request['auth_token'] == VIBER_BOT_CONFIGURATION.auth_token 78 | assert headers['User-Agent'] == VIBER_BOT_USER_AGENT 79 | response = Stub() 80 | response.raise_for_status = stub 81 | response.text = "not a json{/" 82 | 83 | return response 84 | 85 | monkeypatch.setattr("requests.post", callback) 86 | request_sender = ApiRequestSender(logging.getLogger(), VIBER_BOT_API_URL, VIBER_BOT_CONFIGURATION, VIBER_BOT_USER_AGENT) 87 | 88 | with pytest.raises(Exception) as exc: 89 | request_sender.get_account_info() 90 | 91 | 92 | def test_get_online_status_fail(monkeypatch): 93 | def callback(endpoint, data, headers): 94 | request = json.loads(data) 95 | assert endpoint == VIBER_BOT_API_URL + "/" + BOT_API_ENDPOINT.GET_ONLINE 96 | assert request['auth_token'] == VIBER_BOT_CONFIGURATION.auth_token 97 | assert headers['User-Agent'] == VIBER_BOT_USER_AGENT 98 | response = Stub() 99 | response.raise_for_status = stub 100 | response.text = "{\"status\": 1, \"status_message\": \"fail\"}" 101 | 102 | return response 103 | 104 | monkeypatch.setattr("requests.post", callback) 105 | request_sender = ApiRequestSender(logging.getLogger(), VIBER_BOT_API_URL, VIBER_BOT_CONFIGURATION, VIBER_BOT_USER_AGENT) 106 | 107 | with pytest.raises(Exception) as exc: 108 | request_sender.get_online_status(ids=["0123456789="]) 109 | 110 | assert exc.value.message.startswith("failed with status:") 111 | 112 | 113 | def test_get_online_missing_ids(monkeypatch): 114 | monkeypatch.setattr("requests.post", stub) 115 | request_sender = ApiRequestSender(logging.getLogger(), VIBER_BOT_API_URL, VIBER_BOT_CONFIGURATION, VIBER_BOT_USER_AGENT) 116 | 117 | with pytest.raises(Exception) as exc: 118 | request_sender.get_online_status(ids=None) 119 | 120 | assert exc.value.message.startswith("missing parameter ids, should be a list of viber memberIds") 121 | 122 | 123 | def test_get_online_success(monkeypatch): 124 | def callback(endpoint, data, headers): 125 | request = json.loads(data) 126 | assert endpoint == VIBER_BOT_API_URL + "/" + BOT_API_ENDPOINT.GET_ONLINE 127 | assert request['auth_token'] == VIBER_BOT_CONFIGURATION.auth_token 128 | response = Stub() 129 | response.raise_for_status = stub 130 | response.text = "{\"status\": 0, \"status_message\": \"OK\", \"users\": []}" 131 | 132 | return response 133 | 134 | monkeypatch.setattr("requests.post", callback) 135 | request_sender = ApiRequestSender(logging.getLogger(), VIBER_BOT_API_URL, VIBER_BOT_CONFIGURATION, VIBER_BOT_USER_AGENT) 136 | 137 | request_sender.get_online_status(ids=["03249305A="]) 138 | 139 | 140 | def test_get_user_details_success(monkeypatch): 141 | def callback(endpoint, data, headers): 142 | request = json.loads(data) 143 | assert endpoint == VIBER_BOT_API_URL + "/" + BOT_API_ENDPOINT.GET_USER_DETAILS 144 | assert request['auth_token'] == VIBER_BOT_CONFIGURATION.auth_token 145 | response = Stub() 146 | response.raise_for_status = stub 147 | response.text = "{\"status\": 0, \"status_message\": \"OK\", \"user\": {}}" 148 | 149 | return response 150 | 151 | monkeypatch.setattr("requests.post", callback) 152 | request_sender = ApiRequestSender(logging.getLogger(), VIBER_BOT_API_URL, VIBER_BOT_CONFIGURATION, VIBER_BOT_USER_AGENT) 153 | 154 | request_sender.get_user_details(user_id="03249305A=") 155 | -------------------------------------------------------------------------------- /tests/api/test_data/unicode_request: -------------------------------------------------------------------------------- 1 | { 2 | "event": "message", 3 | "timestamp": 1457764197627, 4 | "message_token": 4912661846655238145, 5 | "sender": { 6 | "id": "01234567890A=", 7 | "name": "ふで都急ぼちラり面関ごル期販ずラ否裁ぞるめ数努むや正記味てょ", 8 | "avatar": "http://avatar_url", 9 | "api_version": 2 10 | }, 11 | "message": { 12 | "type": "text", 13 | "text": "批封廟根縄華勢田郎報株聞。変宝長独部鉱内出殺反提似惑金参勧車査真測。見名基現画府日室事集百藤米。発目本当党考万名東拓学早職表治月佐。町要静子呉遊推写間確長無読死覧件一盛治融。", 14 | "tracking_data": "אל תעקוב אחרי!" 15 | }, 16 | "silent": false 17 | } -------------------------------------------------------------------------------- /tests/api/test_message_sender.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | 4 | import pytest 5 | 6 | from viberbot.api.bot_configuration import BotConfiguration 7 | from viberbot.api.consts import BOT_API_ENDPOINT 8 | from viberbot.api.message_sender import MessageSender 9 | from viberbot.api.messages import TextMessage 10 | 11 | LOGGER = logging.getLogger('.') 12 | VIBER_BOT_CONFIGURATION = BotConfiguration("auth-token-sample", "testbot", "http://avatars.com/") 13 | 14 | class Stub(object): pass 15 | 16 | 17 | def stub(*args): pass 18 | 19 | 20 | def test_send_message_sanity(): 21 | to = "012345A=" 22 | text = "hi!" 23 | message_token = "a token" 24 | chat_id = 'my chat id sample' 25 | 26 | def post_request(endpoint, payload): 27 | data = json.loads(payload) 28 | assert endpoint == BOT_API_ENDPOINT.SEND_MESSAGE 29 | assert data['auth_token'] == VIBER_BOT_CONFIGURATION.auth_token 30 | assert data['receiver'] == to 31 | assert data['sender']['name'] == VIBER_BOT_CONFIGURATION.name 32 | assert data['sender']['avatar'] == VIBER_BOT_CONFIGURATION.avatar 33 | assert data['text'] == text 34 | assert data['chat_id'] == chat_id 35 | return dict(status=0, message_token=message_token) 36 | 37 | request_sender = Stub() 38 | request_sender.post_request = post_request 39 | 40 | text_message = TextMessage(text=text) 41 | 42 | message_sender = MessageSender(LOGGER, request_sender, VIBER_BOT_CONFIGURATION) 43 | token = message_sender.send_message( 44 | to, VIBER_BOT_CONFIGURATION.name, VIBER_BOT_CONFIGURATION.avatar, text_message, chat_id) 45 | assert token == message_token 46 | 47 | 48 | def test_post_to_public_account_sanity(): 49 | sender = "012345A=" 50 | text = "hi!" 51 | message_token = "a token" 52 | 53 | def post_request(endpoint, payload): 54 | data = json.loads(payload) 55 | assert endpoint == BOT_API_ENDPOINT.POST 56 | assert data['auth_token'] == VIBER_BOT_CONFIGURATION.auth_token 57 | assert data['from'] == sender 58 | assert data['sender']['name'] == VIBER_BOT_CONFIGURATION.name 59 | assert data['sender']['avatar'] == VIBER_BOT_CONFIGURATION.avatar 60 | assert data['text'] == text 61 | return dict(status=0, message_token=message_token) 62 | 63 | request_sender = Stub() 64 | request_sender.post_request = post_request 65 | 66 | text_message = TextMessage(text=text) 67 | 68 | message_sender = MessageSender(LOGGER, request_sender, VIBER_BOT_CONFIGURATION) 69 | token = message_sender.post_to_public_account( 70 | sender, VIBER_BOT_CONFIGURATION.name, VIBER_BOT_CONFIGURATION.avatar, text_message) 71 | assert token == message_token 72 | 73 | 74 | def test_message_invalid(): 75 | to = "012345A=" 76 | 77 | def post_request(endpoint, payload): 78 | pytest.fail("message sender not supposed to call post_request") 79 | 80 | request_sender = Stub() 81 | request_sender.post_request = post_request 82 | 83 | text_message = TextMessage(text=None) 84 | 85 | message_sender = MessageSender(LOGGER, request_sender, VIBER_BOT_CONFIGURATION) 86 | with pytest.raises(Exception) as exc: 87 | message_sender.send_message(to, VIBER_BOT_CONFIGURATION.name, VIBER_BOT_CONFIGURATION.avatar, text_message) 88 | 89 | assert exc.value.message.startswith("failed validating message:") 90 | 91 | 92 | def test_send_result_failed(): 93 | to = "012345A=" 94 | text = "hi!" 95 | 96 | def post_request(endpoint, payload): 97 | data = json.loads(payload) 98 | return dict(status=1, status_message="failed") 99 | 100 | request_sender = Stub() 101 | request_sender.post_request = post_request 102 | 103 | text_message = TextMessage(text=text) 104 | 105 | message_sender = MessageSender(LOGGER, request_sender, VIBER_BOT_CONFIGURATION) 106 | with pytest.raises(Exception) as exc: 107 | message_sender.send_message(to, VIBER_BOT_CONFIGURATION.name, VIBER_BOT_CONFIGURATION.avatar, text_message) 108 | 109 | assert exc.value.message.startswith("failed with status: 1, message: failed") 110 | 111 | 112 | def test_post_message_to_public_account_failed(): 113 | sender = "012345A=" 114 | text = "hi!" 115 | 116 | def post_request(endpoint, payload): 117 | data = json.loads(payload) 118 | return dict(status=1, status_message="failed") 119 | 120 | request_sender = Stub() 121 | request_sender.post_request = post_request 122 | 123 | text_message = TextMessage(text=text) 124 | 125 | message_sender = MessageSender(LOGGER, request_sender, VIBER_BOT_CONFIGURATION) 126 | with pytest.raises(Exception) as exc: 127 | message_sender.post_to_public_account( 128 | sender, VIBER_BOT_CONFIGURATION.name, VIBER_BOT_CONFIGURATION.avatar, text_message) 129 | 130 | assert exc.value.message.startswith("failed with status: 1, message: failed") -------------------------------------------------------------------------------- /tests/api/viber_requests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-python/5cfe34e3b7d1c98afdf5d2c09a4c5722a61f008f/tests/api/viber_requests/__init__.py -------------------------------------------------------------------------------- /tests/api/viber_requests/test_create_request.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | import pytest 4 | from viberbot.api.messages import MessageType 5 | from viberbot.api.viber_requests import create_request 6 | 7 | 8 | def test_create_request_missing_event(): 9 | sample_request = dict( 10 | timestamp=datetime.now(), 11 | message_token="912661846655238145", 12 | sender=dict( 13 | id="01234567890A=", 14 | name="viberUser", 15 | avatar="http://avatar_url" 16 | ), 17 | message=dict( 18 | type=MessageType.TEXT, 19 | text="HI!" 20 | )) 21 | 22 | with pytest.raises(Exception) as exc: 23 | create_request(sample_request) 24 | assert exc.value.message.startswith("request is missing field 'event'") -------------------------------------------------------------------------------- /tests/api/viber_requests/test_viber_conversation_started_request.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.viber_requests import ViberConversationStartedRequest 5 | from viberbot.api.viber_requests import create_request 6 | 7 | SAMPLE_REQUEST = dict( 8 | event=EventType.CONVERSATION_STARTED, 9 | timestamp=datetime.now(), 10 | message_token="912661846655238145", 11 | type="open", 12 | context="context info", 13 | subscribed=True, 14 | user=dict( 15 | id="01234567890A=", 16 | name="viberUser", 17 | avatar="http://avatar_url", 18 | country="IL", 19 | language="en" 20 | )) 21 | 22 | 23 | def test_create_request(): 24 | request = create_request(SAMPLE_REQUEST) 25 | 26 | assert isinstance(request, ViberConversationStartedRequest) 27 | assert request.event_type == SAMPLE_REQUEST['event'] 28 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 29 | assert request.message_token == SAMPLE_REQUEST['message_token'] 30 | assert request.type == SAMPLE_REQUEST['type'] 31 | assert request.context == SAMPLE_REQUEST['context'] 32 | assert request.user.id == SAMPLE_REQUEST['user']['id'] 33 | assert request.user.name == SAMPLE_REQUEST['user']['name'] 34 | assert request.user.avatar == SAMPLE_REQUEST['user']['avatar'] 35 | assert request.user.country == SAMPLE_REQUEST['user']['country'] 36 | assert request.user.language == SAMPLE_REQUEST['user']['language'] 37 | assert request.subscribed == SAMPLE_REQUEST['subscribed'] 38 | 39 | 40 | def test_missing_user_optional_params(): 41 | request_dict = dict( 42 | event=EventType.CONVERSATION_STARTED, 43 | timestamp=datetime.now(), 44 | message_token="912661846655238145", 45 | type="open", 46 | context="context info", 47 | user=dict( 48 | id="01234567890A=", 49 | name="viberUser" 50 | )) 51 | 52 | request = create_request(request_dict) # should not fail 53 | 54 | assert isinstance(request, ViberConversationStartedRequest) 55 | assert request.event_type == request_dict['event'] 56 | assert request.timestamp == request_dict['timestamp'] 57 | assert request.message_token == request_dict['message_token'] 58 | assert request.type == request_dict['type'] 59 | assert request.context == request_dict['context'] 60 | assert request.user.id == request_dict['user']['id'] 61 | assert request.user.name == request_dict['user']['name'] 62 | assert request.user.avatar is None 63 | assert request.user.country is None 64 | assert request.user.language is None 65 | assert request.subscribed is None 66 | -------------------------------------------------------------------------------- /tests/api/viber_requests/test_viber_delivered_request.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.viber_requests import ViberDeliveredRequest 5 | from viberbot.api.viber_requests import create_request 6 | 7 | SAMPLE_REQUEST = dict( 8 | event=EventType.DELIVERED, 9 | timestamp=datetime.now(), 10 | message_token="912661846655238145", 11 | user_id="01234567890A=", 12 | chat_id="3456675689-45345-35346235" 13 | ) 14 | 15 | 16 | def test_create_request(): 17 | request = create_request(SAMPLE_REQUEST) 18 | 19 | assert isinstance(request, ViberDeliveredRequest) 20 | assert request.event_type == SAMPLE_REQUEST['event'] 21 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 22 | assert request.message_token == SAMPLE_REQUEST['message_token'] 23 | assert request.user_id == SAMPLE_REQUEST['user_id'] 24 | assert request.chat_id == SAMPLE_REQUEST['chat_id'] 25 | -------------------------------------------------------------------------------- /tests/api/viber_requests/test_viber_failed_request.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.viber_requests import ViberFailedRequest 5 | from viberbot.api.viber_requests import create_request 6 | 7 | SAMPLE_REQUEST = dict( 8 | event=EventType.FAILED, 9 | timestamp=datetime.now(), 10 | message_token="912661846655238145", 11 | user_id="01234567890A=", 12 | desc="failure desc" 13 | ) 14 | 15 | 16 | def test_create_request(): 17 | request = create_request(SAMPLE_REQUEST) 18 | 19 | assert isinstance(request, ViberFailedRequest) 20 | assert request.event_type == SAMPLE_REQUEST['event'] 21 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 22 | assert request.meesage_token == SAMPLE_REQUEST['message_token'] 23 | assert request.user_id == SAMPLE_REQUEST['user_id'] 24 | assert request.desc == SAMPLE_REQUEST['desc'] 25 | -------------------------------------------------------------------------------- /tests/api/viber_requests/test_viber_message_request.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.messages import MessageType 5 | from viberbot.api.messages import TextMessage 6 | from viberbot.api.viber_requests import ViberMessageRequest 7 | from viberbot.api.viber_requests import create_request 8 | 9 | SAMPLE_REQUEST = dict( 10 | event=EventType.MESSAGE, 11 | timestamp=datetime.now(), 12 | message_token="912661846655238145", 13 | sender=dict( 14 | id="01234567890A=", 15 | name="viberUser", 16 | avatar="http://avatar_url" 17 | ), 18 | message=dict( 19 | type=MessageType.TEXT, 20 | text="HI!" 21 | ), 22 | silent=True) 23 | 24 | 25 | def test_create_request(): 26 | request = create_request(SAMPLE_REQUEST) 27 | 28 | assert isinstance(request, ViberMessageRequest) 29 | assert request.event_type == SAMPLE_REQUEST['event'] 30 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 31 | assert request.message_token == SAMPLE_REQUEST['message_token'] 32 | assert request.silent == SAMPLE_REQUEST['silent'] 33 | assert request.sender.id == SAMPLE_REQUEST['sender']['id'] 34 | assert request.sender.name == SAMPLE_REQUEST['sender']['name'] 35 | assert request.sender.avatar == SAMPLE_REQUEST['sender']['avatar'] 36 | assert isinstance(request.message, TextMessage) 37 | 38 | 39 | def test_user_has_no_avatar(): 40 | SAMPLE_REQUEST = dict( 41 | event=EventType.MESSAGE, 42 | timestamp=datetime.now(), 43 | message_token="912661846655238145", 44 | sender=dict( 45 | id="01234567890A=", 46 | name="viberUser" 47 | ), 48 | message=dict( 49 | type=MessageType.TEXT, 50 | text="HI!" 51 | ), 52 | silent=False) 53 | request = create_request(SAMPLE_REQUEST) 54 | 55 | assert isinstance(request, ViberMessageRequest) 56 | assert request.event_type == SAMPLE_REQUEST['event'] 57 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 58 | assert request.message_token == SAMPLE_REQUEST['message_token'] 59 | assert request.sender.id == SAMPLE_REQUEST['sender']['id'] 60 | assert request.sender.name == SAMPLE_REQUEST['sender']['name'] 61 | assert isinstance(request.message, TextMessage) 62 | 63 | 64 | def test_request_is_chat_extension(): 65 | SAMPLE_REQUEST = dict( 66 | event=EventType.MESSAGE, 67 | timestamp=datetime.now(), 68 | message_token="912661846655238145", 69 | chat_id=5048086924903818307, 70 | reply_type='message', 71 | sender=dict( 72 | id="01234567890A=", 73 | name="viberUser" 74 | ), 75 | message=dict( 76 | type=MessageType.TEXT, 77 | text="HI!" 78 | ), 79 | silent=False) 80 | request = create_request(SAMPLE_REQUEST) 81 | 82 | assert isinstance(request, ViberMessageRequest) 83 | assert request.event_type == SAMPLE_REQUEST['event'] 84 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 85 | assert request.message_token == SAMPLE_REQUEST['message_token'] 86 | assert request.sender.id == SAMPLE_REQUEST['sender']['id'] 87 | assert request.sender.name == SAMPLE_REQUEST['sender']['name'] 88 | assert request.chat_id == SAMPLE_REQUEST['chat_id'] 89 | assert request.reply_type == SAMPLE_REQUEST['reply_type'] 90 | assert isinstance(request.message, TextMessage) -------------------------------------------------------------------------------- /tests/api/viber_requests/test_viber_seen_request.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.viber_requests import ViberSeenRequest 5 | from viberbot.api.viber_requests import create_request 6 | 7 | SAMPLE_REQUEST = dict( 8 | event=EventType.SEEN, 9 | timestamp=datetime.now(), 10 | message_token="912661846655238145", 11 | user_id="01234567890A=" 12 | ) 13 | 14 | 15 | def test_create_request(): 16 | request = create_request(SAMPLE_REQUEST) 17 | 18 | assert isinstance(request, ViberSeenRequest) 19 | assert request.event_type == SAMPLE_REQUEST['event'] 20 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 21 | assert request.meesage_token == SAMPLE_REQUEST['message_token'] 22 | assert request.user_id == SAMPLE_REQUEST['user_id'] 23 | -------------------------------------------------------------------------------- /tests/api/viber_requests/test_viber_subscribed_request.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.viber_requests import ViberSubscribedRequest 5 | from viberbot.api.viber_requests import create_request 6 | 7 | SAMPLE_REQUEST = dict( 8 | event=EventType.SUBSCRIBED, 9 | timestamp=datetime.now(), 10 | user=dict( 11 | id="01234567890A=", 12 | name="viberUser", 13 | avatar="http://avatar_url", 14 | country="IL", 15 | language="en" 16 | )) 17 | 18 | 19 | def test_create_request(): 20 | request = create_request(SAMPLE_REQUEST) 21 | 22 | assert isinstance(request, ViberSubscribedRequest) 23 | assert request.event_type == SAMPLE_REQUEST['event'] 24 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 25 | assert request.user.id == SAMPLE_REQUEST['user']['id'] 26 | assert request.user.name == SAMPLE_REQUEST['user']['name'] 27 | assert request.user.avatar == SAMPLE_REQUEST['user']['avatar'] 28 | assert request.user.country == SAMPLE_REQUEST['user']['country'] 29 | assert request.user.language == SAMPLE_REQUEST['user']['language'] 30 | -------------------------------------------------------------------------------- /tests/api/viber_requests/test_viber_unsubscribed_request.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.viber_requests import ViberUnsubscribedRequest 5 | from viberbot.api.viber_requests import create_request 6 | 7 | SAMPLE_REQUEST = dict( 8 | event=EventType.UNSUBSCRIBED, 9 | timestamp=datetime.now(), 10 | user_id="01234567890A=" 11 | ) 12 | 13 | 14 | def test_create_request(): 15 | request = create_request(SAMPLE_REQUEST) 16 | 17 | assert isinstance(request, ViberUnsubscribedRequest) 18 | assert request.event_type == SAMPLE_REQUEST['event'] 19 | assert request.timestamp == SAMPLE_REQUEST['timestamp'] 20 | assert request.user_id == SAMPLE_REQUEST['user_id'] 21 | -------------------------------------------------------------------------------- /viberbot/__init__.py: -------------------------------------------------------------------------------- 1 | from .api.api import Api 2 | from .api.bot_configuration import BotConfiguration 3 | from .version import __version__ 4 | 5 | __all__ = ['Api', 'BotConfiguration'] 6 | __version__ = __version__ 7 | 8 | -------------------------------------------------------------------------------- /viberbot/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-python/5cfe34e3b7d1c98afdf5d2c09a4c5722a61f008f/viberbot/api/__init__.py -------------------------------------------------------------------------------- /viberbot/api/api.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import hmac 3 | import json 4 | import logging 5 | 6 | from viberbot.api.consts import VIBER_BOT_API_URL, VIBER_BOT_USER_AGENT 7 | from viberbot.api.viber_requests import create_request 8 | from viberbot.api.api_request_sender import ApiRequestSender 9 | from viberbot.api.message_sender import MessageSender 10 | 11 | 12 | class Api(object): 13 | def __init__(self, bot_configuration): 14 | self._logger = logging.getLogger('viber.bot.api') 15 | self._bot_configuration = bot_configuration 16 | self._request_sender = ApiRequestSender(self._logger, VIBER_BOT_API_URL, bot_configuration, VIBER_BOT_USER_AGENT) 17 | self._message_sender = MessageSender(self._logger, self._request_sender, bot_configuration) 18 | 19 | @property 20 | def name(self): 21 | return self._bot_configuration.name 22 | 23 | @property 24 | def avatar(self): 25 | return self._bot_configuration.avatar 26 | 27 | def set_webhook(self, url, webhook_events=None, is_inline=False): 28 | self._logger.debug(u"setting webhook to url: {0}".format(url)) 29 | return self._request_sender.set_webhook(url, webhook_events, is_inline) 30 | 31 | def unset_webhook(self): 32 | self._logger.debug("unsetting webhook") 33 | return self._request_sender.set_webhook('') 34 | 35 | def get_online(self, ids): 36 | return self._request_sender.get_online_status(ids) 37 | 38 | def get_user_details(self, user_id): 39 | return self._request_sender.get_user_details(user_id) 40 | 41 | def get_account_info(self): 42 | self._logger.debug("requesting account info") 43 | account_info = self._request_sender.get_account_info() 44 | self._logger.debug(u"received account info: {0}".format(account_info)) 45 | return account_info 46 | 47 | def verify_signature(self, request_data, signature): 48 | return signature == self._calculate_message_signature(request_data) 49 | 50 | def parse_request(self, request_data): 51 | self._logger.debug("parsing request") 52 | request_dict = json.loads(request_data.decode() if isinstance( 53 | request_data, bytes) else request_data) 54 | request = create_request(request_dict) 55 | self._logger.debug(u"parsed request={0}".format(request)) 56 | return request 57 | 58 | def send_messages(self, to, messages, chat_id=None): 59 | """ 60 | :param to: Viber user id 61 | :param messages: list of Message objects to be sent 62 | :param chat_id: Optional. String. Indicates that this is a message sent in inline conversation. 63 | :return: list of tokens of the sent messages 64 | """ 65 | self._logger.debug("going to send messages: {0}, to: {1}".format(messages, to)) 66 | if not isinstance(messages, list): 67 | messages = [messages] 68 | 69 | sent_messages_tokens = [] 70 | 71 | for message in messages: 72 | token = self._message_sender.send_message( 73 | to, self._bot_configuration.name, self._bot_configuration.avatar, message, chat_id) 74 | sent_messages_tokens.append(token) 75 | 76 | return sent_messages_tokens 77 | 78 | def post_messages_to_public_account(self, sender, messages): 79 | if not isinstance(messages, list): 80 | messages = [messages] 81 | 82 | sent_messages_tokens = [] 83 | 84 | for message in messages: 85 | token = self._message_sender.post_to_public_account( 86 | sender, self._bot_configuration.name, self._bot_configuration.avatar, message) 87 | sent_messages_tokens.append(token) 88 | 89 | return sent_messages_tokens 90 | 91 | def _calculate_message_signature(self, message): 92 | return hmac.new( 93 | bytes(self._bot_configuration.auth_token.encode('ascii')), 94 | msg=message, 95 | digestmod=hashlib.sha256)\ 96 | .hexdigest() 97 | -------------------------------------------------------------------------------- /viberbot/api/api_request_sender.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests import RequestException 3 | import traceback 4 | from viberbot.api.consts import BOT_API_ENDPOINT 5 | import json 6 | 7 | 8 | class ApiRequestSender(object): 9 | def __init__(self, logger, viber_bot_api_url, bot_configuration, viber_bot_user_agent): 10 | self._logger = logger 11 | self._viber_bot_api_url = viber_bot_api_url 12 | self._bot_configuration = bot_configuration 13 | self._user_agent = viber_bot_user_agent 14 | 15 | def set_webhook(self, url, webhook_events=None, is_inline=False): 16 | payload = { 17 | 'auth_token': self._bot_configuration.auth_token, 18 | 'url': url, 19 | 'is_inline': is_inline 20 | } 21 | 22 | if webhook_events is not None: 23 | if not isinstance(webhook_events, list): 24 | webhook_events = [webhook_events] 25 | payload['event_types'] = webhook_events 26 | 27 | result = self.post_request( 28 | endpoint=BOT_API_ENDPOINT.SET_WEBHOOK, 29 | payload=json.dumps(payload)) 30 | 31 | if not result['status'] == 0: 32 | raise Exception(u"failed with status: {0}, message: {1}".format(result['status'], result['status_message'])) 33 | 34 | return result['event_types'] 35 | 36 | def get_account_info(self): 37 | payload = { 38 | 'auth_token': self._bot_configuration.auth_token 39 | } 40 | return self.post_request( 41 | endpoint=BOT_API_ENDPOINT.GET_ACCOUNT_INFO, 42 | payload=json.dumps(payload)) 43 | 44 | def post_request(self, endpoint, payload): 45 | try: 46 | headers = requests.utils.default_headers() 47 | headers.update({ 48 | 'User-Agent': self._user_agent 49 | }) 50 | response = requests.post(self._viber_bot_api_url + '/' + endpoint, data=payload, headers=headers) 51 | response.raise_for_status() 52 | return json.loads(response.text) 53 | except RequestException as e: 54 | self._logger.error( 55 | u"failed to post request to endpoint={0}, with payload={1}. error is: {2}" 56 | .format(endpoint, payload, traceback.format_exc())) 57 | raise e 58 | except Exception as ex: 59 | self._logger.error( 60 | u"unexpected Exception while trying to post request. error is: {0}" 61 | .format(traceback.format_exc())) 62 | raise ex 63 | 64 | def get_online_status(self, ids=[]): 65 | if ids is None or not isinstance(ids, list) or len(ids) == 0: 66 | raise Exception(u"missing parameter ids, should be a list of viber memberIds") 67 | 68 | payload = { 69 | 'auth_token': self._bot_configuration.auth_token, 70 | 'ids': ids 71 | } 72 | result = self.post_request( 73 | endpoint=BOT_API_ENDPOINT.GET_ONLINE, 74 | payload=json.dumps(payload)) 75 | 76 | if not result['status'] == 0: 77 | raise Exception(u"failed with status: {0}, message: {1}".format(result['status'], result['status_message'])) 78 | 79 | return result['users'] 80 | 81 | def get_user_details(self, user_id): 82 | if user_id is None: 83 | raise Exception(u"missing parameter id") 84 | 85 | payload = { 86 | 'auth_token': self._bot_configuration.auth_token, 87 | 'id': user_id 88 | } 89 | result = self.post_request( 90 | endpoint=BOT_API_ENDPOINT.GET_USER_DETAILS, 91 | payload=json.dumps(payload)) 92 | 93 | if not result['status'] == 0: 94 | raise Exception(u"failed with status: {0}, message: {1}".format(result['status'], result['status_message'])) 95 | 96 | return result['user'] 97 | 98 | 99 | -------------------------------------------------------------------------------- /viberbot/api/bot_configuration.py: -------------------------------------------------------------------------------- 1 | class BotConfiguration(object): 2 | def __init__(self, auth_token, name, avatar): 3 | self._auth_token = auth_token 4 | self._name = name 5 | self._avatar = avatar 6 | 7 | @property 8 | def name(self): 9 | return self._name 10 | 11 | @property 12 | def avatar(self): 13 | return self._avatar 14 | 15 | @property 16 | def auth_token(self): 17 | return self._auth_token 18 | -------------------------------------------------------------------------------- /viberbot/api/consts.py: -------------------------------------------------------------------------------- 1 | from ..version import __version__ 2 | 3 | VIBER_BOT_API_URL = "https://chatapi.viber.com/pa" 4 | VIBER_BOT_USER_AGENT = "ViberBot-Python/" + __version__ 5 | 6 | 7 | class BOT_API_ENDPOINT(object): 8 | SET_WEBHOOK = 'set_webhook' 9 | GET_ACCOUNT_INFO = 'get_account_info' 10 | SEND_MESSAGE = 'send_message' 11 | GET_ONLINE = 'get_online' 12 | GET_USER_DETAILS = 'get_user_details' 13 | POST = 'post' 14 | -------------------------------------------------------------------------------- /viberbot/api/event_type.py: -------------------------------------------------------------------------------- 1 | class EventType(object): 2 | SEEN = 'seen' 3 | CONVERSATION_STARTED = 'conversation_started' 4 | DELIVERED = 'delivered' 5 | MESSAGE = 'message' 6 | SUBSCRIBED = 'subscribed' 7 | UNSUBSCRIBED = 'unsubscribed' 8 | FAILED = 'failed' 9 | WEBHOOK = 'webhook' 10 | -------------------------------------------------------------------------------- /viberbot/api/message_sender.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from viberbot.api.consts import BOT_API_ENDPOINT 4 | 5 | 6 | class MessageSender(object): 7 | def __init__(self, logger, request_sender, bot_configuration): 8 | self._logger = logger 9 | self._request_sender = request_sender 10 | self._bot_configuration = bot_configuration 11 | 12 | def send_message(self, to, sender_name, sender_avatar, message, chat_id=None): 13 | if not message.validate(): 14 | self._logger.error(u"failed validating message: {0}".format(message)) 15 | raise Exception("failed validating message: {0}".format(message)) 16 | 17 | payload = self._prepare_payload( 18 | message=message, 19 | receiver=to, 20 | sender_name=sender_name, 21 | sender_avatar=sender_avatar, 22 | chat_id=chat_id 23 | ) 24 | 25 | self._logger.debug(u"going to send message: {0}".format(payload)) 26 | 27 | return self._post_request(BOT_API_ENDPOINT.SEND_MESSAGE, payload) 28 | 29 | def post_to_public_account(self, sender, sender_name, sender_avatar, message): 30 | if not message.validate(): 31 | self._logger.error(u"failed validating message: {0}".format(message)) 32 | raise Exception("failed validating message: {0}".format(message)) 33 | 34 | if sender is None: 35 | raise Exception(u"missing parameter sender") 36 | 37 | payload = self._prepare_payload( 38 | message=message, 39 | sender=sender, 40 | sender_name=sender_name, 41 | sender_avatar=sender_avatar 42 | ) 43 | 44 | self._logger.debug(u"going to send message: {0}".format(payload)) 45 | 46 | return self._post_request(BOT_API_ENDPOINT.POST, payload) 47 | 48 | def _post_request(self, endpoint, payload): 49 | result = self._request_sender.post_request( 50 | endpoint, json.dumps(payload)) 51 | 52 | if not result['status'] == 0: 53 | raise Exception(u"failed with status: {0}, message: {1}".format(result['status'], result['status_message'])) 54 | 55 | return result['message_token'] 56 | 57 | def _prepare_payload(self, message, sender_name, sender_avatar, sender=None, receiver=None, chat_id=None): 58 | payload = message.to_dict() 59 | payload.update({ 60 | 'auth_token': self._bot_configuration.auth_token, 61 | 'from': sender, 62 | 'receiver': receiver, 63 | 'sender': { 64 | 'name': sender_name, 65 | 'avatar': sender_avatar 66 | }, 67 | "chat_id": chat_id 68 | }) 69 | 70 | return self._remove_empty_fields(payload) 71 | 72 | def _remove_empty_fields(self, message): 73 | return {k: v for k, v in message.items() if v is not None} 74 | -------------------------------------------------------------------------------- /viberbot/api/messages/__init__.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.messages.contact_message import ContactMessage 2 | from viberbot.api.messages.file_message import FileMessage 3 | from viberbot.api.messages.picture_message import PictureMessage 4 | from viberbot.api.messages.sticker_message import StickerMessage 5 | from viberbot.api.messages.url_message import URLMessage 6 | from viberbot.api.messages.video_message import VideoMessage 7 | from viberbot.api.messages.message_type import MessageType 8 | from viberbot.api.messages.text_message import TextMessage 9 | from viberbot.api.messages.location_message import LocationMessage 10 | from viberbot.api.messages.rich_media_message import RichMediaMessage 11 | from viberbot.api.messages.keyboard_message import KeyboardMessage 12 | 13 | MESSAGE_TYPE_TO_CLASS = { 14 | MessageType.URL: URLMessage, 15 | MessageType.LOCATION: LocationMessage, 16 | MessageType.PICTURE: PictureMessage, 17 | MessageType.CONTACT: ContactMessage, 18 | MessageType.FILE: FileMessage, 19 | MessageType.TEXT: TextMessage, 20 | MessageType.VIDEO: VideoMessage, 21 | MessageType.STICKER: StickerMessage, 22 | MessageType.RICH_MEDIA: RichMediaMessage, 23 | MessageType.KEYBOARD: KeyboardMessage 24 | } 25 | 26 | 27 | def get_message(message_dict): 28 | if 'type' not in message_dict: 29 | raise Exception("message data doesn't contain a type") 30 | 31 | if message_dict['type'] not in MESSAGE_TYPE_TO_CLASS: 32 | raise Exception(u"message type '{0}' is not supported".format(message_dict['type'])) 33 | 34 | return MESSAGE_TYPE_TO_CLASS[message_dict['type']]().from_dict(message_dict) 35 | 36 | 37 | __all__ = [ 38 | 'TextMessage', 'ContactMessage', 'FileMessage', 'LocationMessage', 39 | 'PictureMessage', 'StickerMessage', 'URLMessage', 'VideoMessage', 40 | 'RichMediaMessage', 'MessageType', 'KeyboardMessage'] 41 | -------------------------------------------------------------------------------- /viberbot/api/messages/contact_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.data_types.contact import Contact 3 | from viberbot.api.messages.typed_message import TypedMessage 4 | from viberbot.api.messages.message_type import MessageType 5 | 6 | 7 | class ContactMessage(TypedMessage): 8 | def __init__(self, tracking_data=None, keyboard=None, contact=None, min_api_version=None): 9 | super(ContactMessage, self).__init__(MessageType.CONTACT, tracking_data, keyboard, min_api_version) 10 | self._contact = contact 11 | 12 | def to_dict(self): 13 | message_data = super(ContactMessage, self).to_dict() 14 | if self._contact is not None: 15 | message_data['contact'] = self._contact.to_dict() 16 | return message_data 17 | 18 | def from_dict(self, message_data): 19 | super(ContactMessage, self).from_dict(message_data) 20 | if 'contact' in message_data: 21 | self._contact = Contact().from_dict(message_data['contact']) 22 | return self 23 | 24 | @property 25 | def contact(self): 26 | return self._contact 27 | 28 | def validate(self): 29 | return super(ContactMessage, self).validate() \ 30 | and self._contact is not None \ 31 | and self._contact.name is not None \ 32 | and self._contact.phone_number is not None 33 | 34 | @python_2_unicode_compatible 35 | def __str__(self): 36 | return u"ContactMessage [{0}, contact={1}]". \ 37 | format( 38 | super(ContactMessage, self).__str__(), 39 | self._contact) 40 | -------------------------------------------------------------------------------- /viberbot/api/messages/data_types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viber/viber-bot-python/5cfe34e3b7d1c98afdf5d2c09a4c5722a61f008f/viberbot/api/messages/data_types/__init__.py -------------------------------------------------------------------------------- /viberbot/api/messages/data_types/contact.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | 3 | 4 | class Contact(object): 5 | def __init__(self, name=None, phone_number=None, avatar=None): 6 | self._name = name 7 | self._phone_number = phone_number 8 | self._avatar = avatar 9 | 10 | def from_dict(self, contact): 11 | if 'name' in contact: 12 | self._name = contact['name'] 13 | if 'phone_number' in contact: 14 | self._phone_number = contact['phone_number'] 15 | if 'avatar' in contact: 16 | self._avatar = contact['avatar'] 17 | return self 18 | 19 | def to_dict(self): 20 | return { 21 | 'name': self._name, 22 | 'phone_number': self._phone_number, 23 | 'avatar': self._avatar 24 | } 25 | 26 | @property 27 | def name(self): 28 | return self._name 29 | 30 | @property 31 | def phone_number(self): 32 | return self._phone_number 33 | 34 | def __eq__(self, other): 35 | return self._name == other.name and self._phone_number == other.phone_number 36 | 37 | @python_2_unicode_compatible 38 | def __str__(self): 39 | return u"Contact[name={0}, phone_number={1}, avatar={2}]".format(self._name, self._phone_number, self._avatar) 40 | -------------------------------------------------------------------------------- /viberbot/api/messages/data_types/location.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | 3 | 4 | class LocationConsts(object): 5 | MAX_LONGITUDE = 180 6 | MIN_LONGITUDE = -180 7 | MAX_LATITUDE = 90 8 | MIN_LATITUDE = -90 9 | 10 | 11 | class Location(object): 12 | def __init__(self, lat=None, lon=None): 13 | self._lat = lat 14 | self._lon = lon 15 | 16 | def from_dict(self, location): 17 | if 'lat' in location: 18 | self._lat = location['lat'] 19 | if 'lon' in location: 20 | self._lon = location['lon'] 21 | return self 22 | 23 | def to_dict(self): 24 | return { 25 | 'lat': self._lat, 26 | 'lon': self._lon 27 | } 28 | 29 | @property 30 | def latitude(self): 31 | return self._lat 32 | 33 | @property 34 | def longitude(self): 35 | return self._lon 36 | 37 | def validate(self): 38 | if self._lat is None or self._lon is None: 39 | return False 40 | if self._lat >= LocationConsts.MAX_LATITUDE or self._lat <= LocationConsts.MIN_LATITUDE: 41 | return False 42 | if self._lon >= LocationConsts.MAX_LONGITUDE or self._lon <= LocationConsts.MIN_LONGITUDE: 43 | return False 44 | return True 45 | 46 | def __eq__(self, other): 47 | return self._lat == other.latitude and self._lon == other.longitude 48 | 49 | @python_2_unicode_compatible 50 | def __str__(self): 51 | return u"Location[lat={0}, lon={1}]".format(self._lat, self._lon) 52 | -------------------------------------------------------------------------------- /viberbot/api/messages/file_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.typed_message import TypedMessage 3 | from viberbot.api.messages.message_type import MessageType 4 | 5 | 6 | class FileMessage(TypedMessage): 7 | def __init__(self, tracking_data=None, keyboard=None, media=None, size=None, file_name=None, min_api_version=None): 8 | super(FileMessage, self).__init__(MessageType.FILE, tracking_data, keyboard, min_api_version) 9 | self._media = media 10 | self._size = size 11 | self._file_name = file_name 12 | 13 | def to_dict(self): 14 | message_data = super(FileMessage, self).to_dict() 15 | message_data['media'] = self._media 16 | message_data['size'] = self._size 17 | message_data['file_name'] = self._file_name 18 | return message_data 19 | 20 | def from_dict(self, message_data): 21 | super(FileMessage, self).from_dict(message_data) 22 | if 'media' in message_data: 23 | self._media = message_data['media'] 24 | if 'size' in message_data: 25 | self._size = message_data['size'] 26 | if 'file_name' in message_data: 27 | self._file_name = message_data['file_name'] 28 | return self 29 | 30 | @property 31 | def media(self): 32 | return self._media 33 | 34 | @property 35 | def size(self): 36 | return self._size 37 | 38 | @property 39 | def file_name(self): 40 | return self._file_name 41 | 42 | def validate(self): 43 | return super(FileMessage, self).validate() \ 44 | and self._media is not None \ 45 | and self._size is not None \ 46 | and self._file_name is not None 47 | 48 | @python_2_unicode_compatible 49 | def __str__(self): 50 | return u"FileMessage [{0}, media={1}, size={2}, file_name={3}]". \ 51 | format( 52 | super(FileMessage, self).__str__(), 53 | self._media, 54 | self._size, 55 | self._file_name) 56 | -------------------------------------------------------------------------------- /viberbot/api/messages/keyboard_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.message import Message 3 | 4 | 5 | class KeyboardMessage(Message): 6 | def __init__(self, tracking_data=None, keyboard=None, min_api_version=None): 7 | super(KeyboardMessage, self).__init__(tracking_data, keyboard, min_api_version) 8 | 9 | def to_dict(self): 10 | return super(KeyboardMessage, self).to_dict() 11 | 12 | def from_dict(self, message_data): 13 | super(KeyboardMessage, self).from_dict(message_data) 14 | return self 15 | 16 | def validate(self): 17 | return self._keyboard is not None 18 | 19 | @python_2_unicode_compatible 20 | def __str__(self): 21 | return u"KeyboardMessage [{0}]".format(super(KeyboardMessage, self).__str__()) 22 | -------------------------------------------------------------------------------- /viberbot/api/messages/location_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.data_types.location import Location 3 | from viberbot.api.messages.typed_message import TypedMessage 4 | from viberbot.api.messages.message_type import MessageType 5 | 6 | 7 | class LocationMessage(TypedMessage): 8 | def __init__(self, tracking_data=None, keyboard=None, location=None, min_api_version=None): 9 | super(LocationMessage, self).__init__(MessageType.LOCATION, tracking_data, keyboard, min_api_version) 10 | self._location = location 11 | 12 | def to_dict(self): 13 | message_data = super(LocationMessage, self).to_dict() 14 | if self._location is not None: 15 | message_data['location'] = self._location.to_dict() 16 | return message_data 17 | 18 | def from_dict(self, message_data): 19 | super(LocationMessage, self).from_dict(message_data) 20 | if 'location' in message_data: 21 | self._location = Location().from_dict(message_data['location']) 22 | return self 23 | 24 | @property 25 | def location(self): 26 | return self._location 27 | 28 | def validate(self): 29 | return super(LocationMessage, self).validate() \ 30 | and self._location and self._location.validate() 31 | 32 | @python_2_unicode_compatible 33 | def __str__(self): 34 | return u"LocationMessage [{0}, contact={1}]"\ 35 | .format( 36 | super(LocationMessage, self).__str__(), 37 | self._location) 38 | -------------------------------------------------------------------------------- /viberbot/api/messages/message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from abc import abstractmethod 3 | 4 | 5 | class Message(object): 6 | def __init__(self, tracking_data=None, keyboard=None, min_api_version=None, alt_text=None): 7 | self._tracking_data = tracking_data 8 | self._keyboard = keyboard 9 | self._min_api_version = min_api_version 10 | self._alt_text = alt_text 11 | 12 | @abstractmethod 13 | def to_dict(self): 14 | message_data = {} 15 | if self._tracking_data: 16 | message_data['tracking_data'] = self._tracking_data 17 | if self._keyboard: 18 | message_data['keyboard'] = self._keyboard 19 | if self._min_api_version: 20 | message_data['min_api_version'] = self._min_api_version 21 | if self._alt_text: 22 | message_data['alt_text'] = self._alt_text 23 | return message_data 24 | 25 | @abstractmethod 26 | def from_dict(self, message_data): 27 | if 'tracking_data' in message_data: 28 | self._tracking_data = message_data['tracking_data'] 29 | if 'keyboard' in message_data: 30 | self._keyboard = message_data['keyboard'] 31 | if 'min_api_version' in message_data: 32 | self._min_api_version = message_data['min_api_version'] 33 | return self 34 | 35 | @property 36 | def keyboard(self): 37 | return self._keyboard 38 | 39 | @property 40 | def tracking_data(self): 41 | return self._tracking_data 42 | 43 | @property 44 | def min_api_version(self): 45 | return self._min_api_version 46 | 47 | @abstractmethod 48 | def validate(self): 49 | """ 50 | validates message has all the required fields before send 51 | """ 52 | pass 53 | 54 | @python_2_unicode_compatible 55 | def __str__(self): 56 | return u"tracking_data={0}, keyboard={1}, min_api_version={2}"\ 57 | .format( 58 | self._tracking_data, 59 | self._keyboard, 60 | self._min_api_version) 61 | -------------------------------------------------------------------------------- /viberbot/api/messages/message_type.py: -------------------------------------------------------------------------------- 1 | class MessageType(object): 2 | RICH_MEDIA = 'rich_media' 3 | STICKER = 'sticker' 4 | URL = 'url' 5 | LOCATION = 'location' 6 | CONTACT = 'contact' 7 | FILE = 'file' 8 | TEXT = 'text' 9 | PICTURE = 'picture' 10 | VIDEO = 'video' 11 | KEYBOARD = 'keyboard' 12 | -------------------------------------------------------------------------------- /viberbot/api/messages/picture_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.typed_message import TypedMessage 3 | from viberbot.api.messages.message_type import MessageType 4 | 5 | 6 | class PictureMessage(TypedMessage): 7 | def __init__(self, tracking_data=None, keyboard=None, text=None, media=None, thumbnail=None, min_api_version=None): 8 | super(PictureMessage, self).__init__(MessageType.PICTURE, tracking_data, keyboard, min_api_version) 9 | self._text = text or '' 10 | self._media = media 11 | self._thumbnail = thumbnail 12 | 13 | def to_dict(self): 14 | message_data = super(PictureMessage, self).to_dict() 15 | message_data['text'] = self._text 16 | message_data['media'] = self._media 17 | message_data['thumbnail'] = self._thumbnail 18 | return message_data 19 | 20 | def from_dict(self, message_data): 21 | super(PictureMessage, self).from_dict(message_data) 22 | if 'text' in message_data: 23 | self._text = message_data['text'] or '' 24 | if 'media' in message_data: 25 | self._media = message_data['media'] 26 | if 'thumbnail' in message_data: 27 | self._thumbnail = message_data['thumbnail'] 28 | return self 29 | 30 | def validate(self): 31 | return super(PictureMessage, self).validate() \ 32 | and self._text is not None and self._media is not None 33 | 34 | @property 35 | def text(self): 36 | return self._text 37 | 38 | @property 39 | def media(self): 40 | return self._media 41 | 42 | @property 43 | def thumbnail(self): 44 | return self._thumbnail 45 | 46 | @python_2_unicode_compatible 47 | def __str__(self): 48 | return u"PictureMessage [{0}, text={1}, media={2}, thumbnail={3}]"\ 49 | .format( 50 | super(PictureMessage, self).__str__(), 51 | self._text, 52 | self._media, 53 | self._thumbnail) 54 | -------------------------------------------------------------------------------- /viberbot/api/messages/rich_media_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.typed_message import TypedMessage 3 | from viberbot.api.messages.message_type import MessageType 4 | 5 | 6 | class RichMediaMessage(TypedMessage): 7 | def __init__(self, tracking_data=None, keyboard=None, rich_media=None, min_api_version=None, alt_text=None): 8 | super(RichMediaMessage, self).__init__(MessageType.RICH_MEDIA, tracking_data, keyboard, min_api_version) 9 | self._rich_media = rich_media 10 | self._alt_text = alt_text 11 | 12 | def to_dict(self): 13 | message_data = super(RichMediaMessage, self).to_dict() 14 | message_data['rich_media'] = self._rich_media 15 | message_data['alt_text'] = self._alt_text 16 | return message_data 17 | 18 | def from_dict(self, message_data): 19 | super(RichMediaMessage, self).from_dict(message_data) 20 | if 'rich_media' in message_data: 21 | self._rich_media = message_data['rich_media'] 22 | if 'alt_text' in message_data: 23 | self._alt_text = message_data['alt_text'] 24 | return self 25 | 26 | def validate(self): 27 | return super(RichMediaMessage, self).validate() \ 28 | and self._rich_media is not None 29 | 30 | @property 31 | def rich_media(self): 32 | return self._rich_media 33 | 34 | @property 35 | def alt_text(self): 36 | return self._alt_text 37 | 38 | @python_2_unicode_compatible 39 | def __str__(self): 40 | return u"RichMediaMessage [{0}, rich_media={1}]"\ 41 | .format( 42 | super(RichMediaMessage, self).__str__(), 43 | self._rich_media, 44 | self._alt_text) 45 | -------------------------------------------------------------------------------- /viberbot/api/messages/sticker_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.typed_message import TypedMessage 3 | from viberbot.api.messages.message_type import MessageType 4 | 5 | 6 | class StickerMessage(TypedMessage): 7 | def __init__(self, tracking_data=None, keyboard=None, sticker_id=None, min_api_version=None): 8 | super(StickerMessage, self).__init__(MessageType.STICKER, tracking_data, keyboard, min_api_version) 9 | self._sticker_id = sticker_id 10 | 11 | def to_dict(self): 12 | message_data = super(StickerMessage, self).to_dict() 13 | message_data['sticker_id'] = self._sticker_id 14 | return message_data 15 | 16 | def from_dict(self, message_data): 17 | super(StickerMessage, self).from_dict(message_data) 18 | if 'sticker_id' in message_data: 19 | self._sticker_id = message_data['sticker_id'] 20 | return self 21 | 22 | @property 23 | def sticker_id(self): 24 | return self._sticker_id 25 | 26 | def validate(self): 27 | return super(StickerMessage, self).validate() \ 28 | and self._sticker_id is not None 29 | 30 | @python_2_unicode_compatible 31 | def __str__(self): 32 | return u"StickerMessage [{0}, sticker_id={1}]".format(super(StickerMessage, self).__str__(), self._sticker_id) 33 | -------------------------------------------------------------------------------- /viberbot/api/messages/text_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.typed_message import TypedMessage 3 | from viberbot.api.messages.message_type import MessageType 4 | 5 | 6 | class TextMessage(TypedMessage): 7 | def __init__(self, tracking_data=None, keyboard=None, text=None, min_api_version=None): 8 | super(TextMessage, self).__init__(MessageType.TEXT, tracking_data, keyboard, min_api_version) 9 | self._text = text 10 | 11 | def to_dict(self): 12 | message_data = super(TextMessage, self).to_dict() 13 | message_data['text'] = self._text 14 | return message_data 15 | 16 | def from_dict(self, message_data): 17 | super(TextMessage, self).from_dict(message_data) 18 | if 'text' in message_data: 19 | self._text = message_data['text'] 20 | return self 21 | 22 | def validate(self): 23 | return super(TextMessage, self).validate() \ 24 | and self._text is not None 25 | 26 | @property 27 | def text(self): 28 | return self._text 29 | 30 | @python_2_unicode_compatible 31 | def __str__(self): 32 | return u"TextMessage [{0}, text={1}]".format(super(TextMessage, self).__str__(), self._text) 33 | -------------------------------------------------------------------------------- /viberbot/api/messages/typed_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from abc import abstractmethod 3 | from viberbot.api.messages.message import Message 4 | 5 | 6 | class TypedMessage(Message): 7 | def __init__(self, message_type, tracking_data=None, keyboard=None, min_api_version=None, alt_text=None): 8 | super(TypedMessage, self).__init__(tracking_data, keyboard, min_api_version, alt_text) 9 | self._message_type = message_type 10 | 11 | @abstractmethod 12 | def to_dict(self): 13 | message_data = super(TypedMessage, self).to_dict() 14 | message_data['type'] = self._message_type 15 | return message_data 16 | 17 | @abstractmethod 18 | def from_dict(self, message_data): 19 | super(TypedMessage, self).from_dict(message_data) 20 | return self 21 | 22 | @abstractmethod 23 | def validate(self): 24 | """ 25 | validates message has all the required fields before send 26 | """ 27 | return self._message_type is not None 28 | 29 | @python_2_unicode_compatible 30 | def __str__(self): 31 | return super(TypedMessage, self).__str__() 32 | -------------------------------------------------------------------------------- /viberbot/api/messages/url_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.typed_message import TypedMessage 3 | from viberbot.api.messages.message_type import MessageType 4 | 5 | 6 | class URLMessage(TypedMessage): 7 | def __init__(self, tracking_data=None, keyboard=None, media=None, min_api_version=None): 8 | super(URLMessage, self).__init__(MessageType.URL, tracking_data, keyboard, min_api_version) 9 | self._media = media 10 | 11 | def to_dict(self): 12 | message_data = super(URLMessage, self).to_dict() 13 | message_data['media'] = self._media 14 | return message_data 15 | 16 | def from_dict(self, message_data): 17 | super(URLMessage, self).from_dict(message_data) 18 | if 'media' in message_data: 19 | self._media = message_data['media'] 20 | return self 21 | 22 | @property 23 | def media(self): 24 | return self._media 25 | 26 | def validate(self): 27 | return super(URLMessage, self).validate() \ 28 | and self._media is not None 29 | 30 | @python_2_unicode_compatible 31 | def __str__(self): 32 | return u"URLMessage [{0}, media={1}]".format(super(URLMessage, self).__str__(), self._media) 33 | -------------------------------------------------------------------------------- /viberbot/api/messages/video_message.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.messages.typed_message import TypedMessage 3 | from viberbot.api.messages.message_type import MessageType 4 | 5 | 6 | class VideoMessage(TypedMessage): 7 | def __init__(self, tracking_data=None, keyboard=None, media=None, thumbnail=None, size=None, text=None, duration=None, min_api_version=None): 8 | super(VideoMessage, self).__init__(MessageType.VIDEO, tracking_data, keyboard, min_api_version) 9 | self._media = media 10 | self._thumbnail = thumbnail 11 | self._size = size 12 | self._duration = duration 13 | self._text = text 14 | 15 | def to_dict(self): 16 | message_data = super(VideoMessage, self).to_dict() 17 | message_data['media'] = self._media 18 | message_data['thumbnail'] = self._thumbnail 19 | message_data['size'] = self._size 20 | message_data['duration'] = self._duration 21 | message_data['text'] = self._text 22 | return message_data 23 | 24 | def from_dict(self, message_data): 25 | super(VideoMessage, self).from_dict(message_data) 26 | if 'media' in message_data: 27 | self._media = message_data['media'] 28 | if 'thumbnail' in message_data: 29 | self._thumbnail = message_data['thumbnail'] 30 | if 'size' in message_data: 31 | self._size = message_data['size'] 32 | if 'duration' in message_data: 33 | self._duration = message_data['duration'] 34 | if 'text' in message_data: 35 | self._text = message_data['text'] 36 | return self 37 | 38 | def validate(self): 39 | return super(VideoMessage, self).validate() \ 40 | and self._media is not None \ 41 | and self._size is not None 42 | 43 | @property 44 | def media(self): 45 | return self._media 46 | 47 | @property 48 | def thumbnail(self): 49 | return self._thumbnail 50 | 51 | @property 52 | def size(self): 53 | return self._size 54 | 55 | @property 56 | def duration(self): 57 | return self._duration 58 | 59 | @property 60 | def text(self): 61 | return self._text 62 | 63 | @python_2_unicode_compatible 64 | def __str__(self): 65 | return u"VideoMessage [{0}, media={1}, thumbnail={2}, size={3}, duration={4} text={5}]".\ 66 | format( 67 | super(VideoMessage, self).__str__(), 68 | self._media, 69 | self._thumbnail, 70 | self._size, 71 | self._duration, 72 | self._text) 73 | -------------------------------------------------------------------------------- /viberbot/api/user_profile.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | 3 | 4 | class UserProfile(object): 5 | def __init__(self, name=None, avatar=None, user_id=None, country=None, language=None, api_version=None): 6 | self._name = name 7 | self._avatar = avatar 8 | self._id = user_id 9 | self._country = country 10 | self._language = language 11 | self._api_version = api_version 12 | 13 | @property 14 | def name(self): 15 | return self._name 16 | 17 | @property 18 | def avatar(self): 19 | return self._avatar 20 | 21 | @property 22 | def id(self): 23 | return self._id 24 | 25 | @property 26 | def country(self): 27 | return self._country 28 | 29 | @property 30 | def language(self): 31 | return self._language 32 | 33 | @property 34 | def api_version(self): 35 | return self._api_version 36 | 37 | def from_dict(self, user_dict): 38 | if 'name' in user_dict: 39 | self._name = user_dict['name'] 40 | if 'avatar' in user_dict: 41 | self._avatar = user_dict['avatar'] 42 | if 'id' in user_dict: 43 | self._id = user_dict['id'] 44 | if 'country' in user_dict: 45 | self._country = user_dict['country'] 46 | if 'language' in user_dict: 47 | self._language = user_dict['language'] 48 | if 'api_version' in user_dict: 49 | self._api_version = user_dict['api_version'] 50 | return self 51 | 52 | @python_2_unicode_compatible 53 | def __str__(self): 54 | return u"UserProfile[name={0}, avatar={1}, id={2}, country={3}, language={4}, api_version={5}"\ 55 | .format( 56 | self._name, 57 | self._avatar, 58 | self._id, 59 | self._country, 60 | self._language, 61 | self._api_version) 62 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/__init__.py: -------------------------------------------------------------------------------- 1 | from viberbot.api.event_type import EventType 2 | from viberbot.api.viber_requests.viber_conversation_started_request import ViberConversationStartedRequest 3 | from viberbot.api.viber_requests.viber_delivered_request import ViberDeliveredRequest 4 | from viberbot.api.viber_requests.viber_failed_request import ViberFailedRequest 5 | from viberbot.api.viber_requests.viber_message_request import ViberMessageRequest 6 | from viberbot.api.viber_requests.viber_request import ViberRequest 7 | from viberbot.api.viber_requests.viber_seen_request import ViberSeenRequest 8 | from viberbot.api.viber_requests.viber_subscribed_request import ViberSubscribedRequest 9 | from viberbot.api.viber_requests.viber_unsubscribed_request import ViberUnsubscribedRequest 10 | 11 | EVENT_TYPE_TO_CLASS = { 12 | EventType.MESSAGE: ViberMessageRequest, 13 | EventType.FAILED: ViberFailedRequest, 14 | EventType.CONVERSATION_STARTED: ViberConversationStartedRequest, 15 | EventType.DELIVERED: ViberDeliveredRequest, 16 | EventType.SEEN: ViberSeenRequest, 17 | EventType.SUBSCRIBED: ViberSubscribedRequest, 18 | EventType.UNSUBSCRIBED: ViberUnsubscribedRequest, 19 | EventType.WEBHOOK: ViberRequest 20 | } 21 | 22 | 23 | def create_request(request_dict): 24 | if 'event' not in request_dict: 25 | raise Exception("request is missing field 'event'") 26 | 27 | if request_dict['event'] not in EVENT_TYPE_TO_CLASS: 28 | raise Exception("event type '{0}' is not supported".format(request_dict['event'])) 29 | 30 | return EVENT_TYPE_TO_CLASS[request_dict['event']]().from_dict(request_dict) 31 | 32 | 33 | __all__ = [ 34 | 'ViberConversationStartedRequest', 'ViberDeliveredRequest', 'ViberFailedRequest', 'ViberMessageRequest', 35 | 'ViberSeenRequest', 'ViberSubscribedRequest', 'ViberUnsubscribedRequest'] 36 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/viber_conversation_started_request.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.event_type import EventType 3 | from viberbot.api.user_profile import UserProfile 4 | from viberbot.api.viber_requests.viber_request import ViberRequest 5 | 6 | 7 | class ViberConversationStartedRequest(ViberRequest): 8 | def __init__(self): 9 | super(ViberConversationStartedRequest, self).__init__(EventType.CONVERSATION_STARTED) 10 | self._message_token = None 11 | self._type = None 12 | self._context = None 13 | self._user = None 14 | self._api_version = None 15 | self._subscribed = None 16 | 17 | def from_dict(self, request_dict): 18 | super(ViberConversationStartedRequest, self).from_dict(request_dict) 19 | self._message_token = request_dict['message_token'] 20 | self._type = request_dict['type'] 21 | if 'context' in request_dict: 22 | self._context = request_dict['context'] 23 | self._user = UserProfile().from_dict(request_dict['user']) 24 | if 'api_version' in request_dict: 25 | self._api_version = request_dict['api_version'] 26 | if 'subscribed' in request_dict: 27 | self._subscribed = request_dict['subscribed'] 28 | return self 29 | 30 | @property 31 | def user(self): 32 | return self._user 33 | 34 | @property 35 | def type(self): 36 | return self._type 37 | 38 | @property 39 | def context(self): 40 | return self._context 41 | 42 | @property 43 | def message_token(self): 44 | return self._message_token 45 | 46 | @property 47 | def api_version(self): 48 | return self._api_version 49 | 50 | @property 51 | def subscribed(self): 52 | return self._subscribed 53 | 54 | @python_2_unicode_compatible 55 | def __str__(self): 56 | return u"ViberConversationStartedRequest [{0}, message_token={1}, type={2}, context{3}, user={4} subscribed={5}]"\ 57 | .format( 58 | super(ViberConversationStartedRequest, self).__str__(), 59 | self._message_token, 60 | self._type, 61 | self._context, 62 | self._user, 63 | self._subscribed) 64 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/viber_delivered_request.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.event_type import EventType 3 | from viberbot.api.viber_requests.viber_request import ViberRequest 4 | 5 | 6 | class ViberDeliveredRequest(ViberRequest): 7 | def __init__(self): 8 | super(ViberDeliveredRequest, self).__init__(EventType.DELIVERED) 9 | self._message_token = None 10 | self._user_id = None 11 | self._chat_id = None 12 | 13 | def from_dict(self, request_dict): 14 | super(ViberDeliveredRequest, self).from_dict(request_dict) 15 | self._message_token = request_dict['message_token'] 16 | self._user_id = request_dict.get('user_id', None) 17 | self._chat_id = request_dict.get('chat_id', None) 18 | return self 19 | 20 | @property 21 | def message_token(self): 22 | return self._message_token 23 | 24 | @property 25 | def user_id(self): 26 | return self._user_id 27 | 28 | @property 29 | def chat_id(self): 30 | return self._chat_id 31 | 32 | @python_2_unicode_compatible 33 | def __str__(self): 34 | return u"ViberDeliveredRequest [{0}, message_token={1}, user_id={2}]" \ 35 | .format( 36 | super(ViberDeliveredRequest, self).__str__(), 37 | self._message_token, 38 | self._user_id) 39 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/viber_failed_request.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | from future.utils import python_2_unicode_compatible 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.viber_requests.viber_request import ViberRequest 5 | 6 | 7 | class ViberFailedRequest(ViberRequest): 8 | def __init__(self): 9 | super(ViberFailedRequest, self).__init__(EventType.FAILED) 10 | self._message_token = None 11 | self._user_id = None 12 | self._desc = None 13 | 14 | def from_dict(self, request_dict): 15 | super(ViberFailedRequest, self).from_dict(request_dict) 16 | self._message_token = request_dict['message_token'] 17 | self._user_id = request_dict['user_id'] 18 | self._desc = request_dict['desc'] 19 | return self 20 | 21 | @property 22 | def meesage_token(self): 23 | warnings.warn('Property `meesage_token` had typo and now is deprecated, please use `message_token` instead') 24 | return self._message_token 25 | 26 | @property 27 | def message_token(self): 28 | return self._message_token 29 | 30 | @property 31 | def user_id(self): 32 | return self._user_id 33 | 34 | @property 35 | def desc(self): 36 | return self._desc 37 | 38 | @python_2_unicode_compatible 39 | def __str__(self): 40 | return u"ViberFailedRequest [{0}, message_token={1}, user_id={2}, desc={3}]" \ 41 | .format( 42 | super(ViberFailedRequest, self).__str__(), 43 | self._message_token, 44 | self._user_id, 45 | self._desc) 46 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/viber_message_request.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api import messages 3 | from viberbot.api.event_type import EventType 4 | from viberbot.api.user_profile import UserProfile 5 | from viberbot.api.viber_requests.viber_request import ViberRequest 6 | 7 | 8 | class ViberMessageRequest(ViberRequest): 9 | def __init__(self): 10 | super(ViberMessageRequest, self).__init__(EventType.MESSAGE) 11 | self._message = None 12 | self._sender = None 13 | self._message_token = None 14 | self._chat_id = None 15 | self._reply_type = None 16 | self._silent = None 17 | 18 | def from_dict(self, request_dict): 19 | super(ViberMessageRequest, self).from_dict(request_dict) 20 | self._message = messages.get_message(request_dict['message']) 21 | self._sender = UserProfile().from_dict(request_dict['sender']) 22 | self._message_token = request_dict['message_token'] 23 | self._silent = request_dict.get('silent', None) 24 | self._reply_type = request_dict.get('reply_type', None) 25 | self._chat_id = request_dict.get('chat_id', None) 26 | return self 27 | 28 | @property 29 | def message(self): 30 | return self._message 31 | 32 | @property 33 | def sender(self): 34 | return self._sender 35 | 36 | @property 37 | def message_token(self): 38 | return self._message_token 39 | 40 | @property 41 | def chat_id(self): 42 | return self._chat_id 43 | 44 | @property 45 | def reply_type(self): 46 | return self._reply_type 47 | 48 | @property 49 | def silent(self): 50 | return self._silent 51 | 52 | @python_2_unicode_compatible 53 | def __str__(self): 54 | return u"ViberMessageRequest [{0}, message_token={1}, sender={2}, " \ 55 | u"message={3}, chat_id={4}, reply_type={5}, silent={6}]" \ 56 | .format( 57 | super(ViberMessageRequest, self).__str__(), 58 | self._message_token, 59 | self._sender, 60 | self._message, 61 | self._chat_id, 62 | self._reply_type, 63 | self._silent) 64 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/viber_request.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | 3 | 4 | class ViberRequest(object): 5 | def __init__(self, event_type=None): 6 | self._event_type = event_type 7 | self._timestamp = None 8 | 9 | def from_dict(self, request_dict): 10 | self._timestamp = request_dict['timestamp'] 11 | if self._event_type is None: 12 | self._event_type = request_dict['event'] 13 | return self 14 | 15 | @property 16 | def event_type(self): 17 | return self._event_type 18 | 19 | @property 20 | def timestamp(self): 21 | return self._timestamp 22 | 23 | @python_2_unicode_compatible 24 | def __str__(self): 25 | return u"event_type={0}, timestamp={1}".format(self._event_type, self._timestamp) 26 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/viber_seen_request.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | from future.utils import python_2_unicode_compatible 3 | from ..event_type import EventType 4 | from viberbot.api.viber_requests.viber_request import ViberRequest 5 | 6 | 7 | class ViberSeenRequest(ViberRequest): 8 | def __init__(self): 9 | super(ViberSeenRequest, self).__init__(EventType.SEEN) 10 | self._message_token = None 11 | self._user_id = None 12 | 13 | def from_dict(self, request_dict): 14 | super(ViberSeenRequest, self).from_dict(request_dict) 15 | self._message_token = request_dict['message_token'] 16 | self._user_id = request_dict['user_id'] 17 | return self 18 | 19 | @property 20 | def meesage_token(self): 21 | warnings.warn('Property `meesage_token` had typo and now is deprecated, please use `message_token` instead') 22 | return self._message_token 23 | 24 | @property 25 | def message_token(self): 26 | return self._message_token 27 | 28 | @property 29 | def user_id(self): 30 | return self._user_id 31 | 32 | @python_2_unicode_compatible 33 | def __str__(self): 34 | return u"ViberSeenRequest [{0}, message_token={1}, user_id={2}]" \ 35 | .format(super(ViberSeenRequest, self).__str__(), self._message_token, self._user_id) 36 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/viber_subscribed_request.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.event_type import EventType 3 | from viberbot.api.user_profile import UserProfile 4 | from viberbot.api.viber_requests.viber_request import ViberRequest 5 | 6 | 7 | class ViberSubscribedRequest(ViberRequest): 8 | def __init__(self): 9 | super(ViberSubscribedRequest, self).__init__(EventType.SUBSCRIBED) 10 | self._user = None 11 | self._api_version = None 12 | 13 | def from_dict(self, request_dict): 14 | super(ViberSubscribedRequest, self).from_dict(request_dict) 15 | self._user = UserProfile().from_dict(request_dict['user']) 16 | if 'api_version' in request_dict: 17 | self._api_version = request_dict['api_version'] 18 | return self 19 | 20 | @property 21 | def user(self): 22 | return self._user 23 | 24 | @property 25 | def api_version(self): 26 | return self._api_version 27 | 28 | @python_2_unicode_compatible 29 | def __str__(self): 30 | return u"ViberSubscribedRequest [{0}, user={1}]" \ 31 | .format(super(ViberSubscribedRequest, self).__str__(), self._user) 32 | -------------------------------------------------------------------------------- /viberbot/api/viber_requests/viber_unsubscribed_request.py: -------------------------------------------------------------------------------- 1 | from future.utils import python_2_unicode_compatible 2 | from viberbot.api.event_type import EventType 3 | from viberbot.api.viber_requests.viber_request import ViberRequest 4 | 5 | 6 | class ViberUnsubscribedRequest(ViberRequest): 7 | def __init__(self): 8 | super(ViberUnsubscribedRequest, self).__init__(EventType.UNSUBSCRIBED) 9 | self._user_id = None 10 | 11 | def from_dict(self, request_dict): 12 | super(ViberUnsubscribedRequest, self).from_dict(request_dict) 13 | self._user_id = request_dict['user_id'] 14 | return self 15 | 16 | @property 17 | def user_id(self): 18 | return self._user_id 19 | 20 | @python_2_unicode_compatible 21 | def __str__(self): 22 | return u"ViberUnsubscribedRequest [{0}, user_id={1}]" \ 23 | .format(super(ViberUnsubscribedRequest, self).__str__(), self._user_id) 24 | -------------------------------------------------------------------------------- /viberbot/version.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0.12' 2 | --------------------------------------------------------------------------------