├── .gitignore ├── Dockerfile ├── ISSUE_TEMPLATE.md ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── README.md ├── docker-compose.yml ├── docker-entrypoint.sh ├── pylint.rc ├── release ├── requirements.txt ├── sample ├── echo.py ├── log_all.py ├── new_messages_observer.py ├── remote.py ├── send_media.py └── test_remote.py ├── setup.py └── src ├── __init__.py ├── helper.py ├── js ├── .eslintrc.json ├── .gitignore ├── pywapi.js └── wapi.js ├── objects ├── __init__.py ├── chat.py ├── contact.py ├── message.py ├── number_status.py └── whatsapp_object.py └── wapi_js_wrapper.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info/ 2 | build/ 3 | dist/ 4 | *.log 5 | *.pyc 6 | firefox_cache/ 7 | chrome_cache/ 8 | *.png 9 | **/.DS_Store 10 | **/*.pyc 11 | profiles 12 | .idea 13 | docs/_build 14 | .coverage 15 | .eggs 16 | *.sublime-* 17 | sample/testprofile -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official Python runtime as a parent image 2 | FROM python:3.8 3 | 4 | # Set the working directory to /app 5 | WORKDIR /app 6 | 7 | # COPY requirements to /app dir 8 | COPY requirements.txt /app 9 | 10 | # Install any needed packages specified in requirements.txt 11 | RUN pip install --no-cache-dir --trusted-host pypi.python.org -r requirements.txt 12 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | > - Advertising of monetized Frameworks is strictly forbidden and will be reported 3 | > - [ ] The language must be English, if you are not fluent in English consider using: https://www.deepl.com/translator if it supports your native language 4 | > 5 | > - [ ] Please check if any other issue already adresses your concerns or if a recent PR is already fixing the mentioned issue 6 | > 7 | > - [ ] Please make sure to **format your code**, and to mark your code as such, with the provided tools from github (upper right of this box) 8 | > 9 | > - [ ] Please state your **Operating System (inlcuding version)**, your **Python Version**, and your **installation way (pip, from repo)** and if you are running it inside a docker container 10 | > 11 | > - [ ] Note that the pip installation might be behind, so try to fix your issue with a pull from the repo and update to the most recent version 12 | > 13 | > - [ ] Follow the Stackoverflow Guidelines (https://stackoverflow.com/help/how-to-ask) when in need of help 14 | > 15 | > - [ ] Consider posting your question on Stackoverflow if it is a general programming question 16 | > 17 | > - [ ] There will be no support for spamming, or other mischief 18 | > 19 | >- [ ] There will be no support for finding professionals 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ### Copyright (c) 2020 Mohammed Shah 2 | 3 | # Hippocratic + Do Not Harm (H-DNH) Version 1.0 4 | 5 | ## Preamble 6 | 7 | Most software today is developed with little to no thought of how it will be used, or the consequences for our society and planet. 8 | 9 | As software developers, we engineer the infrastructure of the 21st century. We recognise that our infrastructure has great power to shape the world and the lives of those we share it with, and we choose to consciously take responsibility for the social and environmental impacts of what we build. 10 | 11 | We envisage a world free from injustice, inequality, and the reckless destruction of lives and our planet. We reject slavery in all its forms, whether by force, indebtedness, or by algorithms that hack human vulnerabilities. We seek a world where humankind is at peace with our neighbours, nature, and ourselves. We want our work to enrich the physical, mental and spiritual wellbeing of all society. 12 | 13 | We build software to further this vision of a just world, or at the very least, to not put that vision further from reach. 14 | 15 | ## Terms 16 | 17 | Licensor hereby grants permission by this license ("License"), free of charge, to any person or entity (the "Licensee") obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 18 | 19 | 1. All redistribution of source code or binary form, including any modifications must be under these terms. You must inform recipients that the code is governed by these conditions, and how they can obtain a copy of this license. You may not attempt to alter the conditions of who may/may not use this software. 20 | 21 | 2. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 22 | 23 | 3. This software must not be used by any organisation, website, product or service that: 24 | 25 | a) lobbies for, promotes, or derives a majority of income from actions that support or contribute to: 26 | * sex trafficking 27 | * human trafficking 28 | * slavery 29 | * indentured servitude 30 | * gambling 31 | * tobacco 32 | * adversely addictive behaviours 33 | * nuclear energy 34 | * warfare 35 | * weapons manufacturing 36 | * war crimes 37 | * violence (except when required to protect public safety) 38 | * burning of forests 39 | * deforestation 40 | * hate speech or discrimination based on age, gender, gender identity, race, sexuality, religion, nationality 41 | 42 | b) lobbies against, or derives a majority of income from actions that discourage or frustrate: 43 | * peace 44 | * access to the rights set out in the Universal Declaration of Human Rights and the Convention on the Rights of the Child 45 | * peaceful assembly and association (including worker associations) 46 | * a safe environment or action to curtail the use of fossil fuels or prevent climate change 47 | * democratic processes 48 | 49 | 50 | 4. Compliance with Human Rights Laws and Human Rights Principles: 51 | 52 | a. **Human Rights Laws**. The Software shall not be used by any person or entity for any systems, activities, or other uses that violate any applicable laws, regulations, or rules that protect human, civil, labor, privacy, political, environmental, security, economic, due process, or similar rights (the "Human Rights Laws"). Where the Human Rights Laws of more than one jurisdiction are applicable to the use of the Software, the Human Rights Laws that are most protective of the individuals or groups harmed shall apply. 53 | 54 | b. **Human Rights Principles**. Licensee is advised to consult the articles of the United Nations Universal Declaration of Human Rights (https://www.un.org/en/universal-declaration-human-rights/) and the United Nations Global Compact (https://www.unglobalcompact.org/ what-is-gc/mission/principles) that define recognized principles of international human rights (the "Human Rights Principles"). It is Licensor's express intent that all use of the Software be consistent with Human Rights Principles. If Licensor receives notification or otherwise learns of an alleged violation of any Human Rights Principles relating to Licensee's use of the Software, Licensor may in its discretion and without obligation (i) (a) notify Licensee of such allegation and (b) allow Licensee 90 days from notification under (i)(a) to investigate and respond to Licensor regarding the allegation and (ii) (a) after the earlier of 90 days from notification under (i)(a), or Licensee's response under (i)(b), notify Licensee of License termination and (b) allow Licensee an additional 90 days from notification under (ii)(a) to cease use of the Software. 55 | 56 | c. **Indemnity**. Licensee shall hold harmless and indemnify Licensor against all losses, damages, liabilities, deficiencies, claims, actions, judgments, settlements, interest, awards, penalties, fines, costs, or expenses of whatever kind, including Licensor's reasonable attorneys' fees, arising out of or relating to Licensee's non-compliance with this License or use of the Software in violation of Human Rights Laws or Human Rights Principles. 57 | 58 | 5. Enforceability: If any portion or provision of this License is determined to be invalid, illegal, or unenforceable by a court of competent jurisdiction, then such invalidity, illegality, or unenforceability shall not affect any other term or provision of this License or invalidate or render unenforceable such term or provision in any other jurisdiction. Upon a determination that any term or provision is invalid, illegal, or unenforceable, to the extent permitted by applicable law, the court may modify this License to affect the original intent of the parties as closely as possible. The section headings are for convenience only and are not intended to affect the construction or interpretation of this License. Any rule of construction to the effect that ambiguities are to be resolved against the drafting party shall not apply in interpreting this License. The language in this License shall be interpreted as to its fair meaning and not strictly for or against any party. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 61 | 62 | This license is an extension of the Hippocratic License - an Ethical Source license (https://ethicalsource.dev) and the Do Not Harm License - (https://github.com/raisely/NoHarm) -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src * 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | PACKAGE_NAME = webwhatsapi 3 | PACKAGE_COVERAGE = webwhatsapi 4 | 5 | help: 6 | @echo "Options" 7 | @echo "-----------------------------------------------------------------------" 8 | @echo "help: This help" 9 | @echo "requirements: Download requirements" 10 | @echo "requirements-test: Download requirements for tests" 11 | @echo "requirements-docs: Download requirements for docs" 12 | @echo "run-tests: Run tests with coverage" 13 | @echo "publish: Publish new version on Pypi" 14 | @echo "clean: Clean compiled files" 15 | @echo "flake: Run Flake8" 16 | @echo "prepush: Helper to run before to push to repo" 17 | @echo "pull-request: Helper to run before to merge a pull request" 18 | @echo "autopep: Reformat code using PEP8" 19 | @echo "-----------------------------------------------------------------------" 20 | 21 | requirements: 22 | @echo "Installing ${PACKAGE_NAME} requirements..." 23 | pip install -r requirements.txt 24 | 25 | requirements-test: requirements 26 | @echo "Installing ${PACKAGE_NAME} tests requirements..." 27 | pip install -r requirements-test.txt 28 | 29 | requirements-docs: requirements 30 | @echo "Installing ${PACKAGE_NAME} docs requirements..." 31 | pip install -r requirements-docs.txt 32 | 33 | run-tests: 34 | @echo "Running tests..." 35 | nosetests --with-coverage -d --cover-package=${PACKAGE_COVERAGE} --cover-erase 36 | 37 | publish: 38 | @echo "Publishing new version on Pypi..." 39 | python setup.py bdist_wheel upload 40 | 41 | clean: 42 | @echo "Cleaning compiled files..." 43 | find . | grep -E "(__pycache__|\.pyc|\.pyo)$ " | xargs rm -rf 44 | @echo "Cleaning distribution files..." 45 | rm -rf dist 46 | @echo "Cleaning build files..." 47 | rm -rf build 48 | @echo "Cleaning egg info files..." 49 | rm -rf ${PACKAGE_NAME}.egg-info 50 | @echo "Cleaning coverage files..." 51 | rm -f .coverage 52 | 53 | build: 54 | python3 setup.py bdist_wheel 55 | 56 | flake: 57 | @echo "Running flake8 tests..." 58 | flake8 ${PACKAGE_COVERAGE} 59 | flake8 tests 60 | 61 | autopep: 62 | autopep8 --max-line-length 120 -r -j 8 -i . 63 | 64 | prepush: flake run-tests 65 | 66 | pull-request: flake run-tests -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PLEASE CONSIDER USING [wa-automate-socket-client-python](https://github.com/open-wa/wa-automate-socket-client-python) AS THIS LIBRARY WILL BE DEPRECATED 2 |



3 |
4 | 5 | 6 | # wa-automate-python 7 | 8 | 9 | ## (Based on web.whatsapp.com) 10 | ![PyPI version](https://badge.fury.io/py/openwa.svg) 11 | 12 | [![Firefox version](https://img.shields.io/badge/Firefox-75-green.svg)]() 13 | [![Chrome version](https://img.shields.io/badge/Chrome-81-green.svg)]() 14 |
15 | 16 | ## What is it? 17 | This package is used to provide a python interface for interacting with WhatsAPP Web to send and receive Whatsapp messages. 18 | It is based on the official Whatsapp Web Browser Application and uses Selenium browser automation to communicate with Whatsapp Web. 19 | 20 | ### Functions list 21 | | Function | Description | Implemented | 22 | | --------------------------------- | ----------- | ----------- | 23 | | Receive message | | ✅ | 24 | | Automatic QR Refresh | | ✅ | 25 | | Send text | | ✅ | 26 | | Get contacts | | ✅ | 27 | | Get chats | | ✅ | 28 | | Get groups | | ✅ | 29 | | Get group members | | ✅ | 30 | | Send contact | | ✅ | 31 | | Get contact detail | | ✅ | 32 | | Send Images (image) | | ✅ | 33 | | Send media (audio, doc) | | ✅ | 34 | | Send media (video) | | ✅ | 35 | | Send stickers | |✅ | 36 | | Decrypt media (image, audio, doc) | | ✅ | 37 | | Capturing QR Code | | ✅ | 38 | | Multiple Sessions | | ✅ | 39 | | Last seen & isOnline (beta) | | ✅ | 40 | | 📍 SEND LOCATION!! (beta) | | ✅ | 41 | | Simulated '...typing' | | ✅ | 42 | | Send GIFs! | | ✅ | 43 | | Forward Messages | | ✅ | 44 | | Listen to New Messages | | ✅ | 45 | | Listen to Read Receipts | | ✅ | 46 | | Listen to Live Locations | | ✅ | 47 | | Group participant changes | | ❌ | 48 | | Create Groups | | ✅ | 49 | | add, remove, promote, demote participants | | ✅ | 50 | 51 | ## Starting a conversation 52 | To start a new conversation, there are two options: 53 | 1. The other person contacts you first. Once the other person contacts you, you'll have that chat available to send messages. 54 | 2. With a license key. In order to unlock the functionality to send texts to unknown numbers through @open-wa/wa-automate itself, you will need an License key. 55 | 56 | One License Key is valid for one number. Each License Key is USD 5 per month or USD 50 per year. Instructions below. 57 | 58 | ## License key 59 | How to get an License key: 60 | 1. Go to https://openwa.page.link/python-license 61 | 62 | **Important: you must make the purchase entering by this exact link for it to work on the python lib** 63 | 64 | 2. Select one of the tiers that includes the key 65 | 3. At checkout enter the phone number with which you are going to use the bot and the reason why you need this key 66 | 67 | **Important: the phone number must be in the international format, and be just numbers. For example, if the number is +123 456 4564, you must enter 1234564564** 68 | 69 | 4. You'll receive the license key by email. To use it, you have to pass it to the WhatsAPIDriver in the `license_key` kwarg on initialization 70 | 71 | 72 | Notes: 73 | - You can change the number assigned to a specific License Key at any time. 74 | - In order to cancel your License Key, simply stop your membership. 75 | - Apart from passing your licens_key on initialization, you will need to change nothing else in your code. 76 | - An added benefit for members is priority on issues. 77 | - License Key request may be rejected. 78 | 79 | ## Local installation 80 | 81 | ##### Dependencies 82 | You will need to install [Gecko Driver](https://github.com/mozilla/geckodriver) separately, if using Firefox, which is the default, or [chromedriver](https://chromedriver.chromium.org/downloads) if using Chrome. 83 | 84 | #### From PyPI 85 | - Install from pip 86 | `pip install openwa` 87 | 88 | - Upgrade to latest version with 89 | `pip install --upgrade openwa` 90 | 91 | ## Usage 92 | 93 | ### 1. Import library 94 | ```python 95 | from openwa import WhatsAPIDriver 96 | ``` 97 | 98 | ### 2. Instantiate driver 99 | ```python 100 | driver = WhatsAPIDriver() 101 | ``` 102 | 103 | Possible arguments for constructor: 104 | 105 | - client : Type of browser. The default is `firefox`, but `chrome` is also supported. 106 | - remote: Defines if webdriver will be loaded as remote. See sample directory for remote examples. 107 | - username : Can be any value. 108 | - proxy: The proxy server to configure selenium to. Format is ":" 109 | - command_executor: Passed directly as an argument to Remote Selenium. Ignore if not using it. See sample directory for remote examples. 110 | - loadstyles: Default is False. If True, it will load the styling in the browser. 111 | - profile: Pass the full path to the profile to load it. Profile folder will be end in ".default". For persistent login, open a normal firefox tab, log in to whatsapp, then pass the profile as an argument. 112 | - license_key: License key if you have a subscription 113 | 114 | ### 3. Wait until you are loged in, or you are asked to scan the QR 115 | ```python 116 | driver.wait_for_login() 117 | ``` 118 | 119 | ### 4. Get the QR in one of these ways 120 | ```python 121 | # Save image to file 122 | driver.get_qr() 123 | 124 | # Get image in base64 125 | driver.get_qr_base64() 126 | ``` 127 | 128 | The QR code is automatically reloaded if it has expired 129 | 130 | ### 5. Scan the QR code with your phone 131 | Once you scan the QR obtained, you are ready to use the rest of the funcionality 132 | 133 | ### Viewing messages 134 | Before loading messages, 135 | ```python 136 | unread_messages = driver.get_unread() 137 | ``` 138 | 139 | ### Forwarding messages 140 | You need to pass the following params: 141 | 142 | - chat id to forward messages to 143 | - messages: a single or array of message ids 144 | - skipMyMessages: true or false, if true it will filter out messages sent by you from the list of messages, default false. 145 | 146 | driver.forward_messages(to_chat_id, messages, False) 147 | 148 | ### Reply messages 149 | ```python 150 | .reply_message('I like that option!') 151 | ``` 152 | ### List all chats 153 | ```python 154 | driver.get_all_chats() 155 | ``` 156 | 157 | ### Get chat by name 158 | Fetches a chat given its name. Must be an exact match 159 | ```python 160 | driver.get_chat_from_name(chat_name) 161 | ``` 162 | 163 | ### Get chat by number 164 | Fetches a chat given its phone number. Must be in the international format.\ 165 | For example, for the number: +123-45-678-9123, this function expects: 123456789123 166 | 167 | ```python 168 | driver.get_chat_from_phone_number('123456789123') 169 | ``` 170 | 171 | ### To send a message, get a Chat object from before, and call the send_message function with the message. 172 | ```python 173 | .send_message("Hello") 174 | ``` 175 | 176 | ### Sending Media/Files 177 | ```python 178 | .send_media(image_path, "look at this!") 179 | ``` 180 | 181 | ### Sending Gifs 182 | 183 | There are two ways to send GIFs - by Video or by giphy link. 184 | 185 | ##### 1. Sending Video as a GIF. 186 | WhatsApp doesn't actually support the .gif format - probably due to how inefficient it is as a filetype - they instead convert GIFs to video then process them. 187 | 188 | In order to send gifs you need to convert the gif to an mp4 file then use the following method: 189 | ```python 190 | .send_video_as_gif(mp4_file_path, "look at this gif!") 191 | ``` 192 | 193 | ##### 2. Sending a Giphy Media Link 194 | This is a convenience method to make it easier to send gifs from the website [GIPHY](https://giphy.com). You need to make sure you use a giphy media link as shown below. 195 | ```python 196 | .send_video_as_gif("https://media.giphy.com/media/oYtVHSxngR3lC/giphy.gif", "look at this giphy!") 197 | ``` 198 | 199 | ### Sending Location 200 | You need to pass the following params: 201 | 202 | - latitude: '51.5074' 203 | - longitude: '0.1278' 204 | - location text: 'LONDON!' 205 | 206 | ```python 207 | .send_location('51.5074', '0.1278', 'London') 208 | ``` 209 | 210 | ### Simulate typing 211 | You need to pass the following param: 212 | 213 | - typing: `True` or `False` 214 | 215 | ```python 216 | .set_typing_simulation(True) 217 | ``` 218 | 219 | ### Create group 220 | The first parameter is the group name, the second parameter is the contact ids to add as participants 221 | ```python 222 | driver.create_group('The Groopers', [chat_id1, chat_id2]) 223 | ``` 224 | 225 | ### Edit group participants 226 | You can get the GroupChat object with the previously explained methods to obtain chats. 227 | ```python 228 | .add_participant_group(id_participant) 229 | .remove_participant_group(id_participant) 230 | .promove_participant_admin_group(id_participant) 231 | .demote_participant_admin_group(id_participant) 232 | ``` 233 | 234 | ### Listening for new messages 235 | For this we must define a new observer class that implements the `on_message_received` method and receives the new messages. 236 | ```python 237 | class NewMessageObserver: 238 | def on_message_received(self, new_messages): 239 | for message in new_messages: 240 | print("New message received from number {}".format(message.sender.id)) 241 | 242 | driver.subscribe_new_messages(NewMessageObserver()) 243 | ``` 244 | 245 | ## Limitation 246 | Phone needs to manually scan the QR Code from Whatsapp Web. Phone has to be on and connected to the internet. 247 | 248 | ## Docker and remote Selenium Installation 249 | 250 | It may be favorable to run Selenium and the wa-automate-python client as Docker containers. This almost completely avoids any installation problems and any messy local installation or dependency hell. The result is a more stable runtime environment for the client, which could run on a headless server. 251 | Using Docker may also help in developing the library itself. 252 | 253 | ### 1. Create network 254 | ```sh 255 | docker network create selenium 256 | ``` 257 | 258 | ### 2. Run Selenium grid/standalone container 259 | 260 | This is based on the official Selenium image (https://github.com/SeleniumHQ/docker-selenium). 261 | The following Docker command runs a Selenium standalone Firefox browser in debug (VNC) mode. You can use VNC on port 5900 to view the browser. It uses the network "selenium" and the container is named "firefox" for later reference. 262 | ```sh 263 | docker run -d -p 4444:4444 -p 5900:5900 --name firefox --network selenium -v /dev/shm:/dev/shm selenium/standalone-firefox-debug:3.141.59 264 | ``` 265 | 266 | ### 3. Build python/wa-automate-python docker base image 267 | 268 | The following command uses the dockerfile to build a new image based on Python 2.7 with all required packages from requirements.txt. 269 | 270 | ```sh 271 | docker build -t wa-automate-python . 272 | ``` 273 | 274 | ### 4. Run client container 275 | 276 | Now to the client container. The following command installs a local webwhatsapi inside the base container and runs a client. It maps the local directory to the app directory inside the container for easy development. Also sets the network to "selenium" and an environment variable for the remote selenium url. Please note that the remote Selenium hostname must be identical to the name of the Selenium container. 277 | ```sh 278 | docker run --network selenium -it -e SELENIUM='http://firefox:4444/wd/hub' -v $(pwd):/app wa-automate-python /bin/bash -c "pip install ./;pip list;python sample/remote.py" 279 | ``` 280 | 281 | For Windows (cmd): 282 | ```sh 283 | docker run --network selenium -it -e SELENIUM='http://firefox:4444/wd/hub' -v "%cd%:/app" wa-automate-python /bin/bash -c "pip install ./;pip list;python sample/remote.py" 284 | ``` 285 | 286 | For Windows (PowerShell): 287 | ```sh 288 | docker run --network selenium -it -e SELENIUM='http://firefox:4444/wd/hub' -v "$(pwd):/app".ToLower() wa-automate-python /bin/bash -c "pip install ./;pip list;python sample/remote.py" 289 | ``` 290 | 291 | It is also certainly possible to fully build the docker image in advance and define an entrypoint/cmd inside the dockerfile to run a full client. 292 | 293 | 294 | ## Contribute 295 | Contributing is simple as cloning, making changes and submitting a pull request. 296 | If you would like to contribute, here are a few starters: 297 | - Bug Hunts 298 | - More sorts of examples 299 | - Additional features/ More integrations (This api has the minimum amount, but I don't mind having more data accessible to users) 300 | - Create an env/vagrant box to make it easy for others to contribute. (At the moment, all I have is a requirements.txt 301 | 302 | ## Legal 303 | This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by WhatsApp or any of its affiliates or subsidiaries. This is an independent and unofficial software. Use at your own risk. 304 | 305 | ## License 306 | [Hippocratic + Do Not Harm Version 1.0](https://github.com/open-wa/wa-automate-python/blob/master/LICENSE.md) 307 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | firefox: 5 | image: selenium/standalone-firefox-debug:3.14.0-dubnium 6 | ports: 7 | - "4444:4444" 8 | - "5900:5900" 9 | volumes: 10 | - "/dev/shm:/dev/shm" 11 | networks: 12 | - selenium 13 | whatsapp: 14 | build: . 15 | environment: 16 | - SELENIUM=http://firefox:4444/wd/hub 17 | volumes: 18 | - ".:/app" 19 | command: /app/docker-entrypoint.sh 20 | networks: 21 | - selenium 22 | depends_on: 23 | - firefox 24 | 25 | volumes: 26 | devshm: 27 | 28 | networks: 29 | selenium: 30 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | pip install ./ 4 | pip list 5 | python sample/remote.py 6 | -------------------------------------------------------------------------------- /pylint.rc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # Specify a configuration file. 4 | #rcfile= 5 | 6 | # Python code to execute, usually for sys.path manipulation such as 7 | # pygtk.require(). 8 | #init-hook= 9 | 10 | # Profiled execution. 11 | profile=no 12 | 13 | # Add files or directories to the blacklist. They should be base names, not 14 | # paths. 15 | ignore=CVS 16 | 17 | # Pickle collected data for later comparisons. 18 | persistent=yes 19 | 20 | # List of plugins (as comma separated values of python modules names) to load, 21 | # usually to register additional checkers. 22 | load-plugins= 23 | 24 | 25 | [REPORTS] 26 | 27 | # Set the output format. Available formats are text, parseable, colorized, msvs 28 | # (visual studio) and html. You can also give a reporter class, eg 29 | # mypackage.mymodule.MyReporterClass. 30 | output-format=text 31 | 32 | # Put messages in a separate file for each module / package specified on the 33 | # command line instead of printing them on stdout. Reports (if any) will be 34 | # written in a file name "pylint_global.[txt|html]". 35 | files-output=no 36 | 37 | # Tells whether to display a full report or only the messages 38 | reports=yes 39 | 40 | # Python expression which should return a note less than 10 (10 is the highest 41 | # note). You have access to the variables errors warning, statement which 42 | # respectively contain the number of errors / warnings messages and the total 43 | # number of statements analyzed. This is used by the global evaluation report 44 | # (RP0004). 45 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 46 | 47 | # Add a comment according to your evaluation note. This is used by the global 48 | # evaluation report (RP0004). 49 | comment=no 50 | 51 | # Template used to display messages. This is a python new-style format string 52 | # used to format the message information. See doc for all details 53 | #msg-template= 54 | 55 | 56 | [MESSAGES CONTROL] 57 | 58 | # Enable the message, report, category or checker with the given id(s). You can 59 | # either give multiple identifier separated by comma (,) or put this option 60 | # multiple time. See also the "--disable" option for examples. 61 | #enable= 62 | 63 | # Disable the message, report, category or checker with the given id(s). You 64 | # can either give multiple identifiers separated by comma (,) or put this 65 | # option multiple times (only on the command line, not in the configuration 66 | # file where it should appear only once).You can also use "--disable=all" to 67 | # disable everything first and then reenable specific checks. For example, if 68 | # you want to run only the similarities checker, you can use "--disable=all 69 | # --enable=similarities". If you want to run only the classes checker, but have 70 | # no Warning level messages displayed, use"--disable=all --enable=classes 71 | # --disable=W" 72 | disable=old-style-class,bad-mcs-classmethod-argument,super-on-old-class 73 | 74 | 75 | [MISCELLANEOUS] 76 | 77 | # List of note tags to take in consideration, separated by a comma. 78 | notes=FIXME,XXX,TODO 79 | 80 | 81 | [SIMILARITIES] 82 | 83 | # Minimum lines number of a similarity. 84 | min-similarity-lines=4 85 | 86 | # Ignore comments when computing similarities. 87 | ignore-comments=yes 88 | 89 | # Ignore docstrings when computing similarities. 90 | ignore-docstrings=yes 91 | 92 | # Ignore imports when computing similarities. 93 | ignore-imports=no 94 | 95 | 96 | [BASIC] 97 | 98 | # Required attributes for module, separated by a comma 99 | required-attributes= 100 | 101 | # List of builtins function names that should not be used, separated by a comma 102 | bad-functions=map,filter,apply 103 | 104 | # Regular expression which should only match correct module names 105 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 106 | 107 | # Regular expression which should only match correct module level names 108 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 109 | 110 | # Regular expression which should only match correct class names 111 | class-rgx=[A-Z_][a-zA-Z0-9]+$ 112 | 113 | # Regular expression which should only match correct function names 114 | function-rgx=[a-z_][a-z0-9_]{2,30}$ 115 | 116 | # Regular expression which should only match correct method names 117 | method-rgx=[a-z_][a-z0-9_]{2,30}$ 118 | 119 | # Regular expression which should only match correct instance attribute names 120 | attr-rgx=[a-z_][a-z0-9_]{2,30}$ 121 | 122 | # Regular expression which should only match correct argument names 123 | argument-rgx=[a-z_][a-z0-9_]{2,30}$ 124 | 125 | # Regular expression which should only match correct variable names 126 | variable-rgx=[a-z_][a-z0-9_]{2,30}$ 127 | 128 | # Regular expression which should only match correct attribute names in class 129 | # bodies 130 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 131 | 132 | # Regular expression which should only match correct list comprehension / 133 | # generator expression variable names 134 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ 135 | 136 | # Good variable names which should always be accepted, separated by a comma 137 | good-names=i,j,k,ex,Run,_ 138 | 139 | # Bad variable names which should always be refused, separated by a comma 140 | bad-names=foo,bar,baz,toto,tutu,tata 141 | 142 | # Regular expression which should only match function or class names that do 143 | # not require a docstring. 144 | no-docstring-rgx=__.*__ 145 | 146 | # Minimum line length for functions/classes that require docstrings, shorter 147 | # ones are exempt. 148 | docstring-min-length=-1 149 | 150 | 151 | [VARIABLES] 152 | 153 | # Tells whether we should check for unused import in __init__ files. 154 | init-import=no 155 | 156 | # A regular expression matching the beginning of the name of dummy variables 157 | # (i.e. not used). 158 | dummy-variables-rgx=_$|dummy 159 | 160 | # List of additional names supposed to be defined in builtins. Remember that 161 | # you should avoid to define new builtins when possible. 162 | additional-builtins= 163 | 164 | 165 | [FORMAT] 166 | 167 | # Maximum number of characters on a single line. 168 | max-line-length=120 169 | 170 | # Regexp for a line that is allowed to be longer than the limit. 171 | ignore-long-lines=^\s*(# )??$ 172 | 173 | # Allow the body of an if to be on the same line as the test if there is no 174 | # else. 175 | single-line-if-stmt=no 176 | 177 | # List of optional constructs for which whitespace checking is disabled 178 | no-space-check=trailing-comma,dict-separator 179 | 180 | # Maximum number of lines in a module 181 | max-module-lines=1000 182 | 183 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 184 | # tab). 185 | indent-string=' ' 186 | 187 | 188 | [TYPECHECK] 189 | 190 | # Tells whether missing members accessed in mixin class should be ignored. A 191 | # mixin class is detected if its name ends with "mixin" (case insensitive). 192 | ignore-mixin-members=yes 193 | 194 | # List of classes names for which member attributes should not be checked 195 | # (useful for classes with attributes dynamically set). 196 | ignored-classes=SQLObject 197 | 198 | # When zope mode is activated, add a predefined set of Zope acquired attributes 199 | # to generated-members. 200 | zope=no 201 | 202 | # List of members which are set dynamically and missed by pylint inference 203 | # system, and so shouldn't trigger E0201 when accessed. Python regular 204 | # expressions are accepted. 205 | generated-members=REQUEST,acl_users,aq_parent 206 | 207 | 208 | [DESIGN] 209 | 210 | # Maximum number of arguments for function / method 211 | max-args=5 212 | 213 | # Argument names that match this expression will be ignored. Default to name 214 | # with leading underscore 215 | ignored-argument-names=_.* 216 | 217 | # Maximum number of locals for function / method body 218 | max-locals=15 219 | 220 | # Maximum number of return / yield for function / method body 221 | max-returns=6 222 | 223 | # Maximum number of branch for function / method body 224 | max-branches=12 225 | 226 | # Maximum number of statements in function / method body 227 | max-statements=50 228 | 229 | # Maximum number of parents for a class (see R0901). 230 | max-parents=7 231 | 232 | # Maximum number of attributes for a class (see R0902). 233 | max-attributes=7 234 | 235 | # Minimum number of public methods for a class (see R0903). 236 | min-public-methods=2 237 | 238 | # Maximum number of public methods for a class (see R0904). 239 | max-public-methods=20 240 | 241 | 242 | [IMPORTS] 243 | 244 | # Deprecated modules which should not be used, separated by a comma 245 | deprecated-modules=regsub,TERMIOS,Bastion,rexec 246 | 247 | # Create a graph of every (i.e. internal and external) dependencies in the 248 | # given file (report RP0402 must not be disabled) 249 | import-graph= 250 | 251 | # Create a graph of external dependencies in the given file (report RP0402 must 252 | # not be disabled) 253 | ext-import-graph= 254 | 255 | # Create a graph of internal dependencies in the given file (report RP0402 must 256 | # not be disabled) 257 | int-import-graph= 258 | 259 | 260 | [CLASSES] 261 | 262 | # List of interface methods to ignore, separated by a comma. This is used for 263 | # instance to not check methods defines in Zope's Interface base class. 264 | ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by 265 | 266 | # List of method names used to declare (i.e. assign) instance attributes. 267 | defining-attr-methods=__init__,__new__,setUp 268 | 269 | # List of valid names for the first argument in a class method. 270 | valid-classmethod-first-arg=cls 271 | 272 | # List of valid names for the first argument in a metaclass class method. 273 | #valid-metaclass-classmethod-first-arg=mcs 274 | 275 | 276 | [EXCEPTIONS] 277 | 278 | # Exceptions that will emit a warning when being caught. Defaults to 279 | # "Exception" 280 | overgeneral-exceptions=Exception 281 | -------------------------------------------------------------------------------- /release: -------------------------------------------------------------------------------- 1 | rmdir /Q /S dist 2 | python setup.py sdist bdist_wheel 3 | python -m twine upload --repository pypi dist/* -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python-dateutil>=2.6.0 2 | selenium>=3.4.3 3 | six>=1.10.0 4 | python-axolotl 5 | cryptography 6 | typing 7 | Pillow 8 | requests 9 | python-magic 10 | -------------------------------------------------------------------------------- /sample/echo.py: -------------------------------------------------------------------------------- 1 | import time 2 | from src import WhatsAPIDriver 3 | from src.objects.message import Message 4 | 5 | driver = WhatsAPIDriver() 6 | print("Waiting for QR") 7 | while not driver.wait_for_login(): 8 | time.sleep(3) 9 | 10 | print("Bot started") 11 | 12 | while True: 13 | time.sleep(3) 14 | print('Checking for more messages') 15 | for contact in driver.get_unread(): 16 | for message in contact.messages: 17 | if isinstance(message, Message): # Currently works for text messages only. 18 | contact.chat.send_message(message.content) 19 | -------------------------------------------------------------------------------- /sample/log_all.py: -------------------------------------------------------------------------------- 1 | import os, sys, time, json, datetime 2 | from src import WhatsAPIDriver 3 | from src.objects.message import Message, MediaMessage 4 | 5 | # EXAMPLE OF LOGGER BOT 6 | # ===================== 7 | # Logs everything that passes through the phone, and saves media content too. 8 | # Perfect companion for those who hate the ones who use to "erase message". 9 | # The bot interacts with a remote phone, called "master". 10 | # This phone can send simple commands and receives periodic notifications 11 | # in order to know the bot is still running 12 | 13 | # Global Const 14 | # cc = country code, e.g. for UK '44' 15 | # ppp = mobile prefix 16 | # nnnnnnn = mobile number 17 | masters_number = "ccpppnnnnnnn" 18 | 19 | # Global Vars 20 | pinger = -1 21 | now = datetime.datetime.now() 22 | start_time = datetime.datetime.now() 23 | 24 | # Procs and Funcs 25 | def print_and_log(text): 26 | print(text) 27 | f=open("generallog.log", "a+", encoding="utf-8") 28 | f.write("[{timestamp}] : {txt}\n".format(timestamp=datetime.datetime.now(), txt=text)) 29 | f.close() 30 | 31 | def send_message_to_master(message): 32 | phone_safe = masters_number # Phone number with country code 33 | phone_whatsapp = "{}@c.us".format(phone_safe) # WhatsApp Chat ID 34 | driver.chat_send_message(phone_whatsapp,message) 35 | 36 | def process_command(command): 37 | print_and_log("Processing command: {cmd}".format(cmd=command)) 38 | if command.lower() == '#status': 39 | send_message_to_master("I am still alive") 40 | elif command.lower() == '#quit': 41 | quit() 42 | elif command.lower() == '#ping': 43 | send_message_to_master("The counter is now {ping}".format(ping=pinger)) 44 | elif command.lower() == '#uptime': 45 | uptime = datetime.datetime.now() - start_time 46 | send_message_to_master("Up since {start}, hence for a total time of {upt} by now".format(start=start_time,upt=uptime)) 47 | else: 48 | send_message_to_master("I am sorry but I can't understand '{cmd}'".format(cmd=command)) 49 | 50 | # Main 51 | driver = WhatsAPIDriver() 52 | print("Waiting for QR") 53 | 54 | while not driver.wait_for_login(): 55 | time.sleep(3) 56 | 57 | print("Bot started") 58 | start_time = datetime.datetime.now() 59 | 60 | try: 61 | 62 | while True: 63 | time.sleep(3) # Checks for new messages every 3 secs. 64 | pinger = pinger +1 65 | if ((pinger%600) == 0): # Notification every 30 min. (600 * 3 sec = 1800 sec) 66 | pinger=0 67 | send_message_to_master("Resetting counter to {pingcount}. Driver status is '{status}'".format(pingcount=pinger, status=driver.get_status())) 68 | print('Checking for more messages, status. Pinger={pingcount}'.format(pingcount=pinger), driver.get_status()) 69 | for contact in driver.get_unread(include_me=True, include_notifications=True): 70 | for message in contact.messages: 71 | print(json.dumps(message.get_js_obj(), indent = 4)) 72 | # Log full JSON to general log 73 | f=open("generallog.log", "a+", encoding="utf-8") 74 | f.write("\n\n==========================================================================\nMessage received at {timestamp}\n".format(timestamp=str(datetime.datetime.now()))) 75 | try: 76 | f.write(json.dumps(message.get_js_obj(), indent = 4)) 77 | except: 78 | f.write('ERROR!! Unprintable JSON!') 79 | send_message_to_master("Unprintable JSON! Please check!") 80 | f.write("\n") 81 | f.close() 82 | print('class', message.__class__.__name__) 83 | print('message', message) 84 | print('id', message.id) 85 | print('type', message.type) 86 | print('timestamp', message.timestamp) 87 | print('chat_id', message.chat_id) 88 | print('sender', message.sender) 89 | 90 | # Notifications don't seem to have sender.id neither sender.getsafename() 91 | try: 92 | sender_id = message.sender.id 93 | except: 94 | sender_id = 'NONE' 95 | print('sender.id', sender_id) 96 | try: 97 | sender_safe_name = message.sender.get_safe_name() 98 | except: 99 | sender_safe_name = 'NONE' 100 | print('sender.safe_name', sender_safe_name) 101 | 102 | if message.type == 'chat': 103 | print('-- Chat') 104 | print('safe_content', message.safe_content) 105 | try: 106 | print('content', message.content) 107 | except: 108 | print('content is unsafe! Printing safe_content instead', message.safe_content) 109 | send_message_to_master("Unprintable MESSAGE CONTENT! Please check!") 110 | f=open("chat_" + message.chat_id['_serialized'] + ".chat.log","a+", encoding="utf-8") 111 | f.write("[ {sender} | {timestamp} ] ".format(sender=message.sender.get_safe_name(), timestamp=message.timestamp)) 112 | try: 113 | f.write(message.content) 114 | except: 115 | f.write('(safecontent) {content}'.format(content=message.safe_content)) 116 | f.write("\n") 117 | f.close() 118 | f=open("safechat_" + message.chat_id['_serialized'] + ".chat.log","a+") 119 | f.write("[ {sender} | {timestamp} ] {content}\n".format(sender=message.sender.get_safe_name(), timestamp=message.timestamp, content=message.safe_content)) 120 | f.close() 121 | if ( (message.chat_id['user'] == masters_number) and (message.content[0:1]=="#") ) : 122 | print_and_log("Message from master: '{cmd}'.".format(cmd=message.content)) 123 | process_command(message.content) 124 | elif message.type == 'image' or message.type == 'video' or message.type == 'document' or message.type == 'audio' : 125 | print('-- Media') 126 | print('filename', message.filename) 127 | print('size', message.size) 128 | print('mime', message.mime) 129 | msg_caption='' 130 | if hasattr(message, 'caption'): 131 | msg_caption = message.caption 132 | print('caption', message.caption) 133 | print('client_url', message.client_url) 134 | f=open("chat_" + message.chat_id['_serialized'] + ".chat.log","a+") 135 | f.write("[ {sender} | {timestamp} ] sent media chat_{id}\{filename} with caption '{caption}'\n".format(sender=message.sender.get_safe_name(), timestamp=message.timestamp, id=message.chat_id['_serialized'], filename=message.filename, caption=msg_caption)) 136 | f.close() 137 | f=open("safechat_" + message.chat_id['_serialized'] + ".chat.log","a+") 138 | f.write("[ {sender} | {timestamp} ] sent media chat_{id}\{filename} with caption '{caption}'\n".format(sender=message.sender.get_safe_name(), timestamp=message.timestamp, id=message.chat_id['_serialized'], filename=message.filename, caption=msg_caption)) 139 | f.close() 140 | if not os.path.exists('chat_{id}'.format(id=message.chat_id['_serialized'])): 141 | os.makedirs('chat_{id}'.format(id=message.chat_id['_serialized'])) 142 | message.save_media('chat_{id}'.format(id=message.chat_id['_serialized'])) 143 | else: 144 | print('-- Other') 145 | except Exception as e: 146 | print('EXCEPTION:',e) 147 | send_message_to_master("I am dying! HELP!\n") 148 | send_message_to_master("Exception was: {exc}\n".format(exc=e)) 149 | f=open("generallog.log", "a+", encoding="utf-8") 150 | f.write("\n\nEXCEPTION: {exc}\n".format(exc=e)) 151 | f.close 152 | raise 153 | -------------------------------------------------------------------------------- /sample/new_messages_observer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | 5 | from src import WhatsAPIDriver 6 | 7 | 8 | def run(): 9 | print("Environment", os.environ) 10 | try: 11 | os.environ["SELENIUM"] 12 | except KeyError: 13 | print("Please set the environment variable SELENIUM to Selenium URL") 14 | sys.exit(1) 15 | 16 | driver = WhatsAPIDriver(client='firefox', remote=True, command_executor=os.environ["SELENIUM"]) 17 | print("Waiting for QR") 18 | driver.wait_for_login() 19 | print("Bot started") 20 | 21 | driver.subscribe_new_messages(NewMessageObserver()) 22 | print("Waiting for new messages...") 23 | 24 | """ Locks the main thread while the subscription in running """ 25 | while True: 26 | time.sleep(60) 27 | 28 | 29 | class NewMessageObserver: 30 | def on_message_received(self, new_messages): 31 | for message in new_messages: 32 | if message.type == 'chat': 33 | print("New message '{}' received from number {}".format(message.content, message.sender.id)) 34 | else: 35 | print("New message of type '{}' received from number {}".format(message.type, message.sender.id)) 36 | 37 | 38 | if __name__ == '__main__': 39 | run() 40 | -------------------------------------------------------------------------------- /sample/remote.py: -------------------------------------------------------------------------------- 1 | import os, sys, time, json 2 | from src import WhatsAPIDriver 3 | from src.objects.message import Message, MediaMessage 4 | 5 | print("Environment", os.environ) 6 | try: 7 | os.environ["SELENIUM"] 8 | except KeyError: 9 | print("Please set the environment variable SELENIUM to Selenium URL") 10 | sys.exit(1) 11 | 12 | ##Save session on "/firefox_cache/localStorage.json". 13 | ##Create the directory "/firefox_cache", it's on .gitignore 14 | ##The "app" directory is internal to docker, it corresponds to the root of the project. 15 | ##The profile parameter requires a directory not a file. 16 | profiledir = os.path.join(".", "firefox_cache") 17 | if not os.path.exists(profiledir): os.makedirs(profiledir) 18 | driver = WhatsAPIDriver(profile=profiledir, client='firefox', remote=True, command_executor=os.environ["SELENIUM"]) 19 | print("Waiting for QR") 20 | driver.wait_for_login() 21 | print("Saving session") 22 | driver.save_firefox_profile(remove_old=False) 23 | print("Bot started") 24 | 25 | while True: 26 | time.sleep(3) 27 | print('Checking for more messages, status', driver.get_status()) 28 | for contact in driver.get_unread(): 29 | for message in contact.messages: 30 | print(json.dumps(message.get_js_obj(), indent=4)) 31 | print('class', message.__class__.__name__) 32 | print('message', message) 33 | print('id', message.id) 34 | print('type', message.type) 35 | print('timestamp', message.timestamp) 36 | print('chat_id', message.chat_id) 37 | print('sender', message.sender) 38 | print('sender.id', message.sender.id) 39 | print('sender.safe_name', message.sender.get_safe_name()) 40 | if message.type == 'chat': 41 | print('-- Chat') 42 | print('safe_content', message.safe_content) 43 | print('content', message.content) 44 | # contact.chat.send_message(message.safe_content) 45 | elif message.type == 'image' or message.type == 'video': 46 | print('-- Image or Video') 47 | print('filename', message.filename) 48 | print('size', message.size) 49 | print('mime', message.mime) 50 | print('caption', message.caption) 51 | print('client_url', message.client_url) 52 | message.save_media('./') 53 | else: 54 | print('-- Other') 55 | -------------------------------------------------------------------------------- /sample/send_media.py: -------------------------------------------------------------------------------- 1 | from src import WhatsAPIDriver 2 | from src.objects.message import Message 3 | 4 | driver = WhatsAPIDriver(loadstyles=False) 5 | print("Waiting for QR") 6 | driver.wait_for_login() 7 | 8 | print("Bot started") 9 | 10 | try: 11 | 12 | phone_safe = "1122333333333" # Phone number with country code 13 | phone_whatsapp = "{}@c.us".format(phone_safe) # WhatsApp Chat ID 14 | image_path = "image.png" # Local path file 15 | caption = "Testing a media sender!" # The caption sent under the image, optional 16 | 17 | driver.send_media(image_path, phone_whatsapp, caption) # Expected send_media(self, path, chatid, caption) 18 | print("Media file was successfully sent to {}".format(phone_safe)) 19 | 20 | except: 21 | print("Error while trying to send the midia file.") 22 | -------------------------------------------------------------------------------- /sample/test_remote.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | from selenium import webdriver 3 | from selenium.webdriver.common.desired_capabilities import DesiredCapabilities 4 | 5 | print("Environment", os.environ) 6 | try: 7 | os.environ["SELENIUM"] 8 | except KeyError: 9 | print("Please set the environment variable SELENIUM to Selenium URL") 10 | sys.exit(1) 11 | 12 | driver = webdriver.Remote( 13 | command_executor=os.environ["SELENIUM"], 14 | desired_capabilities=DesiredCapabilities.FIREFOX 15 | ) 16 | 17 | print("Driver initialized") 18 | print("Getting https://web.whatsapp.com") 19 | driver.get("https://web.whatsapp.com") 20 | driver.save_screenshot('shot.png') 21 | print("Screenshot saved") 22 | driver.close() 23 | print("Driver closed") 24 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A setuptools based setup module. 2 | 3 | See: 4 | https://packaging.python.org/en/latest/distributing.html 5 | https://github.com/pypa/sampleproject 6 | """ 7 | 8 | import ast 9 | 10 | # To use a consistent encoding 11 | from codecs import open 12 | import os 13 | # Always prefer setuptools over distutils 14 | import setuptools 15 | from setuptools import setup 16 | 17 | PACKAGE_NAME = 'src' 18 | 19 | path = os.path.join(os.path.dirname(__file__), PACKAGE_NAME, '__init__.py') 20 | 21 | with open(path, 'r') as file: 22 | t = compile(file.read(), path, 'exec', ast.PyCF_ONLY_AST) 23 | for node in (n for n in t.body if isinstance(n, ast.Assign)): 24 | if len(node.targets) != 1: 25 | continue 26 | 27 | name = node.targets[0] 28 | if not isinstance(name, ast.Name) or \ 29 | name.id not in ('__version__', '__version_info__', 'VERSION'): 30 | continue 31 | 32 | v = node.value 33 | if isinstance(v, ast.Str): 34 | version = v.s 35 | break 36 | if isinstance(v, ast.Tuple): 37 | r = [] 38 | for e in v.elts: 39 | if isinstance(e, ast.Str): 40 | r.append(e.s) 41 | elif isinstance(e, ast.Num): 42 | r.append(str(e.n)) 43 | version = '.'.join(r) 44 | break 45 | 46 | setup( 47 | name='openwa', 48 | 49 | # Versions should comply with PEP440. For a discussion on single-sourcing 50 | # the version across setup.py and the project code, see 51 | # https://packaging.python.org/en/latest/single_source_version.html 52 | version=version, 53 | 54 | description='A python interface for Whatsapp Web', 55 | 56 | # Author details 57 | author='Mukul Hase', 58 | author_email='mukulhase@gmail.com', 59 | include_package_data=True, 60 | 61 | # Choose your license 62 | license='MIT', 63 | 64 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 65 | classifiers=[ 66 | # How mature is this project? Common values are 67 | # 3 - Alpha 68 | # 4 - Beta 69 | # 5 - Production/Stable 70 | 'Development Status :: 4 - Beta', 71 | 72 | # Indicate who your project is intended for 73 | 'Intended Audience :: Developers', 74 | 'Topic :: Communications :: Chat', 75 | 76 | # Pick your license as you wish (should match "license" above) 77 | 'License :: OSI Approved :: MIT License', 78 | 79 | # Specify the Python versions you support here. In particular, ensure 80 | # that you indicate whether you support Python 2, Python 3 or both. 81 | 'Programming Language :: Python :: 3.8', 82 | ], 83 | 84 | # What does your project relate to? 85 | keywords='Whatsapp Chat Bot Chatbot Selenium Web Whatsapp API', 86 | 87 | # You can just specify the packages manually here if your project is 88 | # simple. Or you can use find_packages(). 89 | packages=['openwa'], 90 | package_dir={'openwa': 'src'} 91 | ) 92 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | wa-automate-python module 3 | 4 | .. moduleauthor:: Mukul Hase , Adarsh Sanjeev 5 | """ 6 | 7 | import binascii 8 | import io 9 | import logging 10 | import os 11 | import re 12 | import shutil 13 | import tempfile 14 | from base64 import b64decode 15 | from io import BytesIO 16 | from json import dumps, loads 17 | 18 | import requests 19 | from PIL import Image 20 | from axolotl.kdf.hkdfv3 import HKDFv3 21 | from axolotl.util.byteutil import ByteUtil 22 | from cryptography.hazmat.backends import default_backend 23 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 24 | from selenium import webdriver 25 | from selenium.common.exceptions import NoSuchElementException 26 | from selenium.webdriver.common.by import By 27 | from selenium.webdriver.common.desired_capabilities import DesiredCapabilities 28 | from selenium.webdriver.firefox.options import Options 29 | from selenium.webdriver.support import expected_conditions as EC 30 | from selenium.webdriver.support.ui import WebDriverWait 31 | 32 | from .helper import convert_to_base64 33 | from .objects.chat import UserChat, factory_chat 34 | from .objects.contact import Contact 35 | from .objects.message import MessageGroup, factory_message 36 | from .objects.number_status import NumberStatus 37 | from .wapi_js_wrapper import WapiJsWrapper 38 | 39 | __version__ = '1.3.16' 40 | 41 | 42 | class WhatsAPIDriverStatus(object): 43 | Unknown = 'Unknown' 44 | NoDriver = 'NoDriver' 45 | NotConnected = 'NotConnected' 46 | NotLoggedIn = 'NotLoggedIn' 47 | LoggedIn = 'LoggedIn' 48 | 49 | 50 | class WhatsAPIException(Exception): 51 | pass 52 | 53 | 54 | class ChatNotFoundError(WhatsAPIException): 55 | pass 56 | 57 | 58 | class ContactNotFoundError(WhatsAPIException): 59 | pass 60 | 61 | 62 | class WhatsAPIDriver(object): 63 | """ 64 | This is our main driver objects. 65 | .. note:: 66 | Runs its own instance of selenium 67 | """ 68 | _PROXY = None 69 | 70 | _URL = "https://web.whatsapp.com" 71 | 72 | _LOCAL_STORAGE_FILE = 'localStorage.json' 73 | 74 | _SELECTORS = { 75 | 'qrCode': "canvas[aria-label=\"Scan me!\"]", 76 | 'qrCodePlain': "div[data-ref]", 77 | 'QRReloader': 'div[data-ref] > span > button', 78 | 'mainPage': ".two", 79 | } 80 | 81 | logger = logging.getLogger(__name__) 82 | driver = None 83 | 84 | # Profile points to the Firefox profile for firefox and Chrome cache for chrome 85 | # Do not alter this 86 | _profile = None 87 | 88 | def get_local_storage(self): 89 | return self.driver.execute_script('return window.localStorage;') 90 | 91 | def set_local_storage(self, data): 92 | self.driver.execute_script(''.join( 93 | ["window.localStorage.setItem('{}', '{}');".format(k, v.replace("\n", "\\n") if isinstance(v, str) else v) 94 | for k, v in data.items()])) 95 | 96 | def save_firefox_profile(self, remove_old=False): 97 | """Function to save the firefox profile to the permanant one""" 98 | self.logger.info("Saving profile from %s to %s" % (self._profile.path, self._profile_path)) 99 | 100 | if remove_old: 101 | if os.path.exists(self._profile_path): 102 | try: 103 | shutil.rmtree(self._profile_path) 104 | except OSError: 105 | pass 106 | 107 | shutil.copytree(os.path.join(self._profile.path), self._profile_path, 108 | ignore=shutil.ignore_patterns("parent.lock", "lock", ".parentlock")) 109 | else: 110 | for item in os.listdir(self._profile.path): 111 | if item in ["parent.lock", "lock", ".parentlock"]: 112 | continue 113 | s = os.path.join(self._profile.path, item) 114 | d = os.path.join(self._profile_path, item) 115 | if os.path.isdir(s): 116 | shutil.copytree(s, d, 117 | ignore=shutil.ignore_patterns("parent.lock", "lock", ".parentlock")) 118 | else: 119 | shutil.copy2(s, d) 120 | 121 | with open(os.path.join(self._profile_path, self._LOCAL_STORAGE_FILE), 'w') as f: 122 | f.write(dumps(self.get_local_storage())) 123 | 124 | def set_proxy(self, proxy): 125 | self.logger.info("Setting proxy to %s" % proxy) 126 | proxy_address, proxy_port = proxy.split(":") 127 | self._profile.set_preference("network.proxy.type", 1) 128 | self._profile.set_preference("network.proxy.http", proxy_address) 129 | self._profile.set_preference("network.proxy.http_port", int(proxy_port)) 130 | self._profile.set_preference("network.proxy.ssl", proxy_address) 131 | self._profile.set_preference("network.proxy.ssl_port", int(proxy_port)) 132 | 133 | def close(self): 134 | """Closes the selenium instance""" 135 | self.driver.close() 136 | 137 | def __init__(self, client="firefox", username="API", proxy=None, command_executor=None, loadstyles=False, 138 | profile=None, remote=False, headless=False, autoconnect=True, logger=None, extra_params=None, chrome_options=None, 139 | executable_path=None, script_timeout=60, element_timeout=30, license_key=None, wapi_version='master'): 140 | """Initialises the webdriver""" 141 | 142 | self.logger = logger or self.logger 143 | self.license_key = license_key 144 | self.remote = remote 145 | extra_params = extra_params or {} 146 | 147 | if profile is not None: 148 | self._profile_path = profile 149 | self.logger.info("Checking for profile at %s" % self._profile_path) 150 | if not os.path.exists(self._profile_path): 151 | self.logger.critical("Could not find profile at %s" % profile) 152 | raise WhatsAPIException("Could not find profile at %s" % profile) 153 | else: 154 | self._profile_path = None 155 | 156 | self.client = client.lower() 157 | if self.client == "firefox": 158 | if self._profile_path is not None: 159 | self._profile = webdriver.FirefoxProfile(self._profile_path) 160 | else: 161 | self._profile = webdriver.FirefoxProfile() 162 | if not loadstyles: 163 | # Disable CSS 164 | self._profile.set_preference('permissions.default.stylesheet', 2) 165 | # Disable images 166 | self._profile.set_preference('permissions.default.image', 2) 167 | # Disable Flash 168 | self._profile.set_preference('dom.ipc.plugins.enabled.libflashplayer.so', 169 | 'false') 170 | # Defaults to no proxy 171 | self._profile.set_preference("network.proxy.type", 0) 172 | if proxy is not None: 173 | self.set_proxy(proxy) 174 | 175 | options = Options() 176 | 177 | if headless: 178 | options.headless = True 179 | 180 | options.profile = self._profile 181 | 182 | capabilities = DesiredCapabilities.FIREFOX.copy() 183 | capabilities['webStorageEnabled'] = True 184 | 185 | 186 | if self.remote: 187 | self.logger.info("Starting Firefox remote webdriver") 188 | 189 | self.driver = webdriver.Remote( 190 | command_executor=command_executor, 191 | desired_capabilities=capabilities, 192 | options=options, 193 | **extra_params 194 | ) 195 | 196 | else: 197 | self.logger.info("Starting Firefox webdriver") 198 | 199 | if executable_path is not None: 200 | executable_path = os.path.abspath(executable_path) 201 | 202 | self.driver = webdriver.Firefox(capabilities=capabilities, options=options, 203 | executable_path=executable_path, 204 | **extra_params) 205 | else: 206 | self.driver = webdriver.Firefox(capabilities=capabilities, options=options, 207 | **extra_params) 208 | 209 | elif self.client == "chrome": 210 | self._profile = webdriver.ChromeOptions() 211 | 212 | if self._profile_path is not None: 213 | self._profile.add_argument("--user-data-dir=%s" % self._profile_path) 214 | if proxy is not None: 215 | self._profile.add_argument('--proxy-server=%s' % proxy) 216 | if headless: 217 | self._profile.add_argument('--headless') 218 | self._profile.add_argument('--disable-gpu') 219 | self._profile.add_argument('--remote-debugging-port=9222') 220 | self._profile.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36') 221 | if chrome_options is not None: 222 | for option in chrome_options: 223 | self._profile.add_argument(option) 224 | 225 | capabilities = DesiredCapabilities.CHROME.copy() 226 | 227 | if self.remote: 228 | self.logger.info("Starting Chrome remote webdriver") 229 | self.driver = webdriver.Remote( 230 | command_executor=command_executor, 231 | desired_capabilities=capabilities, 232 | options=self._profile, 233 | **extra_params 234 | ) 235 | 236 | else: 237 | self.logger.info("Starting Chrome webdriver") 238 | 239 | if executable_path is not None: 240 | self.driver = webdriver.Chrome(executable_path=executable_path, chrome_options=self._profile, **extra_params) 241 | else: 242 | self.driver = webdriver.Chrome(chrome_options=self._profile, **extra_params) 243 | 244 | elif client == 'remote': 245 | self.logger.warning("Client type 'remote' is deprecated and might be removed in future versions. Please, use the method attribute 'remote = True' instead.") 246 | 247 | if self._profile_path is not None: 248 | self._profile = webdriver.FirefoxProfile(self._profile_path) 249 | else: 250 | self._profile = webdriver.FirefoxProfile() 251 | 252 | options = Options() 253 | 254 | if headless: 255 | options.headless = True 256 | 257 | capabilities = DesiredCapabilities.FIREFOX.copy() 258 | self.driver = webdriver.Remote( 259 | command_executor=command_executor, 260 | desired_capabilities=capabilities, 261 | options=options, 262 | **extra_params 263 | ) 264 | 265 | else: 266 | self.logger.error("Invalid client: %s" % client) 267 | self.username = username 268 | self.wapi_functions = WapiJsWrapper(self.driver, self, wapi_version) 269 | 270 | self.driver.set_script_timeout(script_timeout) 271 | self.element_timeout = element_timeout 272 | 273 | if autoconnect: 274 | self.connect() 275 | 276 | def get_status(self): 277 | """ 278 | Returns status of the driver 279 | 280 | :return: Status 281 | :rtype: WhatsAPIDriverStatus 282 | """ 283 | if self.driver is None: 284 | return WhatsAPIDriverStatus.NotConnected 285 | if self.driver.session_id is None: 286 | return WhatsAPIDriverStatus.NotConnected 287 | try: 288 | self.driver.find_element_by_css_selector(self._SELECTORS['mainPage']) 289 | return WhatsAPIDriverStatus.LoggedIn 290 | except NoSuchElementException: 291 | pass 292 | try: 293 | self.driver.find_element_by_css_selector(self._SELECTORS['qrCode']) 294 | return WhatsAPIDriverStatus.NotLoggedIn 295 | except NoSuchElementException: 296 | pass 297 | return WhatsAPIDriverStatus.Unknown 298 | 299 | def connect(self): 300 | self.driver.get(self._URL) 301 | 302 | profilePath = "" 303 | if self.client == "chrome": 304 | profilePath = "" 305 | else: 306 | profilePath = self._profile.path 307 | 308 | local_storage_file = os.path.join(profilePath, self._LOCAL_STORAGE_FILE) 309 | if os.path.exists(local_storage_file): 310 | with open(local_storage_file) as f: 311 | self.set_local_storage(loads(f.read())) 312 | 313 | self.driver.refresh() 314 | 315 | def quit(self): 316 | self.wapi_functions.quit() 317 | self.driver.quit() 318 | 319 | ########################################### 320 | # API Methods 321 | ########################################### 322 | def is_logged_in(self): 323 | """Returns if user is logged. Can be used if non-block needed for wait_for_login""" 324 | 325 | # instead we use this (temporary) solution: 326 | # return 'class="app _3dqpi two"' in self.driver.page_source 327 | return self.driver.execute_script( 328 | "if (document.querySelector('*[data-icon=chat]') !== null) { return true } else { return false }") 329 | 330 | def is_connected(self): 331 | """Returns if user's phone is connected to the internet.""" 332 | return self.wapi_functions.isConnected() 333 | 334 | def wait_for_login(self, timeout=90): 335 | """ 336 | Waits for the app to log in or for the QR to appear 337 | :return: bool: True if has logged in, false if asked for QR 338 | """ 339 | WebDriverWait(self.driver, timeout).until(EC.visibility_of_element_located((By.CSS_SELECTOR, self._SELECTORS['mainPage'] + ',' + self._SELECTORS['qrCode']))) 340 | try: 341 | self.driver.find_element_by_css_selector(self._SELECTORS['mainPage']) 342 | return True 343 | except NoSuchElementException: 344 | self.driver.find_element_by_css_selector(self._SELECTORS['qrCode']) 345 | return False 346 | 347 | def screenshot(self, filename): 348 | self.driver.get_screenshot_as_file(filename) 349 | 350 | def get_battery_level(self): 351 | """ 352 | Check the battery level of device 353 | 354 | :return: int: Battery level 355 | """ 356 | return self.wapi_functions.getBatteryLevel() 357 | 358 | def set_presence(self, present): 359 | """ 360 | Set presence to 'online' or not 361 | """ 362 | return self.wapi_functions.setPresence(present) 363 | 364 | def set_profile_status(self, status): 365 | return self.wapi_functions.setMyStatus(status) 366 | 367 | def set_profile_name(self, name): 368 | return self.wapi_functions.setMyName(name) 369 | 370 | ################# 371 | # QR 372 | ################# 373 | def get_qr_plain(self): 374 | self.reload_qr() 375 | return WebDriverWait(self.driver, self.element_timeout) \ 376 | .until(EC.visibility_of_element_located((By.CSS_SELECTOR, self._SELECTORS['qrCodePlain']))) \ 377 | .get_attribute("data-ref") 378 | 379 | def get_qr(self, filename=None): 380 | """Get pairing QR code from client""" 381 | self.reload_qr() 382 | qr = WebDriverWait(self.driver, self.element_timeout) \ 383 | .until(EC.visibility_of_element_located((By.CSS_SELECTOR, self._SELECTORS['qrCode']))) 384 | 385 | if filename is None: 386 | fd, fn_png = tempfile.mkstemp(prefix=self.username, suffix='.png') 387 | else: 388 | fd = os.open(filename, os.O_RDWR | os.O_CREAT) 389 | fn_png = os.path.abspath(filename) 390 | self.logger.debug("QRcode image saved at %s" % fn_png) 391 | qr.screenshot(fn_png) 392 | os.close(fd) 393 | return fn_png 394 | 395 | def get_qr_base64(self): 396 | self.reload_qr() 397 | qr = WebDriverWait(self.driver, self.element_timeout) \ 398 | .until(EC.visibility_of_element_located((By.CSS_SELECTOR, self._SELECTORS['qrCode']))) 399 | 400 | return qr.screenshot_as_base64 401 | 402 | def reload_qr(self): 403 | try: 404 | self.driver.find_element_by_css_selector(self._SELECTORS['QRReloader']).click() 405 | except NoSuchElementException: 406 | pass 407 | 408 | ################# 409 | # Chats & Contacts 410 | ################# 411 | def get_contact_from_id(self, contact_id): 412 | """ 413 | Fetches a contact given its ID 414 | 415 | :param contact_id: Contact ID 416 | :type contact_id: str 417 | :return: Contact or Error 418 | :rtype: Contact 419 | """ 420 | contact = self.wapi_functions.getContact(contact_id) 421 | 422 | if contact is None: 423 | raise ContactNotFoundError("Contact {0} not found".format(contact_id)) 424 | 425 | return Contact(contact, self) 426 | 427 | def get_chat_from_id(self, chat_id): 428 | """ 429 | Fetches a chat given its ID 430 | 431 | :param chat_id: Chat ID 432 | :type chat_id: str 433 | :return: Chat or Error 434 | :rtype: Chat 435 | """ 436 | chat = self.wapi_functions.getChatById(chat_id) 437 | if chat: 438 | return factory_chat(chat, self) 439 | 440 | raise ChatNotFoundError("Chat {0} not found".format(chat_id)) 441 | 442 | def get_chat_from_name(self, chat_name): 443 | """ 444 | Fetches a chat given its name 445 | 446 | :param chat_name: Chat name 447 | :type chat_name: str 448 | :return: Chat or Error 449 | :rtype: Chat 450 | """ 451 | chat = self.wapi_functions.getChatByName(chat_name) 452 | if chat: 453 | return factory_chat(chat, self) 454 | 455 | raise ChatNotFoundError("Chat {0} not found".format(chat_name)) 456 | 457 | def get_chat_from_phone_number(self, number): 458 | """ 459 | Gets chat by phone number 460 | Number format should be as it appears in Whatsapp ID 461 | For example, for the number: 462 | +972-51-234-5678 463 | This function would receive: 464 | 972512345678 465 | 466 | :param number: Phone number 467 | :return: Chat 468 | :rtype: Chat 469 | """ 470 | for chat in self.get_all_chats(): 471 | if not isinstance(chat, UserChat) or number not in chat.id: 472 | continue 473 | return chat 474 | 475 | raise ChatNotFoundError('Chat for phone {0} not found'.format(number)) 476 | 477 | def get_contacts(self): 478 | """ 479 | Fetches list of all contacts 480 | This will return chats with people from the address book only 481 | Use get_all_chats for all chats 482 | 483 | :return: List of contacts 484 | :rtype: list[Contact] 485 | """ 486 | all_contacts = self.wapi_functions.getAllContacts() 487 | return [Contact(contact, self) for contact in all_contacts] 488 | 489 | def get_my_contacts(self): 490 | """ 491 | Fetches list of added contacts 492 | 493 | :return: List of contacts 494 | :rtype: list[Contact] 495 | """ 496 | my_contacts = self.wapi_functions.getMyContacts() 497 | return [Contact(contact, self) for contact in my_contacts] 498 | 499 | def get_all_groups(self): 500 | chats = self.wapi_functions.getAllGroups() 501 | if chats: 502 | return [factory_chat(chat, self) for chat in chats] 503 | else: 504 | return [] 505 | 506 | def get_all_chats(self): 507 | """ 508 | Fetches all chats 509 | 510 | :return: List of chats 511 | :rtype: list[Chat] 512 | """ 513 | chats = self.wapi_functions.getAllChats() 514 | if chats: 515 | return [factory_chat(chat, self) for chat in chats] 516 | else: 517 | return [] 518 | 519 | def open_chat(self, chat_id): 520 | return self.wapi_functions.openChat(chat_id) 521 | 522 | def get_all_chat_ids(self): 523 | """ 524 | Fetches all chat ids 525 | 526 | :return: List of chat ids 527 | :rtype: list[str] 528 | """ 529 | return self.wapi_functions.getAllChatIds() 530 | 531 | def chat_send_seen(self, chat_id): 532 | """ 533 | Send a seen to a chat given its ID 534 | 535 | :param chat_id: Chat ID 536 | :type chat_id: str 537 | """ 538 | return self.wapi_functions.sendSeen(chat_id) 539 | 540 | def chat_load_earlier_messages(self, chat_id): 541 | self.wapi_functions.loadEarlierMessages(chat_id) 542 | 543 | def chat_load_all_earlier_messages(self, chat_id): 544 | self.wapi_functions.loadAllEarlierMessages(chat_id) 545 | 546 | def async_chat_load_all_earlier_messages(self, chat_id): 547 | self.wapi_functions.asyncLoadAllEarlierMessages(chat_id) 548 | 549 | def are_all_messages_loaded(self, chat_id): 550 | return self.wapi_functions.areAllMessagesLoaded(chat_id) 551 | 552 | def get_profile_pic_from_id(self, id): 553 | profile_pic_url = self.wapi_functions.getProfilePicFromServer(id) 554 | if profile_pic_url: 555 | return requests.get(profile_pic_url).content 556 | else: 557 | return False 558 | 559 | def check_number_status(self, number_id): 560 | """ 561 | Check if a number is valid/registered in the whatsapp service 562 | 563 | :param number_id: number id 564 | :return: 565 | """ 566 | number_status = self.wapi_functions.checkNumberStatus(number_id) 567 | return NumberStatus(number_status, self) 568 | 569 | def contact_block(self, id): 570 | return self.wapi_functions.contactBlock(id) 571 | 572 | def contact_unblock(self, id): 573 | return self.wapi_functions.contactUnblock(id) 574 | 575 | def is_chat_online(self, chat_id): 576 | return self.wapi_functions.isChatOnline(chat_id) 577 | 578 | def get_chat_last_seen(self, chat_id): 579 | return self.wapi_functions.getLastSeen(chat_id) 580 | 581 | def delete_chat(self, chat_id): 582 | """ 583 | Delete a chat 584 | 585 | :param chat_id: id of chat 586 | :return: 587 | """ 588 | return self.wapi_functions.deleteConversation(chat_id) 589 | 590 | ################# 591 | # Messages 592 | ################# 593 | def get_unread(self, include_me=False, include_notifications=False, use_unread_count=False): 594 | """ 595 | Fetches unread messages 596 | :param include_me: Include user's messages 597 | :type include_me: bool or None 598 | :param include_notifications: Include events happening on chat 599 | :type include_notifications: bool or None 600 | :param use_unread_count: If set uses chat's 'unreadCount' attribute to fetch last n messages from chat 601 | :type use_unread_count: bool 602 | :return: List of unread messages grouped by chats 603 | :rtype: list[MessageGroup] 604 | """ 605 | raw_message_groups = self.wapi_functions.getUnreadMessages(include_me, include_notifications, use_unread_count) 606 | 607 | unread_messages = [] 608 | for raw_message_group in raw_message_groups: 609 | chat = factory_chat(raw_message_group, self) 610 | messages = list( 611 | filter(None.__ne__, [factory_message(message, self) for message in raw_message_group['messages']])) 612 | messages.sort(key=lambda message: message.timestamp) 613 | unread_messages.append(MessageGroup(chat, messages)) 614 | 615 | return unread_messages 616 | 617 | def get_unread_messages_in_chat(self, 618 | id, 619 | include_me=False, 620 | include_notifications=False): 621 | """ 622 | I fetch unread messages from an asked chat. 623 | 624 | :param id: chat id 625 | :type id: str 626 | :param include_me: if user's messages are to be included 627 | :type include_me: bool 628 | :param include_notifications: if events happening on chat are to be included 629 | :type include_notifications: bool 630 | :return: list of unread messages from asked chat 631 | :rtype: list 632 | """ 633 | # get unread messages 634 | messages = self.wapi_functions.getUnreadMessagesInChat( 635 | id, 636 | include_me, 637 | include_notifications 638 | ) 639 | 640 | # process them 641 | unread = [factory_message(message, self) for message in messages] 642 | 643 | # return them 644 | return unread 645 | 646 | # get_unread_messages_in_chat() 647 | 648 | def get_all_messages_in_chat(self, chat, include_me=False, include_notifications=False): 649 | """ 650 | Fetches messages in chat 651 | 652 | :param include_me: Include user's messages 653 | :type include_me: bool or None 654 | :param include_notifications: Include events happening on chat 655 | :type include_notifications: bool or None 656 | :return: List of messages in chat 657 | :rtype: list[Message] 658 | """ 659 | message_objs = self.wapi_functions.getAllMessagesInChat(chat.id, include_me, include_notifications) 660 | 661 | for message in message_objs: 662 | yield (factory_message(message, self)) 663 | 664 | def get_all_message_ids_in_chat(self, chat, include_me=False, include_notifications=False): 665 | """ 666 | Fetches message ids in chat 667 | 668 | :param include_me: Include user's messages 669 | :type include_me: bool or None 670 | :param include_notifications: Include events happening on chat 671 | :type include_notifications: bool or None 672 | :return: List of message ids in chat 673 | :rtype: list[str] 674 | """ 675 | return self.wapi_functions.getAllMessageIdsInChat(chat.id, include_me, include_notifications) 676 | 677 | def get_message_by_id(self, message_id): 678 | """ 679 | Fetch a message 680 | 681 | :param message_id: Message ID 682 | :type message_id: str 683 | :return: Message or False 684 | :rtype: Message 685 | """ 686 | result = self.wapi_functions.getMessageById(message_id) 687 | 688 | if result: 689 | result = factory_message(result, self) 690 | 691 | return result 692 | 693 | def chat_send_message(self, chat_id, message): 694 | result = self.wapi_functions.sendMessage(chat_id, message) 695 | 696 | return isinstance(result, str) 697 | 698 | def reply_message(self, chat_id, message_id, message): 699 | result = self.wapi_functions.reply(chat_id, message, message_id) 700 | 701 | return bool(result) 702 | 703 | def forward_messages(self, chat_id_to, message_ids, skip_my_messages=False): 704 | return self.wapi_functions.forwardMessages(chat_id_to, message_ids, skip_my_messages) 705 | 706 | def send_image_as_sticker(self, path, chatid): 707 | """ 708 | Converts the file to base64 and sends it using the sendImageAsSticker function of wapi.js 709 | :param path: file path 710 | :param chatid: chatId to be sent 711 | :param caption: 712 | :return: 713 | """ 714 | img = Image.open(path) 715 | img.thumbnail((512, 512)) 716 | webp_img = io.BytesIO() 717 | img.save(webp_img, 'webp') 718 | webp_img.seek(0) 719 | imgBase64 = convert_to_base64(webp_img, is_thumbnail=True) 720 | return self.wapi_functions.sendImageAsSticker(imgBase64, chatid, {}) 721 | 722 | def send_media(self, path, chatid, caption): 723 | """ 724 | Converts the file to base64 and sends it using the sendImage function of wapi.js 725 | :param path: file path 726 | :param chatid: chatId to be sent 727 | :param caption: 728 | :return: 729 | """ 730 | base64 = convert_to_base64(path) 731 | filename = os.path.split(path)[-1] 732 | return self.wapi_functions.sendImage(base64, chatid, filename, caption) 733 | 734 | def send_voice_note(self, path, chatid): 735 | """ 736 | Attempts to send a file as a voice note 737 | """ 738 | base64 = convert_to_base64(path) 739 | return self.wapi_functions.sendImage(base64, chatid, 'ptt.ogg', '', None, True, True) 740 | 741 | def send_video_as_gif(self, path, chatid, caption): 742 | """ 743 | :param path: video file path 744 | :param chatid: chatId to be sent 745 | :param caption: text to send with video 746 | :return: 747 | """ 748 | imgBase64 = convert_to_base64(path) 749 | filename = os.path.split(path)[-1] 750 | return self.wapi_functions.sendVideoAsGif(imgBase64, chatid, filename, caption) 751 | 752 | def send_giphy(self, giphy_url, chat_id, caption): 753 | match = re.search(r'https?:\/\/media\.giphy\.com\/media\/([a-zA-Z0-9]+)', giphy_url) 754 | if match: 755 | giphy_id = match.group(1) 756 | filename = giphy_id + '.mp4' 757 | resp = requests.get('https://i.giphy.com/' + filename) 758 | b64 = convert_to_base64(io.BytesIO(resp.content)) 759 | return self.wapi_functions.sendVideoAsGif(b64, chat_id, filename, caption) 760 | 761 | def send_contact(self, chat_id, contact_ids): 762 | return self.wapi_functions.sendContact(chat_id, contact_ids) 763 | 764 | def send_location(self, chat_id, lat, long, text): 765 | return self.wapi_functions.sendLocation(chat_id, lat, long, text) 766 | 767 | def send_message_with_thumbnail(self, path, chatid, url, title, description, text): 768 | """ 769 | converts the file to base64 and sends it using the sendImage function of wapi.js 770 | :param path: image file path 771 | :param chatid: chatId to be sent 772 | :param url: of thumbnail 773 | :param title: of thumbnail 774 | :param description: of thumbnail 775 | :return: 776 | """ 777 | imgBase64 = convert_to_base64(path, is_thumbnail=True) 778 | return self.wapi_functions.sendMessageWithThumb(imgBase64, url, title, description, text, chatid) 779 | 780 | def send_message_with_auto_preview(self, chat_id, url, text): 781 | return self.wapi_functions.sendLinkWithAutoPreview(chat_id, url, text) 782 | 783 | def delete_message(self, chat_id, message_array, revoke=False): 784 | """ 785 | Delete a chat 786 | 787 | :param chat_id: id of chat 788 | :param message_array: one or more message(s) id 789 | :param revoke: Set to true so the message will be deleted for everyone, not only you 790 | :return: 791 | """ 792 | return self.wapi_functions.deleteMessage(chat_id, message_array, revoke) 793 | 794 | def set_typing_simulation(self, chat_id, typing): 795 | return self.wapi_functions.simulateTyping(chat_id, typing) 796 | 797 | def download_media(self, media_msg, force_download=False): 798 | if not force_download: 799 | try: 800 | if media_msg.content: 801 | return BytesIO(b64decode(media_msg.content)) 802 | except AttributeError: 803 | pass 804 | 805 | file_data = b64decode(self.wapi_functions.downloadFile(media_msg.client_url)) 806 | 807 | if not file_data: 808 | raise Exception('Impossible to download file') 809 | 810 | media_key = b64decode(media_msg.media_key) 811 | derivative = HKDFv3().deriveSecrets(media_key, 812 | binascii.unhexlify(media_msg.crypt_keys[media_msg.type]), 813 | 112) 814 | 815 | parts = ByteUtil.split(derivative, 16, 32) 816 | iv = parts[0] 817 | cipher_key = parts[1] 818 | e_file = file_data[:-10] 819 | 820 | cr_obj = Cipher(algorithms.AES(cipher_key), modes.CBC(iv), backend=default_backend()) 821 | decryptor = cr_obj.decryptor() 822 | return BytesIO(decryptor.update(e_file) + decryptor.finalize()) 823 | 824 | ################# 825 | # Groups 826 | ################# 827 | def contact_get_common_groups(self, contact_id): 828 | """ 829 | Returns groups common between a user and the contact with given id. 830 | 831 | :return: Contact or Error 832 | :rtype: Contact 833 | """ 834 | for group in self.wapi_functions.getCommonGroups(contact_id): 835 | yield factory_chat(group, self) 836 | 837 | def create_group(self, name, chat_ids): 838 | return self.wapi_functions.createGroup(name, chat_ids) 839 | 840 | def group_get_participants_ids(self, group_id): 841 | return self.wapi_functions.getGroupParticipantIDs(group_id) 842 | 843 | def group_get_participants(self, group_id): 844 | participant_ids = self.group_get_participants_ids(group_id) 845 | 846 | for participant_id in participant_ids: 847 | yield self.get_contact_from_id(participant_id) 848 | 849 | def group_get_admin_ids(self, group_id): 850 | return self.wapi_functions.getGroupAdmins(group_id) 851 | 852 | def group_get_admins(self, group_id): 853 | admin_ids = self.group_get_admin_ids(group_id) 854 | 855 | for admin_id in admin_ids: 856 | yield self.get_contact_from_id(admin_id) 857 | 858 | def add_participant_group(self, idGroup, idParticipant): 859 | return self.wapi_functions.addParticipant(idGroup, idParticipant) 860 | 861 | def remove_participant_group(self, idGroup, idParticipant): 862 | return self.wapi_functions.removeParticipant(idGroup, idParticipant) 863 | 864 | def promove_participant_admin_group(self, idGroup, idParticipant): 865 | return self.wapi_functions.promoteParticipant(idGroup, idParticipant) 866 | 867 | def demote_participant_admin_group(self, idGroup, idParticipant): 868 | return self.wapi_functions.demoteParticipant(idGroup, idParticipant) 869 | 870 | def leave_group(self, chat_id): 871 | """ 872 | Leave a group 873 | 874 | :param chat_id: id of group 875 | :return: 876 | """ 877 | return self.wapi_functions.leaveGroup(chat_id) 878 | 879 | ################# 880 | # Observer 881 | ################# 882 | def subscribe_new_messages(self, observer): 883 | self.wapi_functions.new_messages_observable.subscribe_new_messages(observer) 884 | 885 | def subscribe_acks(self, observer): 886 | self.wapi_functions.new_messages_observable.subscribe_acks(observer) 887 | 888 | def subscribe_live_location_updates(self, observer, chat_id): 889 | self.wapi_functions.new_messages_observable.subscribe_live_location_updates(observer, chat_id) 890 | 891 | def unsubscribe_new_messages(self, observer): 892 | self.wapi_functions.new_messages_observable.unsubscribe_new_messages(observer) 893 | 894 | def unsubscribe_acks(self, observer): 895 | self.wapi_functions.new_messages_observable.unsubscribe_acks(observer) 896 | 897 | def unsubscribe_live_location_updates(self, observer, chat_id): 898 | self.wapi_functions.new_messages_observable.unsubscribe_live_location_updates(observer, chat_id) 899 | -------------------------------------------------------------------------------- /src/helper.py: -------------------------------------------------------------------------------- 1 | import io 2 | from base64 import b64encode 3 | 4 | import magic 5 | import six 6 | 7 | 8 | def safe_str(text): 9 | if not text: 10 | return "(empty)" 11 | assert isinstance(text, six.string_types), "obj is not a string: %r" % text 12 | return str(text.encode('utf-8').decode('ascii', 'ignore')) if text else "(empty)" 13 | 14 | 15 | def convert_to_base64(source, is_thumbnail=False): 16 | """ 17 | :param source: file source 18 | :return: returns the converted string and formatted for the send media function send_media 19 | """ 20 | if type(source) is str: 21 | with open(source, "rb") as image_file: 22 | source = io.BytesIO(image_file.read()) 23 | archive = b64encode(source.read()) 24 | archive = archive.decode('utf-8') 25 | if not is_thumbnail: 26 | mime = magic.Magic(mime=True) 27 | source.seek(0) 28 | content_type = mime.from_buffer(source.read()) 29 | archive = 'data:' + content_type + ';base64,' + archive 30 | return archive 31 | -------------------------------------------------------------------------------- /src/js/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "mocha": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "ecmaVersion": 2017 11 | }, 12 | "rules": { 13 | "array-bracket-spacing": ["error", "never"], 14 | "brace-style": ["error", "allman", {"allowSingleLine": true}], 15 | "comma-spacing": ["error"], 16 | "comma-style": [ 17 | "error", 18 | "last", 19 | { 20 | "exceptions": { 21 | "ArrayExpression": false, 22 | "ArrayPattern": false, 23 | "ArrowFunctionExpression": false, 24 | "CallExpression": false, 25 | "FunctionDeclaration": false, 26 | "FunctionExpression": false, 27 | "ImportDeclaration": false, 28 | "ObjectExpression": false, 29 | "ObjectPattern": false, 30 | "VariableDeclaration": false 31 | } 32 | } 33 | ], 34 | "complexity": ["error", 7], 35 | "consistent-return": ["error"], 36 | "eol-last": ["error"], 37 | "eqeqeq": ["error"], 38 | "func-call-spacing": ["error"], 39 | "guard-for-in": ["error"], 40 | "indent": [ 41 | "error", 42 | 4, 43 | { 44 | "ArrayExpression": "first", 45 | "CallExpression": {"arguments": "first"}, 46 | "ObjectExpression": "first", 47 | "SwitchCase": 1 48 | } 49 | ], 50 | "linebreak-style": ["error", "unix"], 51 | "lines-around-comment": [ 52 | "error", 53 | { 54 | "allowArrayStart": true, 55 | "allowBlockStart": true, 56 | "allowObjectStart": true, 57 | "beforeBlockComment": true, 58 | "beforeLineComment": true 59 | } 60 | ], 61 | "key-spacing": ["error"], 62 | "keyword-spacing": ["error"], 63 | "max-len": ["error"], 64 | "max-statements": ["error", {"max": 20}], 65 | "new-cap": ["error"], 66 | "new-parens": ["error"], 67 | "no-alert": ["error"], 68 | "no-caller": ["error"], 69 | "no-else-return": ["error"], 70 | "no-empty": ["error", {"allowEmptyCatch": true}], 71 | "no-eval": ["error"], 72 | "no-floating-decimal": ["error"], 73 | "no-implicit-coercion": ["error"], 74 | "no-implicit-globals": ["error"], 75 | "no-implied-eval": ["error"], 76 | "no-invalid-this": ["error"], 77 | "no-iterator": ["error"], 78 | "no-labels": ["error"], 79 | "no-lonely-if": ["error"], 80 | "no-multi-spaces": ["error"], 81 | "no-multi-str": ["error"], 82 | "no-new": ["error"], 83 | "no-new-func": ["error"], 84 | "no-new-wrappers": ["error"], 85 | "no-octal-escape": ["error"], 86 | "no-param-reassign": ["error", {"props": true}], 87 | "no-proto": ["error"], 88 | "no-return-assign": ["error", "always"], 89 | "no-script-url": ["error"], 90 | "no-self-compare": ["error"], 91 | "no-trailing-spaces": ["error"], 92 | "no-unused-expressions": [ 93 | "error", 94 | { 95 | "allowShortCircuit": true, 96 | "allowTaggedTemplates": true, 97 | "allowTernary": true 98 | } 99 | ], 100 | "no-unused-vars": ["error", {"args": "none"}], 101 | "no-use-before-define": ["error"], 102 | "no-var": ["error"], 103 | "object-curly-spacing": ["error", "never"], 104 | "quotes": ["error", "single"], 105 | "padded-blocks": [ 106 | "error", 107 | { 108 | "blocks": "never", 109 | "classes": "never", 110 | "switches": "never" 111 | } 112 | ], 113 | "semi": ["error", "always"], 114 | "semi-spacing": ["error"], 115 | "space-before-function-paren": ["error", "always"], 116 | "space-in-parens": ["error", "never"], 117 | "space-infix-ops": ["error"], 118 | "space-unary-ops": ["error", {"words": true, "nonwords": false}], 119 | "strict": ["error", "global"] 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /src/js/pywapi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Observable functions. 3 | */ 4 | window._WAPI._newAcksBuffer = (sessionStorage.getItem('saved_acks') != null) ? JSON.parse(sessionStorage.getItem('saved_acks')) : []; 5 | window._WAPI._participantChangesBuffer = (sessionStorage.getItem('parti_changes') != null) ? JSON.parse(sessionStorage.getItem('parti_changes')) : []; 6 | window._WAPI._liveLocUpdatesBuffer = (sessionStorage.getItem('liveloc_updates') != null) ? JSON.parse(sessionStorage.getItem('liveloc_updates')) : []; 7 | window._WAPI._newMessagesBuffer = (sessionStorage.getItem('saved_msgs') != null) ? JSON.parse(sessionStorage.getItem('saved_msgs')) : []; 8 | window.WAPI.onLiveLocation = function (chatId, callback) { 9 | var lLChat = Store.LiveLocation.get(chatId); 10 | if (lLChat) { 11 | var validLocs = lLChat.participants.validLocations(); 12 | validLocs.map(x => x.on('change:lastUpdated', (x, y, z) => { 13 | console.log(x, y, z); 14 | const {id, lat, lng, accuracy, degrees, speed, lastUpdated} = x; 15 | const l = { 16 | id: id.toString(), lat, lng, accuracy, degrees, speed, lastUpdated 17 | }; 18 | WAPI._liveLocUpdatesBuffer.push(l); 19 | callback(l); 20 | })); 21 | return true; 22 | } else { 23 | return false; 24 | } 25 | } 26 | 27 | window.WAPI.onParticipantsChanged = function (groupId, callback) { 28 | const subtypeEvents = [ 29 | "invite", 30 | "add", 31 | "remove", 32 | "leave", 33 | "promote", 34 | "demote" 35 | ]; 36 | const events = [ 37 | 'change:isAdmin', 38 | 'remove', 39 | 'add' 40 | ] 41 | const chat = window.Store.Chat.get(groupId); 42 | chat.groupMetadata.participants.on('all', (eventName, eventData, extra) => { 43 | if (events.includes(eventName)) { 44 | let action = eventName; 45 | if (eventName == 'change:isAdmin') { 46 | action = extra ? 'promote' : 'demote'; 47 | } 48 | let event = { 49 | id: groupId, 50 | by: undefined, 51 | action: action, 52 | who: eventData.id._serialized 53 | }; 54 | _WAPI._participantChangesBuffer.push(event); 55 | callback(event); 56 | } 57 | }) 58 | } 59 | 60 | window.WAPI._onParticipantsChanged = function (groupId, callback) { 61 | const subtypeEvents = [ 62 | "invite", 63 | "add", 64 | "remove", 65 | "leave", 66 | "promote", 67 | "demote" 68 | ]; 69 | const chat = window.Store.Chat.get(groupId); 70 | //attach all group Participants to the events object as 'add' 71 | const metadata = window.Store.GroupMetadata.get(groupId); 72 | if (!groupParticpiantsEvents[groupId]) { 73 | groupParticpiantsEvents[groupId] = {}; 74 | metadata.participants.forEach(participant => { 75 | groupParticpiantsEvents[groupId][participant.id.toString()] = { 76 | subtype: "add", 77 | from: metadata.owner 78 | } 79 | }); 80 | } 81 | let i = 0; 82 | chat.on("change:groupMetadata.participants", 83 | _ => chat.on("all", (x, y) => { 84 | const {isGroup, previewMessage} = y; 85 | if (isGroup && x === "change" && previewMessage && previewMessage.type === "gp2" && subtypeEvents.includes(previewMessage.subtype)) { 86 | const {subtype, author, recipients} = previewMessage; 87 | const rec = recipients[0].toString(); 88 | if (groupParticpiantsEvents[groupId][rec] && groupParticpiantsEvents[groupId][recipients[0]].subtype == subtype) { 89 | //ignore, this is a duplicate entry 90 | // console.log('duplicate event') 91 | } else { 92 | //ignore the first message 93 | if (i == 0) { 94 | //ignore it, plus 1, 95 | i++; 96 | } else { 97 | groupParticpiantsEvents[groupId][rec] = {subtype, author}; 98 | //fire the callback 99 | // // previewMessage.from.toString() 100 | // x removed y 101 | // x added y 102 | let event = { 103 | id: groupId, 104 | by: author.toString(), 105 | action: subtype, 106 | who: recipients 107 | }; 108 | callback(event); 109 | _WAPI._participantChangesBuffer.push(event); 110 | chat.off("all", this) 111 | i = 0; 112 | } 113 | } 114 | } 115 | }) 116 | ) 117 | return true; 118 | } 119 | 120 | window.WAPI.onMessage(msg=>{ 121 | window._WAPI._newMessagesBuffer.push(msg); 122 | }) 123 | 124 | WAPI.getBufferedEvents = function () { 125 | let bufferedEvents = { 126 | 'new_msgs': _WAPI._newMessagesBuffer, 127 | 'new_acks': _WAPI._newAcksBuffer, 128 | 'parti_changes': _WAPI._participantChangesBuffer, 129 | 'liveloc_updates': _WAPI._liveLocUpdatesBuffer, 130 | }; 131 | _WAPI._newMessagesBuffer = []; 132 | _WAPI._newAcksBuffer = []; 133 | _WAPI._participantChangesBuffer = []; 134 | _WAPI._liveLocUpdatesBuffer = []; 135 | 136 | return bufferedEvents; 137 | }; 138 | 139 | window.WAPI._newAcksListener = Store.Msg.on("change:ack", msg => { 140 | let message = window.WAPI.processMessageObj(msg, true, false); 141 | if (message) { 142 | window._WAPI._newAcksBuffer.push(message) 143 | } 144 | }); 145 | 146 | /** 147 | * This next line is jsSha 148 | */ 149 | 'use strict'; 150 | (function (I) { 151 | function w(c, a, d) { 152 | var l = 0, b = [], g = 0, f, n, k, e, h, q, y, p, m = !1, t = [], r = [], u, z = !1; 153 | d = d || {}; 154 | f = d.encoding || "UTF8"; 155 | u = d.numRounds || 1; 156 | if (u !== parseInt(u, 10) || 1 > u) throw Error("numRounds must a integer >= 1"); 157 | if (0 === c.lastIndexOf("SHA-", 0)) if (q = function (b, a) { 158 | return A(b, a, c) 159 | }, y = function (b, a, l, f) { 160 | var g, e; 161 | if ("SHA-224" === c || "SHA-256" === c) g = (a + 65 >>> 9 << 4) + 15, e = 16; else throw Error("Unexpected error in SHA-2 implementation"); 162 | for (; b.length <= g;) b.push(0); 163 | b[a >>> 5] |= 128 << 24 - a % 32; 164 | a = a + l; 165 | b[g] = a & 4294967295; 166 | b[g - 1] = a / 4294967296 | 0; 167 | l = b.length; 168 | for (a = 0; a < l; a += e) f = A(b.slice(a, a + e), f, c); 169 | if ("SHA-224" === c) b = [f[0], f[1], f[2], f[3], f[4], f[5], f[6]]; else if ("SHA-256" === c) b = f; else throw Error("Unexpected error in SHA-2 implementation"); 170 | return b 171 | }, p = function (b) { 172 | return b.slice() 173 | }, "SHA-224" === c) h = 512, e = 224; else if ("SHA-256" === c) h = 512, e = 256; else throw Error("Chosen SHA variant is not supported"); else throw Error("Chosen SHA variant is not supported"); 174 | k = B(a, f); 175 | n = x(c); 176 | this.setHMACKey = function (b, a, g) { 177 | var e; 178 | if (!0 === m) throw Error("HMAC key already set"); 179 | if (!0 === z) throw Error("Cannot set HMAC key after calling update"); 180 | f = (g || {}).encoding || "UTF8"; 181 | a = B(a, f)(b); 182 | b = a.binLen; 183 | a = a.value; 184 | e = h >>> 3; 185 | g = e / 4 - 1; 186 | if (e < b / 8) { 187 | for (a = y(a, b, 0, x(c)); a.length <= g;) a.push(0); 188 | a[g] &= 4294967040 189 | } else if (e > b / 8) { 190 | for (; a.length <= g;) a.push(0); 191 | a[g] &= 4294967040 192 | } 193 | for (b = 0; b <= g; b += 1) t[b] = a[b] ^ 909522486, r[b] = a[b] ^ 1549556828; 194 | n = q(t, n); 195 | l = h; 196 | m = !0 197 | }; 198 | this.update = function (a) { 199 | var c, f, e, d = 0, p = h >>> 5; 200 | c = k(a, b, g); 201 | a = c.binLen; 202 | f = c.value; 203 | c = a >>> 5; 204 | for (e = 0; e < c; e += p) d + h <= a && (n = q(f.slice(e, e + p), n), d += h); 205 | l += d; 206 | b = f.slice(d >>> 5); 207 | g = a % h; 208 | z = !0 209 | }; 210 | this.getHash = function (a, f) { 211 | var d, h, k, q; 212 | if (!0 === m) throw Error("Cannot call getHash after setting HMAC key"); 213 | k = C(f); 214 | switch (a) { 215 | case"HEX": 216 | d = function (a) { 217 | return D(a, e, k) 218 | }; 219 | break; 220 | case"B64": 221 | d = function (a) { 222 | return E(a, e, k) 223 | }; 224 | break; 225 | case"BYTES": 226 | d = function (a) { 227 | return F(a, e) 228 | }; 229 | break; 230 | case"ARRAYBUFFER": 231 | try { 232 | h = new ArrayBuffer(0) 233 | } catch (v) { 234 | throw Error("ARRAYBUFFER not supported by this environment"); 235 | } 236 | d = function (a) { 237 | return G(a, e) 238 | }; 239 | break; 240 | default: 241 | throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER"); 242 | } 243 | q = y(b.slice(), g, l, p(n)); 244 | for (h = 1; h < u; h += 1) q = y(q, e, 0, x(c)); 245 | return d(q) 246 | }; 247 | this.getHMAC = function (a, f) { 248 | var d, k, t, u; 249 | if (!1 === m) throw Error("Cannot call getHMAC without first setting HMAC key"); 250 | t = C(f); 251 | switch (a) { 252 | case"HEX": 253 | d = function (a) { 254 | return D(a, e, t) 255 | }; 256 | break; 257 | case"B64": 258 | d = function (a) { 259 | return E(a, e, t) 260 | }; 261 | break; 262 | case"BYTES": 263 | d = function (a) { 264 | return F(a, e) 265 | }; 266 | break; 267 | case"ARRAYBUFFER": 268 | try { 269 | d = new ArrayBuffer(0) 270 | } catch (v) { 271 | throw Error("ARRAYBUFFER not supported by this environment"); 272 | } 273 | d = function (a) { 274 | return G(a, e) 275 | }; 276 | break; 277 | default: 278 | throw Error("outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER"); 279 | } 280 | k = y(b.slice(), g, l, p(n)); 281 | u = q(r, x(c)); 282 | u = y(k, e, h, u); 283 | return d(u) 284 | } 285 | } 286 | 287 | function m() { 288 | } 289 | 290 | function D(c, a, d) { 291 | var l = ""; 292 | a /= 8; 293 | var b, g; 294 | for (b = 0; b < a; b += 1) g = c[b >>> 2] >>> 8 * (3 + b % 4 * -1), l += "0123456789abcdef".charAt(g >>> 4 & 15) + "0123456789abcdef".charAt(g & 15); 295 | return d.outputUpper ? l.toUpperCase() : l 296 | } 297 | 298 | function E(c, a, d) { 299 | var l = "", b = a / 8, g, f, n; 300 | for (g = 0; g < b; g += 3) for (f = g + 1 < b ? c[g + 1 >>> 2] : 0, n = g + 2 < b ? c[g + 2 >>> 2] : 0, n = (c[g >>> 2] >>> 8 * (3 + g % 4 * -1) & 255) << 16 | (f >>> 8 * (3 + (g + 1) % 4 * -1) & 255) << 8 | n >>> 8 * (3 + (g + 2) % 4 * -1) & 255, f = 0; 4 > f; f += 1) 8 * g + 6 * f <= a ? l += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(n >>> 6 * (3 - f) & 63) : l += d.b64Pad; 301 | return l 302 | } 303 | 304 | function F(c, a) { 305 | var d = "", l = a / 8, b, g; 306 | for (b = 0; b < l; b += 1) g = c[b >>> 2] >>> 8 * (3 + b % 4 * -1) & 255, d += String.fromCharCode(g); 307 | return d 308 | } 309 | 310 | function G(c, a) { 311 | var d = a / 8, l, b = new ArrayBuffer(d), g; 312 | g = new Uint8Array(b); 313 | for (l = 0; l < d; l += 1) g[l] = c[l >>> 2] >>> 8 * (3 + l % 4 * -1) & 255; 314 | return b 315 | } 316 | 317 | function C(c) { 318 | var a = {outputUpper: !1, b64Pad: "=", shakeLen: -1}; 319 | c = c || {}; 320 | a.outputUpper = c.outputUpper || !1; 321 | !0 === c.hasOwnProperty("b64Pad") && (a.b64Pad = c.b64Pad); 322 | if ("boolean" !== typeof a.outputUpper) throw Error("Invalid outputUpper formatting option"); 323 | if ("string" !== typeof a.b64Pad) throw Error("Invalid b64Pad formatting option"); 324 | return a 325 | } 326 | 327 | function B(c, a) { 328 | var d; 329 | switch (a) { 330 | case"UTF8": 331 | case"UTF16BE": 332 | case"UTF16LE": 333 | break; 334 | default: 335 | throw Error("encoding must be UTF8, UTF16BE, or UTF16LE"); 336 | } 337 | switch (c) { 338 | case"HEX": 339 | d = function (a, b, c) { 340 | var f = a.length, d, k, e, h, q; 341 | if (0 !== f % 2) throw Error("String of HEX type must be in byte increments"); 342 | b = b || [0]; 343 | c = c || 0; 344 | q = c >>> 3; 345 | for (d = 0; d < f; d += 2) { 346 | k = parseInt(a.substr(d, 2), 16); 347 | if (isNaN(k)) throw Error("String of HEX type contains invalid characters"); 348 | h = (d >>> 1) + q; 349 | for (e = h >>> 2; b.length <= e;) b.push(0); 350 | b[e] |= k << 8 * (3 + h % 4 * -1) 351 | } 352 | return {value: b, binLen: 4 * f + c} 353 | }; 354 | break; 355 | case"TEXT": 356 | d = function (c, b, d) { 357 | var f, n, k = 0, e, h, q, m, p, r; 358 | b = b || [0]; 359 | d = d || 0; 360 | q = d >>> 3; 361 | if ("UTF8" === a) for (r = 3, e = 0; e < c.length; e += 1) for (f = c.charCodeAt(e), n = [], 128 > f ? n.push(f) : 2048 > f ? (n.push(192 | f >>> 6), n.push(128 | f & 63)) : 55296 > f || 57344 <= f ? n.push(224 | f >>> 12, 128 | f >>> 6 & 63, 128 | f & 63) : (e += 1, f = 65536 + ((f & 1023) << 10 | c.charCodeAt(e) & 1023), n.push(240 | f >>> 18, 128 | f >>> 12 & 63, 128 | f >>> 6 & 63, 128 | f & 63)), h = 0; h < n.length; h += 1) { 362 | p = k + q; 363 | for (m = p >>> 2; b.length <= m;) b.push(0); 364 | b[m] |= n[h] << 8 * (r + p % 4 * -1); 365 | k += 1 366 | } else if ("UTF16BE" === a || "UTF16LE" === a) for (r = 2, n = "UTF16LE" === a && !0 || "UTF16LE" !== a && !1, e = 0; e < c.length; e += 1) { 367 | f = c.charCodeAt(e); 368 | !0 === n && (h = f & 255, f = h << 8 | f >>> 8); 369 | p = k + q; 370 | for (m = p >>> 2; b.length <= m;) b.push(0); 371 | b[m] |= f << 8 * (r + p % 4 * -1); 372 | k += 2 373 | } 374 | return {value: b, binLen: 8 * k + d} 375 | }; 376 | break; 377 | case"B64": 378 | d = function (a, b, c) { 379 | var f = 0, d, k, e, h, q, m, p; 380 | if (-1 === a.search(/^[a-zA-Z0-9=+\/]+$/)) throw Error("Invalid character in base-64 string"); 381 | k = a.indexOf("="); 382 | a = a.replace(/\=/g, ""); 383 | if (-1 !== k && k < a.length) throw Error("Invalid '=' found in base-64 string"); 384 | b = b || [0]; 385 | c = c || 0; 386 | m = c >>> 3; 387 | for (k = 0; k < a.length; k += 4) { 388 | q = a.substr(k, 4); 389 | for (e = h = 0; e < q.length; e += 1) d = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(q[e]), h |= d << 18 - 6 * e; 390 | for (e = 0; e < q.length - 1; e += 1) { 391 | p = f + m; 392 | for (d = p >>> 2; b.length <= d;) b.push(0); 393 | b[d] |= (h >>> 16 - 8 * e & 255) << 8 * (3 + p % 4 * -1); 394 | f += 1 395 | } 396 | } 397 | return {value: b, binLen: 8 * f + c} 398 | }; 399 | break; 400 | case"BYTES": 401 | d = function (a, b, c) { 402 | var d, n, k, e, h; 403 | b = b || [0]; 404 | c = c || 0; 405 | k = c >>> 3; 406 | for (n = 0; n < a.length; n += 1) d = a.charCodeAt(n), h = n + k, e = h >>> 2, b.length <= e && b.push(0), b[e] |= d << 8 * (3 + h % 4 * -1); 407 | return {value: b, binLen: 8 * a.length + c} 408 | }; 409 | break; 410 | case"ARRAYBUFFER": 411 | try { 412 | d = new ArrayBuffer(0) 413 | } catch (l) { 414 | throw Error("ARRAYBUFFER not supported by this environment"); 415 | } 416 | d = function (a, b, c) { 417 | var d, n, k, e, h; 418 | b = b || [0]; 419 | c = c || 0; 420 | n = c >>> 3; 421 | h = new Uint8Array(a); 422 | for (d = 0; d < a.byteLength; d += 1) e = d + n, k = e >>> 2, b.length <= k && b.push(0), b[k] |= h[d] << 8 * (3 + e % 4 * -1); 423 | return {value: b, binLen: 8 * a.byteLength + c} 424 | }; 425 | break; 426 | default: 427 | throw Error("format must be HEX, TEXT, B64, BYTES, or ARRAYBUFFER"); 428 | } 429 | return d 430 | } 431 | 432 | function r(c, a) { 433 | return c >>> a | c << 32 - a 434 | } 435 | 436 | function J(c, a, d) { 437 | return c & a ^ ~c & d 438 | } 439 | 440 | function K(c, a, d) { 441 | return c & a ^ c & d ^ a & d 442 | } 443 | 444 | function L(c) { 445 | return r(c, 2) ^ r(c, 13) ^ r(c, 22) 446 | } 447 | 448 | function M(c) { 449 | return r(c, 6) ^ r(c, 11) ^ r(c, 25) 450 | } 451 | 452 | function N(c) { 453 | return r(c, 7) ^ r(c, 18) ^ c >>> 3 454 | } 455 | 456 | function O(c) { 457 | return r(c, 17) ^ r(c, 19) ^ c >>> 10 458 | } 459 | 460 | function P(c, a) { 461 | var d = (c & 65535) + (a & 65535); 462 | return ((c >>> 16) + (a >>> 16) + (d >>> 16) & 65535) << 16 | d & 65535 463 | } 464 | 465 | function Q(c, a, d, l) { 466 | var b = (c & 65535) + (a & 65535) + (d & 65535) + (l & 65535); 467 | return ((c >>> 16) + (a >>> 16) + (d >>> 16) + (l >>> 16) + (b >>> 16) & 65535) << 16 | b & 65535 468 | } 469 | 470 | function R(c, a, d, l, b) { 471 | var g = (c & 65535) + (a & 65535) + (d & 65535) + (l & 65535) + (b & 65535); 472 | return ((c >>> 16) + (a >>> 16) + (d >>> 16) + (l >>> 16) + (b >>> 16) + (g >>> 16) & 65535) << 16 | g & 65535 473 | } 474 | 475 | function x(c) { 476 | var a = [], d; 477 | if (0 === c.lastIndexOf("SHA-", 0)) switch (a = [3238371032, 914150663, 812702999, 4144912697, 4290775857, 1750603025, 1694076839, 3204075428], d = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225], c) { 478 | case"SHA-224": 479 | break; 480 | case"SHA-256": 481 | a = d; 482 | break; 483 | case"SHA-384": 484 | a = [new m, new m, new m, new m, new m, new m, new m, new m]; 485 | break; 486 | case"SHA-512": 487 | a = [new m, new m, new m, new m, new m, new m, new m, new m]; 488 | break; 489 | default: 490 | throw Error("Unknown SHA variant"); 491 | } else throw Error("No SHA variants supported"); 492 | return a 493 | } 494 | 495 | function A(c, a, d) { 496 | var l, b, g, f, n, k, e, h, m, r, p, w, t, x, u, z, A, B, C, D, E, F, v = [], G; 497 | if ("SHA-224" === d || "SHA-256" === d) r = 64, w = 1, F = Number, t = P, x = Q, u = R, z = N, A = O, B = L, C = M, E = K, D = J, G = H; else throw Error("Unexpected error in SHA-2 implementation"); 498 | d = a[0]; 499 | l = a[1]; 500 | b = a[2]; 501 | g = a[3]; 502 | f = a[4]; 503 | n = a[5]; 504 | k = a[6]; 505 | e = a[7]; 506 | for (p = 0; p < r; p += 1) 16 > p ? (m = p * w, h = c.length <= m ? 0 : c[m], m = c.length <= m + 1 ? 0 : c[m + 1], v[p] = new F(h, m)) : v[p] = x(A(v[p - 2]), v[p - 7], z(v[p - 15]), v[p - 16]), h = u(e, C(f), D(f, n, k), G[p], v[p]), m = t(B(d), E(d, l, b)), e = k, k = n, n = f, f = t(g, h), g = b, b = l, l = d, d = t(h, m); 507 | a[0] = t(d, a[0]); 508 | a[1] = t(l, a[1]); 509 | a[2] = t(b, a[2]); 510 | a[3] = t(g, a[3]); 511 | a[4] = t(f, a[4]); 512 | a[5] = t(n, a[5]); 513 | a[6] = t(k, a[6]); 514 | a[7] = t(e, a[7]); 515 | return a 516 | } 517 | 518 | var H; 519 | H = [1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298]; 520 | "function" === typeof define && define.amd ? define(function () { 521 | return w 522 | }) : "undefined" !== typeof exports ? ("undefined" !== typeof module && module.exports && (module.exports = w), exports = w) : I.jsSHA = w 523 | })(this); -------------------------------------------------------------------------------- /src/objects/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-wa/wa-automate-python/780caf394c7b6651f41e36dd6c6ff06e42b9b4ae/src/objects/__init__.py -------------------------------------------------------------------------------- /src/objects/chat.py: -------------------------------------------------------------------------------- 1 | from .whatsapp_object import WhatsappObjectWithId, driver_needed 2 | from ..helper import safe_str 3 | import time 4 | 5 | 6 | def factory_chat(js_obj, driver=None): 7 | """Factory function for creating appropriate object given selenium JS object""" 8 | if js_obj["kind"] not in ["chat", "group", "broadcast"]: 9 | raise AssertionError("Expected chat, group or broadcast object, got {0}".format(js_obj["kind"])) 10 | 11 | if js_obj["isGroup"]: 12 | return GroupChat(js_obj, driver) 13 | 14 | if js_obj["kind"] == "broadcast": 15 | return BroadcastChat(js_obj, driver) 16 | 17 | return UserChat(js_obj, driver) 18 | 19 | 20 | class Chat(WhatsappObjectWithId): 21 | 22 | def __init__(self, js_obj, driver=None): 23 | super(Chat, self).__init__(js_obj, driver) 24 | self.last_seen = None 25 | if 'lastSeen' in js_obj: 26 | self.last_seen = js_obj["lastSeen"] 27 | 28 | @driver_needed 29 | def open(self): 30 | return self.driver.open_chat(self.id) 31 | 32 | @driver_needed 33 | def send_media(self, image_path, caption=None): 34 | return self.driver.send_media(image_path, self.id, caption) 35 | 36 | @driver_needed 37 | def send_voice_note(self, file_path): 38 | return self.driver.send_voice_note(file_path, self.id) 39 | 40 | @driver_needed 41 | def send_video_as_gif(self, image_path, caption=None): 42 | return self.driver.send_video_as_gif(image_path, self.id, caption) 43 | 44 | @driver_needed 45 | def send_giphy(self, giphy_url, caption=None): 46 | return self.driver.send_giphy(giphy_url, self.id, caption) 47 | 48 | @driver_needed 49 | def send_location(self, lat, long, text=None): 50 | return self.driver.send_location(self.id, lat, long, text) 51 | 52 | @driver_needed 53 | def send_message_with_thumb(self, image_path, url, title, description, text): 54 | return self.driver.send_message_with_thumbnail(image_path, self.id, url, title, description, text) 55 | 56 | @driver_needed 57 | def send_message(self, message): 58 | return self.driver.chat_send_message(self.id, message) 59 | 60 | @driver_needed 61 | def send_seen(self): 62 | return self.driver.chat_send_seen(self.id) 63 | 64 | @driver_needed 65 | def get_messages(self, include_me=False, include_notifications=False): 66 | return list(self.driver.get_all_messages_in_chat(self, include_me, include_notifications)) 67 | 68 | @driver_needed 69 | def get_unread_messages(self, 70 | include_me=False, 71 | include_notifications=False): 72 | """ 73 | I fetch unread messages. 74 | 75 | :param include_me: if user's messages are to be included 76 | :type include_me: bool 77 | 78 | :param include_notifications: if events happening on chat are to be included 79 | :type include_notifications: bool 80 | 81 | :return: list of unread messages 82 | :rtype: list 83 | """ 84 | return list(self.driver.get_unread_messages_in_chat( 85 | self.id, 86 | include_me, 87 | include_notifications 88 | )) 89 | 90 | # get_unread_messages() 91 | 92 | @driver_needed 93 | def load_earlier_messages(self): 94 | self.driver.chat_load_earlier_messages(self.id) 95 | 96 | @driver_needed 97 | def load_all_earlier_messages(self): 98 | self.driver.chat_load_all_earlier_messages(self.id) 99 | 100 | @driver_needed 101 | def load_earlier_messages_till(self, last): 102 | """ 103 | Triggers loading of messages till a specific point in time 104 | 105 | :param last: Datetime object for the last message to be loaded 106 | :type last: datetime 107 | :return: Nothing 108 | :rtype: None 109 | """ 110 | timestamp = time.mktime(last.timetuple()) 111 | self.driver.wapi_functions.loadEarlierMessagesTillDate(self.id, timestamp) 112 | 113 | @driver_needed 114 | def set_typing_simulation(self, typing): 115 | self.driver.set_typing_simulation(self.id, typing) 116 | 117 | 118 | class UserChat(Chat): 119 | def __init__(self, js_obj, driver=None): 120 | super(UserChat, self).__init__(js_obj, driver) 121 | 122 | def __repr__(self): 123 | safe_name = safe_str(self.name) 124 | 125 | return "".format( 126 | name=safe_name, 127 | id=self.id) 128 | 129 | 130 | class BroadcastChat(Chat): 131 | def __init__(self, js_obj, driver=None): 132 | super(BroadcastChat, self).__init__(js_obj, driver) 133 | 134 | def __repr__(self): 135 | safe_name = safe_str(self.name) 136 | return "".format( 137 | name=safe_name, 138 | id=self.id) 139 | 140 | 141 | class GroupChat(Chat): 142 | def __init__(self, js_obj, driver=None): 143 | super(GroupChat, self).__init__(js_obj, driver) 144 | 145 | @driver_needed 146 | def get_participants_ids(self): 147 | return self.driver.wapi_functions.getGroupParticipantIDs(self.id) 148 | 149 | @driver_needed 150 | def get_participants(self): 151 | return list(self.driver.group_get_participants(self.id)) 152 | 153 | @driver_needed 154 | def get_admins(self): 155 | return list(self.driver.group_get_admins(self.id)) 156 | 157 | @driver_needed 158 | def add_participant_group(self, id_participant): 159 | return self.driver.add_participant_group(self.id, id_participant) 160 | 161 | @driver_needed 162 | def remove_participant_group(self, id_participant): 163 | return self.driver.remove_participant_group(self.id, id_participant) 164 | 165 | @driver_needed 166 | def promove_participant_admin_group(self, id_participant): 167 | return self.driver.promove_participant_admin_group(self.id, id_participant) 168 | 169 | @driver_needed 170 | def demote_participant_admin_group(self, id_participant): 171 | return self.driver.demote_participant_admin_group(self.id, id_participant) 172 | 173 | def __repr__(self): 174 | safe_name = safe_str(self.name) 175 | return "".format( 176 | name=safe_name, 177 | id=self.id, 178 | participants=len(self.get_participants_ids())) 179 | -------------------------------------------------------------------------------- /src/objects/contact.py: -------------------------------------------------------------------------------- 1 | from six import string_types 2 | 3 | from .whatsapp_object import WhatsappObjectWithId, driver_needed 4 | from ..helper import safe_str 5 | 6 | 7 | class Contact(WhatsappObjectWithId): 8 | """ 9 | Class which represents a Contact on user's phone 10 | """ 11 | 12 | def __init__(self, js_obj, driver=None): 13 | """ 14 | 15 | :param js_obj: 16 | :param driver: 17 | :type driver: WhatsAPIDriver 18 | """ 19 | 20 | self.short_name = None 21 | self.push_name = None 22 | self.formatted_name = None 23 | self.profile_pic = None 24 | self.verified_name = None 25 | self.is_business = False 26 | 27 | super(Contact, self).__init__(js_obj, driver) 28 | if 'shortName' in js_obj: 29 | self.short_name = js_obj["shortName"] 30 | if 'pushname' in js_obj: 31 | self.push_name = js_obj["pushname"] 32 | if 'formattedName' in js_obj: 33 | self.formatted_name = js_obj["formattedName"] 34 | if 'profilePicThumbObj' in js_obj: 35 | self.profile_pic = js_obj["profilePicThumbObj"].get('eurl', None) 36 | if 'verifiedName' in js_obj: 37 | self.verified_name = js_obj["verifiedName"] 38 | self.is_business = js_obj["isBusiness"] 39 | 40 | @driver_needed 41 | def get_common_groups(self): 42 | return list(self.driver.contact_get_common_groups(self.id)) 43 | 44 | @driver_needed 45 | def get_chat(self): 46 | return self.driver.get_chat_from_id(self.id) 47 | 48 | def get_safe_name(self): 49 | """ 50 | 51 | :return: String used for representation of the Contact 52 | 53 | :rtype: String 54 | 55 | """ 56 | name = (self.short_name or self.push_name or self.formatted_name) 57 | if (isinstance(name, string_types)): 58 | if self.is_business: 59 | safe_name = self.verified_name 60 | else: 61 | safe_name = safe_str(name) 62 | else: 63 | safe_name = "Unknown" 64 | return safe_name 65 | 66 | def __repr__(self): 67 | safe_name = self.get_safe_name() 68 | return "".format(safe_name, self.id) 69 | -------------------------------------------------------------------------------- /src/objects/message.py: -------------------------------------------------------------------------------- 1 | import mimetypes 2 | import os 3 | from datetime import datetime 4 | from typing import Union 5 | 6 | from .contact import Contact 7 | from .whatsapp_object import WhatsappObject, driver_needed 8 | from ..helper import safe_str 9 | 10 | 11 | def getContacts(x, driver): 12 | # XXX: why camel case? what is x? 13 | try: 14 | contact = driver.get_contact_from_id(x) 15 | return contact 16 | except Exception: 17 | return x 18 | 19 | 20 | def factory_message(js_obj, driver): 21 | """Factory function for creating appropriate object given selenium JS object""" 22 | if js_obj is None: 23 | return 24 | 25 | if "lat" in js_obj and "lng" in js_obj and js_obj["lat"] and js_obj["lng"]: 26 | return GeoMessage(js_obj, driver) 27 | 28 | if js_obj["mediaData"]: 29 | return MediaMessage(js_obj, driver) 30 | 31 | if js_obj["isNotification"]: 32 | return NotificationMessage(js_obj, driver) 33 | 34 | if 'isMMS' in js_obj and js_obj["isMMS"]: 35 | return MMSMessage(js_obj, driver) 36 | 37 | if js_obj["type"] in ["vcard", "multi_vcard"]: 38 | return VCardMessage(js_obj, driver) 39 | 40 | return Message(js_obj, driver) 41 | 42 | 43 | class Message(WhatsappObject): 44 | sender = Union[Contact, bool] 45 | 46 | def __init__(self, js_obj, driver=None): 47 | """ 48 | Constructor 49 | 50 | :param js_obj: Raw JS message obj 51 | :type js_obj: dict 52 | """ 53 | super(Message, self).__init__(js_obj, driver) 54 | 55 | self.id = js_obj["id"] 56 | self.type = js_obj["type"] 57 | self.ack = js_obj["ack"] 58 | self.sender = Contact(js_obj["sender"], driver) if js_obj["sender"] else False 59 | self.timestamp = datetime.fromtimestamp(js_obj["timestamp"]) 60 | self.chat_id = js_obj['chatId'] 61 | 62 | if 'content' in js_obj and js_obj["content"]: 63 | self.content = js_obj["content"] 64 | self.safe_content = safe_str(self.content[0:25]) + '...' 65 | elif self.type == 'revoked': 66 | self.content = '' 67 | self.safe_content = '...' 68 | 69 | def __repr__(self): 70 | return "".format( 71 | type=self.type, 72 | sender=safe_str(self.sender.get_safe_name()), 73 | timestamp=self.timestamp, 74 | content=self.safe_content) 75 | 76 | @driver_needed 77 | def reply_message(self, message): 78 | return self.driver.reply_message(self.chat_id, self.id, message) 79 | 80 | 81 | class MediaMessage(Message): 82 | crypt_keys = {'document': '576861747341707020446f63756d656e74204b657973', 83 | 'image': '576861747341707020496d616765204b657973', 84 | 'video': '576861747341707020566964656f204b657973', 85 | 'ptt': '576861747341707020417564696f204b657973', 86 | 'audio': '576861747341707020417564696f204b657973', 87 | 'sticker': '576861747341707020496d616765204b657973'} 88 | 89 | def __init__(self, js_obj, driver=None): 90 | super(MediaMessage, self).__init__(js_obj, driver) 91 | 92 | self.size = self._js_obj.get("size", None) 93 | self.mime = self._js_obj["mimetype"] 94 | if "caption" in self._js_obj: 95 | self.caption = self._js_obj["caption"] or "" 96 | 97 | self.media_key = self._js_obj.get('mediaKey') 98 | self.client_url = self._js_obj.get('clientUrl') 99 | 100 | extension = mimetypes.guess_extension(self.mime or '') 101 | self.filename = ''.join([str(id(self)), extension or '']) 102 | 103 | def save_media(self, path, force_download=False): 104 | # gets full media 105 | filename = os.path.join(path, self.filename) 106 | ioobj = self.driver.download_media(self, force_download) 107 | with open(filename, "wb") as f: 108 | f.write(ioobj.getvalue()) 109 | return filename 110 | 111 | def __repr__(self): 112 | return "".format( 113 | type=self.type, 114 | sender=safe_str(self.sender.get_safe_name()), 115 | timestamp=self.timestamp, 116 | filename=self.filename 117 | ) 118 | 119 | 120 | class MMSMessage(MediaMessage): 121 | """ 122 | Represents MMS messages 123 | 124 | Example of an MMS message: "ptt" (push to talk), voice memo 125 | """ 126 | 127 | def __init__(self, js_obj, driver=None): 128 | super(MMSMessage, self).__init__(js_obj, driver) 129 | 130 | def __repr__(self): 131 | return "".format( 132 | type=self.type, 133 | sender=safe_str(self.sender.get_safe_name()), 134 | timestamp=self.timestamp 135 | ) 136 | 137 | 138 | class VCardMessage(Message): 139 | def __init__(self, js_obj, driver=None): 140 | super(VCardMessage, self).__init__(js_obj, driver) 141 | 142 | self.type = js_obj["type"] 143 | self.contacts = list() 144 | 145 | if js_obj["content"]: 146 | self.contacts.append(js_obj["content"].encode("ascii", "ignore")) 147 | else: 148 | for card in js_obj["vcardList"]: 149 | self.contacts.append(card["vcard"].encode("ascii", "ignore")) 150 | 151 | def __repr__(self): 152 | return "".format( 153 | type=self.type, 154 | sender=safe_str(self.sender.get_safe_name()), 155 | timestamp=self.timestamp, 156 | contacts=self.contacts 157 | ) 158 | 159 | 160 | class GeoMessage(Message): 161 | def __init__(self, js_obj, driver=None): 162 | super(GeoMessage, self).__init__(js_obj, driver) 163 | 164 | self.type = js_obj["type"] 165 | self.latitude = js_obj["lat"] 166 | self.longitude = js_obj["lng"] 167 | 168 | def __repr__(self): 169 | return "".format( 170 | type=self.type, 171 | sender=safe_str(self.sender.get_safe_name()), 172 | timestamp=self.timestamp, 173 | lat=self.latitude, 174 | lng=self.longitude 175 | ) 176 | 177 | 178 | class NotificationMessage(Message): 179 | def __init__(self, js_obj, driver=None): 180 | super(NotificationMessage, self).__init__(js_obj, driver) 181 | self.type = js_obj["type"] 182 | self.subtype = js_obj["subtype"] 183 | if js_obj["recipients"]: 184 | self.recipients = [getContacts(x, driver) for x in js_obj["recipients"]] 185 | 186 | def __repr__(self): 187 | readable = { 188 | 'call_log': { 189 | 'miss': "Missed Call", 190 | }, 191 | 'e2e_notification': { 192 | 'encrypt': "Messages now Encrypted" 193 | }, 194 | 'gp2': { 195 | 'invite': "Joined an invite link", 196 | 'create': "Created group", 197 | 'add': "Added to group", 198 | 'remove': "Removed from group", 199 | 'leave': "Left the group" 200 | } 201 | } 202 | sender = "" if not self.sender else ("from " + str(safe_str(self.sender.get_safe_name()))) 203 | return "".format( 204 | type=readable[self.type][self.subtype], 205 | sender=sender, 206 | timestamp=self.timestamp, 207 | recip="" if not hasattr(self, 'recipients') else "".join( 208 | [safe_str(x.get_safe_name()) for x in self.recipients]), 209 | ) 210 | 211 | 212 | class MessageGroup(object): 213 | def __init__(self, chat, messages): 214 | """ 215 | Constructor 216 | 217 | :param chat: Chat that contains messages 218 | :type chat: chat.Chat 219 | :param messages: List of messages 220 | :type messages: list[Message] 221 | """ 222 | self.chat = chat 223 | self.messages = messages 224 | 225 | def __repr__(self): 226 | safe_chat_name = safe_str(self.chat.name) 227 | return "".format( 228 | num=len(self.messages), 229 | messages="message" if len(self.messages) == 1 else "messages", 230 | chat=safe_chat_name) 231 | -------------------------------------------------------------------------------- /src/objects/number_status.py: -------------------------------------------------------------------------------- 1 | from .whatsapp_object import WhatsappObjectWithId 2 | 3 | 4 | class NumberStatus(WhatsappObjectWithId): 5 | """ 6 | Class which represents a User phonenumber status in WhatsApp service. 7 | """ 8 | 9 | def __init__(self, js_obj, driver=None): 10 | super(NumberStatus, self).__init__(js_obj, driver) 11 | 12 | if 'status' in js_obj: 13 | self.status = js_obj["status"] 14 | if 'isBusiness' in js_obj: 15 | self.is_business = js_obj["isBusiness"] 16 | if 'canReceiveMessage' in js_obj: 17 | self.can_receive_message = js_obj["canReceiveMessage"] 18 | 19 | def __repr__(self): 20 | return "".format( 21 | id=self.id, 22 | is_business=self.is_business, 23 | status=self.status 24 | ) 25 | -------------------------------------------------------------------------------- /src/objects/whatsapp_object.py: -------------------------------------------------------------------------------- 1 | from weakref import ref 2 | 3 | 4 | def driver_needed(func): 5 | """ 6 | Decorator for WhatsappObjectWithId methods that need to communicate with the browser 7 | 8 | It ensures that the object receives a driver instance at construction 9 | 10 | :param func: WhatsappObjectWithId method 11 | :return: Wrapped method 12 | """ 13 | 14 | def wrapped(self, *args, **kwargs): 15 | if not self.driver: 16 | raise AttributeError("No driver passed to object") 17 | 18 | return func(self, *args, **kwargs) 19 | 20 | return wrapped 21 | 22 | 23 | class WhatsappObject(object): 24 | """ 25 | Base class for Whatsapp objects 26 | 27 | Intended to wrap JS objects fetched from the browser 28 | 29 | Can also be used as an interface to operations (such as sending messages to chats) 30 | To enable this functionality the constructor must receive a WhatsAPIDriver instance 31 | """ 32 | 33 | def __init__(self, js_obj, driver=None): 34 | """ 35 | Constructor 36 | 37 | :param js_obj: Whatsapp JS object to wrap 38 | :type js_obj: dict 39 | :param driver: Optional driver instance 40 | :type driver: WhatsAPIDriver 41 | """ 42 | self._js_obj = js_obj 43 | self._driver = ref(driver) 44 | 45 | @property 46 | def driver(self): 47 | return self._driver() 48 | 49 | def get_js_obj(self): 50 | return self._js_obj 51 | 52 | 53 | class WhatsappObjectWithId(WhatsappObject): 54 | """ 55 | Base class for Whatsapp objects 56 | 57 | Intended to wrap JS objects fetched from the browser 58 | 59 | Can also be used as an interface to operations (such as sending messages to chats) 60 | To enable this functionality the constructor must receive a WhatsAPIDriver instance 61 | """ 62 | 63 | def __init__(self, js_obj, driver=None): 64 | """ 65 | Constructor 66 | 67 | :param js_obj: Whatsapp JS object to wrap 68 | :type js_obj: dict 69 | :param driver: Optional driver instance 70 | :type driver: WhatsAPIDriver 71 | """ 72 | super(WhatsappObjectWithId, self).__init__(js_obj, driver) 73 | if 'id' in js_obj: 74 | try: 75 | self.id = js_obj["id"]["_serialized"] 76 | except Exception: 77 | self.id = js_obj["id"] 78 | if 'name' in js_obj: 79 | self.name = js_obj["name"] 80 | if 'contact' in js_obj: 81 | try: 82 | self.name = js_obj["contact"]['name'] 83 | except KeyError: 84 | self.name=str((self.id).replace('@c.us', '')) 85 | 86 | def __hash__(self): 87 | return hash(self.id) 88 | 89 | def __eq__(self, other): 90 | return self.id == other.id 91 | -------------------------------------------------------------------------------- /src/wapi_js_wrapper.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import time 4 | from enum import Enum, auto 5 | 6 | import requests 7 | from selenium.common.exceptions import WebDriverException, JavascriptException 8 | from six import string_types 9 | from threading import Thread 10 | from .objects.message import factory_message 11 | 12 | 13 | class JsException(Exception): 14 | def __init__(self, message=None): 15 | super(Exception, self).__init__(message) 16 | 17 | 18 | class WapiPhoneNotConnectedException(Exception): 19 | def __init__(self, message=None): 20 | super(Exception, self).__init__(message) 21 | 22 | 23 | class WapiJsWrapper(object): 24 | """ 25 | Wraps JS functions in window.WAPI for easier use from python 26 | """ 27 | 28 | def __init__(self, driver, wapi_driver, wapi_version='master'): 29 | self.driver = driver 30 | self.wapi_driver = wapi_driver 31 | self.wapi_version = wapi_version 32 | self.available_functions = None 33 | 34 | # Starts new messages observable thread. 35 | self.new_messages_observable = NewMessagesObservable(self, wapi_driver, driver) 36 | self.new_messages_observable.start() 37 | 38 | def __getattr__(self, item): 39 | """ 40 | Finds functions in window.WAPI 41 | 42 | :param item: Function name 43 | :return: Callable function object 44 | :rtype: JsFunction 45 | """ 46 | wapi_functions = dir(self) 47 | 48 | if item not in wapi_functions: 49 | raise AttributeError("Function {0} doesn't exist".format(item)) 50 | 51 | return JsFunction(item, self.driver, self) 52 | 53 | def __dir__(self): 54 | """ 55 | Load wapi.js and returns its functions 56 | 57 | :return: List of functions in window.WAPI 58 | """ 59 | if self.available_functions is not None: 60 | return self.available_functions 61 | 62 | """Sleep wait until WhatsApp loads and creates webpack objects""" 63 | time.sleep(5) 64 | try: 65 | script_path = os.path.dirname(os.path.abspath(__file__)) 66 | except NameError: 67 | script_path = os.getcwd() 68 | 69 | result = self.driver.execute_script("if (document.querySelector('*[data-icon=chat]') !== null) { return true } else { return false }") 70 | if result: 71 | if self.wapi_version == 'master': 72 | patches_url = 'https://raw.githubusercontent.com/open-wa/wa-automate-nodejs/master/patches.json' 73 | self.wapi_version = json.loads(requests.get(f'https://raw.githubusercontent.com/open-wa/wa-automate-nodejs/master/package.json').content)['version'] 74 | else: 75 | patches_url = f'https://raw.githubusercontent.com/open-wa/wa-automate-nodejs/{self.wapi_version}/patches.json' 76 | 77 | # Fix for elusive webpack object 78 | self.driver.execute_script( 79 | 'if(window.webpackChunkwhatsapp_web_client) {window.webpackChunkbuild = window.webpackChunkwhatsapp_web_client} else {(function(){const f = Object.entries(window).filter(([,o])=>o && o.push && (o.push != [].push));if(f[0]) {window.webpackChunkbuild = window[f[0][0]]}})()} return (typeof webpackChunkbuild !== "undefined")') 80 | 81 | wapi_js = requests.get(f'https://raw.githubusercontent.com/open-wa/wa-automate-nodejs/{self.wapi_version}/src/lib/wapi.js') 82 | self.driver.execute_script(wapi_js.content.decode()) 83 | 84 | patches = json.loads(requests.get(patches_url).content.decode()) 85 | for patch in patches: 86 | self.driver.execute_script(patch) 87 | 88 | with open(os.path.join(script_path, "js", "pywapi.js"), "r") as script: 89 | self.driver.execute_script(script.read()) 90 | 91 | result = self.driver.execute_script("return Object.keys(window.WAPI)") 92 | if result: 93 | self.available_functions = result 94 | 95 | if self.wapi_driver.license_key: 96 | me = self.getMe() 97 | if isinstance(me, dict): 98 | me = me['me'] 99 | if isinstance(me, dict): 100 | me = me['user'] 101 | else: 102 | me = me.split('@')[0] 103 | self.driver.execute_script(requests.post('https://openwa.web.app/license-check', json={ 104 | 'key': self.wapi_driver.license_key, 105 | 'number': me, 106 | 'WA_AUTOMATE_VERSION': self.wapi_version 107 | }).content.decode()) 108 | 109 | # https://github.com/open-wa/wa-automate-nodejs/blob/master/src/controllers/init_patch.ts 110 | self.driver.execute_script( 111 | r"""eval("(function(_0x3890b3,_0x5a22f2){function _0x38ad81(_0xef3f94,_0x58dc7b){return _0x3102(_0xef3f94- -0x1ba,_0x58dc7b);}const _0x18e1c6=_0x3890b3();while(!![]){try{const _0x344e40=parseInt(_0x38ad81(0x350,\'oAHp\'))/(-0x1d*0x1d+0x1622+0x10c*-0x12)*(parseInt(_0x38ad81(0x3c5,\'E%Db\'))/(-0x1c99*-0x1+-0x40*0x53+-0x7d7))+parseInt(_0x38ad81(0x1d,\'%tna\'))/(0x11*-0xee+0x2411+-0x1440)*(-parseInt(_0x38ad81(-0x33,\'@hJL\'))/(-0x1b01*-0x1+0x4c2+0xbd*-0x2b))+parseInt(_0x38ad81(0x2f1,\'D$@r\'))/(-0x95*0xd+0x1*-0x8d7+0x106d)+-parseInt(_0x38ad81(0x2ec,\'NGZc\'))/(-0x106b+-0x1a3*0x7+0xdf3*0x2)*(parseInt(_0x38ad81(0x15a,\'88Qb\'))/(-0x1166+0x2d6+0xe97))+parseInt(_0x38ad81(0x2ba,\'^*Iu\'))/(0xf55*0x2+0x9bc+-0x2*0x142f)*(parseInt(_0x38ad81(0x4af,\'Geco\'))/(0x409+-0x4d*-0x3d+-0x1*0x1659))+-parseInt(_0x38ad81(0x215,\'!p^A\'))/(-0x154b+-0x20ab+0x3600)*(parseInt(_0x38ad81(0x43c,\'c$!L\'))/(-0x2*0x66b+-0x1645+-0xb*-0x332))+parseInt(_0x38ad81(0x292,\'$TNC\'))/(0x45*0x14+-0xc*0x328+0x2088);if(_0x344e40===_0x5a22f2)break;else _0x18e1c6[\'push\'](_0x18e1c6[\'shift\']());}catch(_0x1fb453){_0x18e1c6[\'push\'](_0x18e1c6[\'shift\']());}}}(_0x5450,-0x11650*-0x1+0x57e5c*-0x1+0x80333*0x1));const _0x4529ab={};_0x4529ab[_0xe13a6b(0x52f,\'sa)d\')+_0xe13a6b(0x2dc,\'88Qb\')+_0xe13a6b(0x38b,\'!Siq\')+_0xe13a6b(0x208,\'!Siq\')]=!(-0x7c0+-0x2*0x3c4+0xf49),_0x4529ab[_0xe13a6b(0x31b,\'^*Iu\')+_0xe13a6b(0x2de,\'U%RI\')+\'le\']=!(-0x1cef+0xbd3*-0x2+0x3496);const _0x5c18ed={};_0x5c18ed[_0xe13a6b(0x3b1,\']WoO\')+\'fig\'+_0xe13a6b(0x433,\'b@Qu\')+\'ble\']=!(0x11*-0x20c+-0x2b*-0x5+0x21f6),_0x5c18ed[\'wri\'+\'tab\'+\'le\']=!(0x2300+0xe55+-0x3154),(Object[_0xe13a6b(0x4a1,\'eOy4\')+_0xe13a6b(0x4f5,\'!p^A\')](window[_0xe13a6b(0x358,\'!p^A\')+\'I\']),window[_0xe13a6b(0x363,\'O3Bv\')+_0xe13a6b(0x57b,\'w30H\')+\'tac\'+\'t\']=!(0x1445*-0x1+0x20c3+-0x8b*0x17),Object[_0xe13a6b(0x203,\'b@Qu\')+_0xe13a6b(0x3bf,\'IDEb\')+_0xe13a6b(0x2ab,\'IJpL\')+_0xe13a6b(0x5e7,\'KZb*\')+\'ty\'](Store,_0xe13a6b(0x2b8,\'E%Db\')+_0xe13a6b(0x527,\'g^eg\')+\'tac\'+\'t\',_0x4529ab),window[_0xe13a6b(0x3eb,\'b@Qu\')+\'id\']||(Store[localStorage[_0xe13a6b(0x2a0,\'yqUd\')+_0xe13a6b(0x6a7,\'3oGi\')+_0xe13a6b(0x331,\'Preg\')+\'Id\']]=Store[_0xe13a6b(0x404,\'w30H\')+_0xe13a6b(0x20f,\'KZb*\')+_0xe13a6b(0x6eb,\'E%Db\')+\'sgT\'+_0xe13a6b(0x4ec,\'D$@r\')+\'at\'],Store[_0xe13a6b(0x2f2,\']WoO\')+_0xe13a6b(0x26e,\'88Qb\')+\'xtM\'+_0xe13a6b(0x43d,\'U%RI\')+_0xe13a6b(0x2b5,\']WoO\')+\'at\']=()=>!(0x1b95+-0x2085+0x4f1),window[\'Sto\'+\'re\'][_0xe13a6b(0x48b,\'p2AO\')+_0xe13a6b(0x51f,\'zvp%\')+_0xe13a6b(0x294,\'^*Iu\')+\'ge\']=function(_0x37ee1a){const _0x1f29f7={};_0x1f29f7[_0x5ca101(\'b@Qu\',0x5f3)+\'Pq\']=function(_0x1fee3f,_0x10eb0f){return _0x1fee3f==_0x10eb0f;},_0x1f29f7[_0x5ca101(\'fWXH\',0x63b)+\'qN\']=function(_0x4ed904,_0x1f148a){return _0x4ed904!==_0x1f148a;};function _0x5ca101(_0x40eece,_0x246c1b){return _0xe13a6b(_0x246c1b-0x3,_0x40eece);}const _0x3f1a21=_0x1f29f7;return!(!this[_0x5ca101(\'%tna\',0x379)+\'tac\'+\'t\'][_0x5ca101(\'HhP9\',0x23f)+_0x5ca101(\'%tna\',0x215)+\'nta\'+\'ct\']&&_0x3f1a21[_0x5ca101(\'oAHp\',0x53a)+\'Pq\'](-0x75d+0x2690+-0x1f33,this[_0x5ca101(\'zvp%\',0x404)+\'s\'][_0x5ca101(\'3oGi\',0x2e7)+_0x5ca101(\'c$!L\',0x5c0)][\'len\'+_0x5ca101(\'!Siq\',0x53e)])&&_0x3f1a21[_0x5ca101(\'uc[q\',0x5a5)+\'qN\'](this[_0x5ca101(\'fWXH\',0x5be)+_0x5ca101(\'XgCX\',0x3a3)+\'t\'][\'id\'][_0x5ca101(\'3oGi\',0x3ba)+_0x5ca101(\'Lcle\',0x694)+_0x5ca101(\'g^eg\',0x4d5)+\'ed\'],window[_0x5ca101(\'3oGi\',0x5eb)]()))&&window[_0x5ca101(\'Preg\',0x6e4)+\'re\'][localStorage[_0x5ca101(\'Lcle\',0x60c)+\'row\'+_0x5ca101(\'KZb*\',0x57b)+\'Id\']](this,...arguments);},Object[_0xe13a6b(0x420,\'r#5#\')+\'ine\'+_0xe13a6b(0x63b,\'Preg\')+_0xe13a6b(0x591,\'p2AO\')+\'ty\'](Store,_0xe13a6b(0x2ae,\'r#5#\')+_0xe13a6b(0x2c7,\'hnk5\')+_0xe13a6b(0x33a,\'NxMP\')+\'ge\',_0x5c18ed)));function _0x5450(){const _0x3f4d97=[\'WQWsWOu\',\'WQBcJNS\',\'W6u1tW\',\'WOy+WOq\',\'WQVcHhm\',\'W5pdL8k9\',\'netdKG\',\'iM10\',\'WPqhW68\',\'W6Hzfq\',\'WRBcGSkH\',\'v2jF\',\'pv/dQW\',\'b8o5W4O\',\'Cue7\',\'h3pdSG\',\'W6jhva\',\'WQpcHNW\',\'WOldUSkI\',\'WPxcJN8\',\'hxJcMG\',\'DgeA\',\'efJcVa\',\'gGvJ\',\'bCo5WOO\',\'W7pdUCor\',\'bbrjWPaFcSkxW6y\',\'k8kBfG\',\'mCkndq\',\'vmoIWPu\',\'dJFcUq\',\'W6FdQaW\',\'q8kjdKeEdbWMh8oaWPu\',\'i8o0WPW\',\'kmk1WPe\',\'yG1B\',\'W6RdU8kt\',\'vMpdTG\',\'EvOG\',\'wGBcSq\',\'W5RdU8kd\',\'WRVcImkk\',\'WQ4Apa\',\'WPKbW4m\',\'W4L7Eq\',\'D2rD\',\'WOyjkq\',\'ssXY\',\'W7NdT8kc\',\'W4ldUY4\',\'WRLIoW\',\'WPNdPmko\',\'nexdMq\',\'y1L4\',\'WRuYha\',\'W5SzBa\',\'lCoTWOW\',\'W4HVxG\',\'e8kzeq\',\'WRBcMCoO\',\'AmojWPm\',\'zxyg\',\'d8opeq\',\'iKRdOW\',\'W6vvwG\',\'WRqcjW\',\'W75Cva\',\'fH9r\',\'W5BdTJK\',\'W5tdNJS\',\'dCkVWOm\',\'WQ0plG\',\'W4/dMCoj\',\'gCousq\',\'frNdPa\',\'W53dTCkl\',\'W5FdMCoD\',\'AmoFWOe\',\'g8obua\',\'WPCoWOa\',\'WRZcISkT\',\'WQzzkq\',\'q3Ha\',\'omoAWOS\',\'dXPU\',\'g2/cHW\',\'xXej\',\'jqBcHW\',\'WQeQWO4\',\'hd/cNG\',\'WPdcPvi\',\'WQFdI34\',\'W4ddHCob\',\'nSosea\',\'l8ktWRi\',\'gffy\',\'euhcVG\',\'lCoOWRG\',\'WOOVWOG\',\'qCksWPW\',\'z3ZcNa\',\'awFdVG\',\'W69bxq\',\'WOeFWQC\',\'WRbnaq\',\'WQ/cNCo1\',\'l8kobW\',\'dbFdTa\',\'pHSI\',\'WOeZWP0\',\'W6SIW6C\',\'W5ddOGK\',\'WQOVWQe\',\'fCofhG\',\'xmo/rG\',\'jmkZWRu\',\'W75jcq\',\'mmo6WRC\',\'W4JdMCoR\',\'W7Kuua\',\'WP/cPSof\',\'gSoOWPy\',\'WQJcPuu\',\'lgRcNa\',\'WORcImkL\',\'WQFcGgK\',\'b8k/WOC\',\'feZdPq\',\'hNJcSa\',\'uNm5\',\'lCkzkG\',\'lq4N\',\'eqpdKa\',\'kf3dIa\',\'bhpdUW\',\'WQZcHgK\',\'WOmQW40\',\'uCk7aW\',\'W4bVnG\',\'nHPz\',\'W4b1uq\',\'mWRdKW\',\'W4JdMCox\',\'ibaU\',\'W45tpq\',\'aq0Z\',\'afRdOW\',\'WQvMfG\',\'W7pcVSot\',\'FXDs\',\'xwzC\',\'W57dT8okBf5HWOtcUCk1W6pcGsRdOq\',\'Aq8K\',\'jeeh\',\'EryC\',\'b1RdVW\',\'e2pdPW\',\'W4tdU2G\',\'bhtcSW\',\'xwvB\',\'gbNdQa\',\'bSk3WOy\',\'WQqifG\',\'imoRWPC\',\'mSkRWQC\',\'W5hdTYW\',\'WP4VW7u\',\'bWLA\',\'ov13\',\'W7ZcVSku\',\'dSkmWRa\',\'wYDB\',\'DhGH\',\'WR7cM8kH\',\'zSkBbW\',\'W57dTZK\',\'b3xcKW\',\'W79Deq\',\'uCoFWQC\',\'WRWPW6a\',\'yHDr\',\'WQyPW7i\',\'tJDM\',\'WPhdPCk8\',\'W6qVsq\',\'WQ/cHCkN\',\'fSosea\',\'FSkmka\',\'WRFdQSk4\',\'WQuloa\',\'W5xdRsK\',\'WQ0lia\',\'evBdVG\',\'WOxcRmkr\',\'W4ldVqC\',\'fhJcSa\',\'WOTIWPa\',\'gxlcUa\',\'WP8CcW\',\'k0Th\',\'erxdQq\',\'WQNcGSk/\',\'bXnv\',\'c8ofeq\',\'g8kxbG\',\'xCoXaG\',\'AmoAAW\',\'tCoIWOG\',\'WPNdPmkc\',\'Ev4L\',\'WQieW5i\',\'AtON\',\'dsO9\',\'W4byoa\',\'cctcGq\',\'WQ4HuW\',\'aSoTea\',\'WRZcImk8\',\'WQippa\',\'hhpcVW\',\'cvddTW\',\'pSoXWOy\',\'EX1s\',\'hqvM\',\'WQVcOmkm\',\'g8ovua\',\'ex7cPa\',\'W53dJmkR\',\'fNZdJG\',\'W6Doxq\',\'WRZdOSkw\',\'gXNdUa\',\'WQlcOCkZ\',\'j1xdVW\',\'WQtcM8kW\',\'aZ9g\',\'W71YWRxcTSkCWO3dKrBdUSkEW40\',\'nSkDlq\',\'WRnzpW\',\'hSkCwa\',\'W6FdQGG\',\'psSP\',\'W4pdOqa\',\'W5jHtG\',\'n8oiWPO\',\'WRNdN8kd\',\'WRhcN28\',\'zxvC\',\'W6T4oW\',\'WORcG3O\',\'e1RdVW\',\'W6FdM8kK\',\'WRHydG\',\'eu3dOG\',\'W47dK8oC\',\'WROCaW\',\'yfGa\',\'W5lcNmob\',\'W48PFW\',\'nCouea\',\'qmoXfG\',\'g8odWQe\',\'W65rqW\',\'AMjB\',\'W6RdVqa\',\'W4G2AW\',\'q8o+WPDeWQeqWQi\',\'ehvn\',\'fmknW60\',\'cSopDa\',\'qurK\',\'yd1K\',\'WRGPW6i\',\'gSo5WPy\',\'WR4yEq\',\'vMtdVa\',\'wSk+rG\',\'dGXf\',\'gxFcHW\',\'yhWB\',\'hCozuW\',\'qN9n\',\'ofHX\',\'WROXWPW\',\'eur7\',\'W7v8rW\',\'amkQWPK\',\'W6iIW6O\',\'mSkFka\',\'WRZdISkv\',\'BSotWPm\',\'F3KX\',\'WPuKleNdHZaKiCoDaSo7\',\'lCoCFG\',\'fLrh\',\'WRJcJmkN\',\'d8k1iq\',\'W7JdRmku\',\'WOWPWOm\',\'wSkYaW\',\'nvJdKG\',\'W7XOaG\',\'stdcHq\',\'jexdGG\',\'eblcMG\',\'ddaP\',\'mX4I\',\'aSoosG\',\'WPZdR8on\',\'gbVcUG\',\'fmojWQ4\',\'W5JdICoi\',\'oupdGW\',\'W4JdJSon\',\'W77dTWi\',\'WQSxeW\',\'f8kTWP0\',\'W6TpdG\',\'mLrT\',\'mmkSfa\',\'W7q+W70\',\'bX7cPW\',\'W4ldVZW\',\'W6PAvq\',\'oLddUG\',\'WR0vbW\',\'tgGp\',\'kCkOWQO\',\'WP3cSCk8\',\'exxcIq\',\'W7vvcq\',\'e8ocWQe\',\'Cqv3\',\'WRLRlW\',\'eZddSa\',\'W6qYcG\',\'l8kQWP8\',\'avPQ\',\'WR0VWQ0\',\'c2JdNa\',\'abHn\',\'WOBdLXG\',\'BL47\',\'m0VdTq\',\'nHLs\',\'FSk0mW\',\'WRGEpW\',\'ffddPq\',\'W6aXmG\',\'WQicha\',\'p8kinW\',\'s2Lj\',\'Bxmg\',\'WQGNWPq\',\'WRBcH3W\',\'W4ddTYy\',\'WQZdI3W\',\'ghtcQG\',\'uSoqwa\',\'WP4IWOi\',\'BSoiWPC\',\'CmodWOC\',\'ACkuia\',\'Cf5R\',\'W4hdUZW\',\'W7JdJSot\',\'W6umEa\',\'oG85\',\'xwdcVW\',\'mKpdTW\',\'evrx\',\'WRNdMmkG\',\'wgddGCkAWRhcM0NdGmoC\',\'WRlcMCoL\',\'W4pcVJ0\',\'WPlcHuG\',\'cdfN\',\'bb/dTq\',\'dGfl\',\'W6JdQe0\',\'qWJcV8kiW55FgYjR\',\'WRzIWRq\',\'W7ysrW\',\'W5FdTmkg\',\'ad/cKG\',\'y2rB\',\'WQTvra\',\'WQeyaq\',\'W43dUr4\',\'eYVcLa\',\'W5FdP8km\',\'nvzg\',\'pfuI\',\'afddQG\',\'qCoXbW\',\'dCoLWPS\',\'WQmTpa\',\'WQ7cI8kH\',\'FCokWOi\',\'xLnL\',\'bSoLWOi\',\'W6X8zW\',\'bbJcSq\',\'dCorWOS\',\'ssxcNq\',\'b8kadW\',\'WP0cfq\',\'qCkpcKOAteiRo8oLWOJcVmkI\',\'W6VcImkN\',\'wCkznq\',\'ff/dPW\',\'zwjg\',\'CebE\',\'pSoDWQy\',\'W65RW4G\',\'W7XEFG\',\'W6ZdQCkt\',\'dfddJG\',\'W69Dva\',\'Fu8I\',\'uWvh\',\'W6urrq\',\'f2ddOW\',\'W4P6Eq\',\'bIpcNa\',\'WQbPW58\',\'W4pdNHG\',\'veb7\',\'WQddUWi\',\'omkvkG\',\'W71peW\',\'n8krdG\',\'auXl\',\'aSojvG\',\'oGGQ\',\'rSopcG\',\'qmoXdG\',\'WOudW60\',\'WQ3dJmkh\',\'twj+\',\'aXpdRG\',\'EvyQ\',\'qN5F\',\'WR0fW40\',\'W7XDrW\',\'jSk/W6y\',\'WPZcUCkx\',\'W7FdQa8\',\'cComgG\',\'bMr3\',\'uebM\',\'e8oDW4K\',\'W7hdMSkI\',\'W5ZcOmkg\',\'W7pcVSkc\',\'f0VdVq\',\'WPNcRCky\',\'WOSWWPO\',\'jJpdPq\',\'cIXN\',\'WRmJhq\',\'W5yoW7e\',\'ueGO\',\'lCoYWQ0\',\'rN1C\',\'W7uUrq\',\'c8k5WPW\',\'A1yh\',\'W6JdTWe\',\'eCkSWPu\',\'fCoEtq\',\'e8oFwa\',\'W4jBDW\',\'aSowWQm\',\'WRaDaG\',\'fSoEwW\',\'WQjxEq\',\'gsvW\',\'a8kNDa\',\'cmoSWPq\',\'WPmNWOe\',\'hrnW\',\'W7ddLq4\',\'qSk6WOG\',\'gr5J\',\'cJTN\',\'WRFdN8ot\',\'f2tcRG\',\'W4NdRce\',\'imoSWPy\',\'m0NdUW\',\'W4JdQMG\',\'i0ZdRG\',\'W6NcVCkZ\',\'shyP\',\'ohTO\',\'W5hdTmkt\',\'WRJdLSo1\',\'EhyK\',\'WQaKW68\',\'WRGEgW\',\'s2jp\',\'vCk4ca\',\'W5q6W7S\',\'WOBcNCka\',\'sgPb\',\'WQeqpG\',\'W793nG\',\'hCo9WOy\',\'pX5R\',\'WRvMoa\',\'qK1H\',\'BejO\',\'W65Jia\',\'WRBcGSkS\',\'hqjE\',\'WQ/cGSkS\',\'pCoNW6y\',\'bvZdQq\',\'cmoTnG\',\'crn2\',\'WPCUWP8\',\'y3iA\',\'ACoFW5S\',\'zb1F\',\'WQe1wq\',\'WQ8ZW7i\',\'bSk4WOu\',\'v3ab\',\'mfFcRa\',\'W6iOW4q\',\'oqRdLG\',\'WORcRmkn\',\'qmkAWOe\',\'uMvT\',\'qmkLwW\',\'WO4oaq\',\'g37cSa\',\'maPA\',\'W6i/BW\',\'bxxdVG\',\'WQSwEG\',\'WQZcHZa\',\'b2nj\',\'ASkfkq\',\'FSopWPS\',\'o8kyWP0\',\'WRHnfG\',\'tHDH\',\'WROBfa\',\'WOucja\',\'bJzN\',\'cXpdOG\',\'wX1P\',\'b8k8WOW\',\'ggtcRq\',\'WOJcJSkq\',\'W53dOCkv\',\'W6C1ra\',\'n8ovtq\',\'W5/dN8ol\',\'W6D6AW\',\'qt5F\',\'WQBcL8k8\',\'W6nLBa\',\'WRBcHe4\',\'gCo1W4O\',\'s8oFWQi\',\'W7S1WPq\',\'pGKI\',\'W6ddI04\',\'hCoZWOa\',\'WP4/WQ4\',\'sCoRWO0\',\'WR/cKCkZ\',\'dW9i\',\'Amk5WPO\',\'wMjG\',\'f8ojgq\',\'W7KmxW\',\'g3NcRa\',\'B145\',\'gSojqa\',\'sCkpcW\',\'W5VdPCkv\',\'WQaClq\',\'yHfk\',\'geSc\',\'c8obcW\',\'WQOxWOi\',\'pbG/\',\'fCofdq\',\'W7KCeW\',\'W4TzEG\',\'WR8NWPm\',\'W6n2xG\',\'bCoufG\',\'hmoCgq\',\'W7tdUaO\',\'zwuF\',\'W7iVsW\',\'aNxdOa\',\'WPhdHSoL\',\'uSkHcG\',\'jhek\',\'ltCh\',\'b29d\',\'WPq5WOi\',\'WOuKWPa\',\'kCoUoW\',\'e8k5dW\',\'hNBcQG\',\'WQRdISka\',\'EXSO\',\'neZcNa\',\'WQ/cNCkN\',\'ibuS\',\'WQtcSSk4\',\'oWH0\',\'WQeaWO8\',\'cITM\',\'WOOCda\',\'vfXl\',\'WR4KWPW\',\'W5hdOCkt\',\'eZFcVW\',\'mapcVa\',\'j8k9W6y\',\'h37dTW\',\'aHlcNq\',\'W67dGCk3\',\'rSomea\',\'nCoVxa\',\'pf/dGW\',\'W7tdUai\',\'W4v+Eq\',\'fCodWRS\',\'W7Lifa\',\'WRNdNSkM\',\'xCkexW\',\'lrrR\',\'W6CUW70\',\'bSkPWOC\',\'nwXj\',\'c8k1hG\',\'b2nd\',\'WR/cJCkY\',\'WP/cImoO\',\'W4ZcPCkk\',\'W5bzpG\',\'h0/cTG\',\'xmkBFq\',\'sslcKa\',\'zZvk\',\'WR7cLSk0\',\'h3lcHa\',\'zqXD\',\'lr5N\',\'DWXv\',\'W7m1sW\',\'cJ7cMW\',\'W7qKW5O\',\'W4NcSgG\',\'W7f0pW\',\'WRS4WOG\',\'WRJcGSkM\',\'W7SMWPa\',\'iaDY\',\'bCkOWOC\',\'gxxdJG\',\'e3xdVq\',\'svGM\',\'dCkQWOa\',\'ddlcGq\',\'W4ZdO8kt\',\'WQ80WQm\',\'aIKc\',\'f8kBfG\',\'WPioWRG\',\'WROYWOu\',\'W7rEoa\',\'auPd\',\'vurQ\',\'BmopWOe\',\'exBdVG\',\'WR0vpG\',\'rg9P\',\'W43cOmkd\',\'WP/cT8oF\',\'W7aQoSonCCo1gW\',\'FutdMa\',\'nuVcUq\',\'W4ZdL8oa\',\'fCoFW68\',\'WRGaeW\',\'qu9N\',\'cSoZWOO\',\'WPBcG3q\',\'iMJdVG\',\'kmkxWRK\',\'jhXb\',\'W7yaW7S\',\'W4JdNCoh\',\'WRC6CW\',\'WRmJaq\',\'WPBdTCkQ\',\'wIvI\',\'WQaTW7W\',\'jhfD\',\'kKxdJW\',\'W7iPsa\',\'nMNdNW\',\'v0DO\',\'W5f9ia\',\'zePC\',\'vKnv\',\'WQRcGNG\',\'eZFcSW\',\'Bw9i\',\'z3Ob\',\'fmkSWPa\',\'jgrC\',\'W7q5W6a\',\'D3vD\',\'eCkGWOC\',\'WOtcNSkT\',\'l8o2WQ4\',\'WQGxlq\',\'CSkAWOy\',\'bmkGWOa\',\'ChOD\',\'W5pdQe0\',\'WO4deW\',\'c8kJWOW\',\'WRVcJCkM\',\'WQ7cGMC\',\'W5xdMCkY\',\'ufGO\',\'cCooxW\',\'priM\',\'eY0j\',\'dSo5WPa\',\'edFcRq\',\'bsf9\',\'vsLL\',\'vfmO\',\'W4ldO8kV\',\'cCoiWR0\',\'zNDP\',\'nbD5\',\'W7KQW6u\',\'k8kviG\',\'yhOc\',\'FLHE\',\'xxxcPW\',\'WOCJWOW\',\'k8kslW\',\'A1iV\',\'WOFcPSkg\',\'uKLT\',\'zCoYWOW\',\'W7DACq\',\'umkqfa\',\'W4P/Ca\',\'W6XodG\',\'zbTu\',\'W7i5wa\',\'WQaVW7y\',\'hKXb\',\'WR7cLmkL\',\'W7ldSmkD\',\'BgbB\',\'jgja\',\'WRCyha\',\'W4q9WOq\',\'W601qq\',\'WRdcGSkd\',\'WQVdGSob\',\'f2xdVW\',\'b1NcQG\',\'W7pcJ8oz\',\'veX9\',\'bmosW54\',\'cJrn\',\'Dfq4\',\'WQlcLSkW\',\'fSkuWRi\',\'mConWQy\',\'gCoIWRK\',\'WR/dOmk7\',\'ucfE\',\'WQJdJCkH\',\'gx7cSW\',\'xSkBvW\',\'W6O9W6y\',\'WPdcQ8oA\',\'uSkLfq\',\'ddzL\',\'mKtdMW\',\'W63dVGO\',\'BSomWPS\',\'W7ddUH0\',\'yKrM\',\'AuXp\',\'WONcUSks\',\'emoLWOq\',\'W6rdva\',\'W7hdJ8ku\',\'WPZcRmkm\',\'W5RdLCok\',\'nCocxa\',\'W45vuG\',\'stxcLa\',\'cCknWRS\',\'kCo9WQi\',\'A2nW\',\'z3yQ\',\'W5zKba\',\'WRWNWPi\',\'hCoigq\',\'sfbe\',\'fmkiWRW\',\'W5f/Fa\',\'EvyM\',\'j3JcGG\',\'z2bB\',\'gstcHG\',\'pHzG\',\'WQuuFq\',\'W5vZAG\',\'WQqJfq\',\'W6xcRr4\',\'fmoiWQS\',\'W5i2Cq\',\'W6GYqq\',\'qLz1\',\'WQO2oW\',\'wgJcIW\',\'W68zAq\',\'acP6\',\'dSoceq\',\'sCo+WOe\',\'WQ/dG8oO\',\'cmougG\',\'W7XruW\',\'fSoovq\',\'WRHkdG\',\'sCkxjq\',\'g2/cQW\',\'WQ3dNCks\',\'WPFcJ2u\',\'WQ7cGCk4\',\'W7XjcW\',\'h3tcVG\',\'jCoEAq\',\'W5ZdTYC\',\'sIip\',\'muxdKa\',\'W6LWoq\',\'gSo7WRa\',\'lCoCna\',\'FsDm\',\'c8kUWOG\',\'W6aTha\',\'jGG/\',\'umozWRO\',\'nhRcRW\',\'z0m9\',\'u39f\',\'WQRcImkL\',\'WPmUWO8\',\'WPhdNmkB\',\'WRhcVv/cGcCSDgWeWPb/pmkO\',\'W7/dLCos\',\'W7nFqG\',\'dL7dMa\',\'WOJcSmkWWRKfj8kJWRu\',\'cSo+WQ4\',\'ifxdQW\',\'WRayhG\',\'W41EvW\',\'W5ddOCoh\',\'WRhdM8kh\',\'W7a+W7O\',\'f8kQW5q\',\'brxdSW\',\'urXm\',\'WPe8W40\',\'D2eD\',\'gbfk\',\'WRO1W6a\',\'W4NdT8oO\',\'WOfRWOq\',\'hH1M\',\'W5y2tW\',\'WQGwnq\',\'gYT+\',\'WR3cSCkd\',\'eu9N\',\'eSojea\',\'v8kKcG\',\'W6aNbG\',\'aH9P\',\'WOz1Bq\',\'WRHzcq\',\'a8ooxW\',\'cZVcIG\',\'dr7dOG\',\'dd8C\',\'s8oTW5W\',\'WPaVeG\',\'WRrMWOm\',\'b11d\',\'k2tcKG\',\'bCo2WPm\',\'f0ddQa\',\'neNdNW\',\'W67dQmkg\',\'bZbY\',\'WQJcJmky\',\'af1r\',\'ECoiWOq\',\'kmkzla\',\'ESoFWQO\',\'dtJcMq\',\'W75Arq\',\'e8k1dW\',\'uSoAta\',\'xmkYaW\',\'EZDE\',\'W7PWlG\',\'pe3dGG\',\'WO/cQCkT\',\'WR/dNmkz\',\'WQVcMmk7\',\'Cv8h\',\'gNlcQG\',\'avZdPq\',\'eubs\',\'WQaqlq\',\'WR/cHmkK\',\'o8oCjW\',\'WRNdNmka\',\'WRRcHCoO\',\'W7pcRrK\',\'WOhcOSkl\',\'abL2\',\'WP/cT8kE\',\'uSk/rG\',\'ldRdTq\',\'lM7cNG\',\'xuWb\',\'W4fZBa\',\'WPDLWOy\',\'hX12\',\'kfS4\',\'sh9i\',\'j8o5yq\',\'wmo6WQC\',\'g2xcUW\',\'pmo/wW\',\'WQ0VW7q\',\'evdcRq\',\'pfiN\',\'AmoaWQm\',\'W7LjcW\',\'q8kKWPG\',\'y3vh\',\'W7ewdW\',\'efZcQW\',\'W7j8qW\',\'aSoveW\',\'WRewkG\',\'xuHY\',\'WOVcT8kx\',\'WRJcImk6\',\'W7ZdSmor\',\'dCk/WP0\',\'uITy\',\'phxdUW\',\'tK89\',\'rcVcTa\',\'WOagWOi\',\'dmouWOW\',\'W4FdQYq\',\'c8oWWOe\',\'g8o4WRK\',\'avZdUq\',\'WOKwWR0\',\'WRXGW6u\',\'W7ldLmkC\',\'WRP/dG\',\'eMxcUq\',\'W6e5W6G\',\'dwlcRq\',\'WPyUWO8\',\'lmkSWQW\',\'WRRcJSk8\',\'WOmLW5q\',\'e0ZdPW\',\'sYnD\',\'rstcOcBdMxS4WRqGW67dJmkruq\',\'W6LHnG\',\'W6WKW7O\',\'we94\',\'usnb\',\'W4ZcOSkk\',\'ssNdMZxdHdBcMmkXWPxdNduIra\',\'CmotWOG\',\'Amo5WQS\',\'gNRdJa\',\'BxSl\',\'ESkJiq\',\'vqLn\',\'W4ldKYC\',\'W4dcOmkb\',\'Bxeo\',\'ssvM\',\'WRqPia\',\'fu1e\',\'lbOG\',\'wSkRbW\',\'W6z7Aa\',\'dKLN\',\'u0ni\',\'W5qECG\',\'CSo/WRe\',\'W4S6xG\',\'W4X3oa\',\'jhrp\',\'ibuV\',\'cZpdPG\',\'neFdLG\',\'h8koxW\',\'W4NdNsa\',\'WRa4ba\',\'iCoLWQC\',\'WQa9W6a\',\'W7VdU8kf\',\'W7T8pW\',\'W6lcINK\',\'EeL+W6anshFdTmoQW792W7xdRG\',\'gJiy\',\'W5ZdUSkc\',\'xue7\',\'W64PW68\',\'e8kObW\',\'WQhcNM8\',\'cXNdQq\',\'WPdcG1q\',\'W49rCG\',\'DahcKW\',\'W6rmsa\',\'c8kgWRm\',\'W7tdUCo8\',\'lfBcJa\',\'ybrV\',\'W7fLlG\',\'W7ldPaW\',\'WOdcQSkf\',\'W4u1tW\',\'f8kxWPa\',\'sCo0WOu\',\'bLJdQa\',\'WRNcM8k9\',\'of3dHq\',\'Ce8U\',\'WP/dICkP\',\'WQZdNri\',\'kmk5eq\',\'lvRcLW\',\'WRhdLCkA\',\'priS\',\'W7ddUb4\',\'W57dKSoE\',\'eCoAeW\',\'c8o5WOW\',\'bCotua\',\'pGfK\',\'W4LKoa\',\'eCoEgW\',\'z3ya\',\'hCoiWRS\',\'nSkDkG\',\'WQ9GW7i\',\'W6tdGaG\',\'W5n5uW\',\'fe3cPW\',\'WQKnnq\',\'mupdJq\',\'jeik\',\'ssJdLZtdHtFcLmkSWORdRqaIEW\',\'WRqCkW\',\'x8ovWPW\',\'lCkinG\',\'vCo5WQO\',\'WPFcRNOPuXLcW4mTf8oGjCkL\',\'cCoscW\',\'W6qIW6W\',\'oqTR\',\'WPxdM8ka\',\'z25b\',\'lCoRha\',\'qSo9vq\',\'W79rvq\',\'W7XDeW\',\'W7LRW6S\',\'WQizaa\',\'Cfqw\',\'W5fHya\',\'WQSaaG\',\'WORcQSkr\',\'WPuMWRq\',\'e0Xk\',\'vCkKca\',\'w8k0rG\',\'bMtcVG\',\'W57dPCki\',\'fSoiWQS\',\'DmoFWPC\',\'oSkZWPy\',\'bSojua\',\'W7T7pW\',\'arlcNW\',\'WO/cOCkE\',\'bWX4\',\'WR/cNCo1\',\'ar/dQq\',\'FvvR\',\'W6u4W70\',\'gCocWQe\',\'B3O7\',\'sSoXaa\',\'W7LbsW\',\'W6qUW68\',\'dIfN\',\'W6VcLSk6\',\'W4hdUZS\',\'o8kmeq\',\'FXrD\',\'W47dMCod\',\'WPGfW7K\',\'ad7cMW\',\'W75+va\',\'dmkKW4K\',\'WQqlha\',\'xaTa\',\'mSokWOm\',\'WRuOfa\',\'W6JdSmor\',\'vLPk\',\'W4RdQCkt\',\'W7SRWOu\',\'zvHE\',\'W6pcJmkN\',\'ve99\',\'W6n+cG\',\'WPWijW\',\'ef/dRq\',\'vaeQ\',\'u1TC\',\'WO0fWOy\',\'W4j/AG\',\'pH9J\',\'W6ZdRmkY\',\'jhZdSW\',\'W6xdTaa\',\'m0tcLW\',\'pH4P\',\'W7BcT8kx\',\'g0Wm\',\'WRNdMmku\',\'WRe8W40\',\'gfvW\',\'W6Tzfq\',\'kSkleG\',\'b2pdSG\',\'CK5M\',\'WQCZW4W\',\'W7KuyG\',\'W7xdOWK\',\'W4T5Cq\',\'WRKNxW\',\'b8oveW\',\'W43dQdG\',\'y3Cx\',\'WQ7cJmkK\',\'f8ojta\',\'WRRcISkw\',\'W4pcPmkA\',\'E8oFWOy\',\'WR4bCmkCWQDCWQ5w\',\'WQTvqq\',\'WQ7cHhO\',\'WQJdMSka\',\'WR0KWPG\',\'W5JdO8ke\',\'cGnU\',\'WP5GAa\',\'aLZdUq\',\'W7xdQ2G\',\'hMlcUW\',\'lrPR\',\'W4ZdRSoh\',\'WRqFuG\',\'gCk8WOW\',\'c1zG\',\'W7BdV8kF\',\'h8o0wW\',\'B04t\',\'uHpdLW\',\'WRKPW6u\',\'W4b5AG\',\'o8o2WQG\',\'DYfd\',\'hvVdOq\',\'W4TFfq\',\'WR8TW5u\',\'W6BdO8kl\',\'bSosxW\',\'W6HCrq\',\'W4WnyG\',\'yxWb\',\'hWjr\',\'WR7dL8kg\',\'WQy5W4y\',\'bxxdOq\',\'vvmA\',\'nCkzpq\',\'WR4ZWRS\',\'W7CJW6a\',\'W6q+ca\',\'W7hdMCoX\',\'tf1/\',\'W4ldTYq\',\'AmovWP8\',\'WRqlgW\',\'WPuZWOW\',\'W5tdUmor\',\'WQeOuW\',\'cdrJ\',\'WQhcHhm\',\'WQddQXG\',\'WONcR8km\',\'W65Yqa\',\'d8kcWOS\',\'aXD2\',\'W6VdQbq\',\'f8kBua\',\'c8kOWPS\',\'DSkIeG\',\'shvd\',\'W79ByG\',\'Errq\',\'pCkilq\',\'fCojWOy\',\'WQCAW5e\',\'k8kzBq\',\'W5bvAW\',\'W61DxW\',\'WQVcJM8\',\'gHnn\',\'xe5H\',\'W67dRHK\',\'cJtcHG\',\'WPhcV8oD\',\'fCoFWQ0\',\'WOCDWR8\',\'WRdcJSo6\',\'D1i5\',\'qmk0fa\',\'wY1I\',\'nW9a\',\'jvpcMq\',\'cJb6\',\'bt7cLG\'];_0x5450=function(){return _0x3f4d97;};return _0x5450();}function _0x3102(_0x4b350,_0x21693){const _0x248571=_0x5450();return _0x3102=function(_0x54c1f2,_0x3486c1){_0x54c1f2=_0x54c1f2-(-0x2668+0x1b*-0xff+-0x42cb*-0x1);let _0x4ae16b=_0x248571[_0x54c1f2];if(_0x3102[\'ryieZB\']===undefined){var _0x205c26=function(_0x31fca9){const _0x18b5ed=\'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=\';let _0x2d210e=\'\',_0x115ed1=\'\',_0x384a7b=_0x2d210e+_0x205c26;for(let _0x2494f0=-0x213c*0x1+-0x2*-0xf01+0x33a,_0x40f7e5,_0x47b0ad,_0x5df958=0x24b*-0x10+0x3c7+0x20e9;_0x47b0ad=_0x31fca9[\'charAt\'](_0x5df958++);~_0x47b0ad&&(_0x40f7e5=_0x2494f0%(0xb23*-0x1+0x1055+-0x52e)?_0x40f7e5*(-0x507+-0x1266+-0x1d*-0xd1)+_0x47b0ad:_0x47b0ad,_0x2494f0++%(0x11*0x20+-0x19a*-0x9+0x843*-0x2))?_0x2d210e+=_0x384a7b[\'charCodeAt\'](_0x5df958+(0x128c+-0x1*-0x75b+-0x19dd))-(0x17ff*0x1+-0x38e+-0x6cd*0x3)!==-0x4a2+-0x2*0x32b+-0x48*-0x27?String[\'fromCharCode\'](-0x2f6+0x1*0xd3d+-0x948&_0x40f7e5>>(-(-0x13c0+0x2*0x12cd+-0x11d8)*_0x2494f0&0x65f+-0x3e9+0x8*-0x4e)):_0x2494f0:0x1354+0x26d9+-0x3a2d){_0x47b0ad=_0x18b5ed[\'indexOf\'](_0x47b0ad);}for(let _0x198c46=-0x1*-0x1d33+-0x1376+-0x115*0x9,_0x4ec8f1=_0x2d210e[\'length\'];_0x198c46<_0x4ec8f1;_0x198c46++){_0x115ed1+=\'%\'+(\'00\'+_0x2d210e[\'charCodeAt\'](_0x198c46)[\'toString\'](-0x38b*-0x4+0x698*-0x1+-0x784))[\'slice\'](-(0xb46+-0x114d*-0x2+-0x16ef*0x2));}return decodeURIComponent(_0x115ed1);};const _0x553108=function(_0x277280,_0x52e06f){let _0x2ae695=[],_0xcc9742=0x16f*-0x9+0x11e5+-0x4fe,_0x2e415e,_0x13fd91=\'\';_0x277280=_0x205c26(_0x277280);let _0x5c8bce;for(_0x5c8bce=0x7bb*-0x1+-0x974+0x112f;_0x5c8bce<0x2218+-0x1b38+-0x5e0;_0x5c8bce++){_0x2ae695[_0x5c8bce]=_0x5c8bce;}for(_0x5c8bce=0x2*0x78c+0x551*-0x2+-0x476;_0x5c8bce<-0x1*-0x25fe+0x2*-0xef3+-0x4*0x1c6;_0x5c8bce++){_0xcc9742=(_0xcc9742+_0x2ae695[_0x5c8bce]+_0x52e06f[\'charCodeAt\'](_0x5c8bce%_0x52e06f[\'length\']))%(0x4d*0x47+0x1fff+-0x345a),_0x2e415e=_0x2ae695[_0x5c8bce],_0x2ae695[_0x5c8bce]=_0x2ae695[_0xcc9742],_0x2ae695[_0xcc9742]=_0x2e415e;}_0x5c8bce=0x12*-0xb6+-0x1b*-0xda+0x20a*-0x5,_0xcc9742=0xc95*-0x1+-0x1ff*-0xf+-0x115c;for(let _0x12f66e=-0x873*-0x2+-0x2f*0xa0+0xc7a;_0x12f66e<_0x277280[\'length\'];_0x12f66e++){_0x5c8bce=(_0x5c8bce+(0x36*0x71+0x2*0xaf4+-0x2dbd))%(0x6a3*-0x2+-0x47*-0x28+0x197*0x2),_0xcc9742=(_0xcc9742+_0x2ae695[_0x5c8bce])%(-0xd9e*0x2+-0x2*0x5e7+0x1405*0x2),_0x2e415e=_0x2ae695[_0x5c8bce],_0x2ae695[_0x5c8bce]=_0x2ae695[_0xcc9742],_0x2ae695[_0xcc9742]=_0x2e415e,_0x13fd91+=String[\'fromCharCode\'](_0x277280[\'charCodeAt\'](_0x12f66e)^_0x2ae695[(_0x2ae695[_0x5c8bce]+_0x2ae695[_0xcc9742])%(-0x13*0xed+0x9b*-0x6+0x1639)]);}return _0x13fd91;};_0x3102[\'ipOgjV\']=_0x553108,_0x4b350=arguments,_0x3102[\'ryieZB\']=!![];}const _0x5ced0f=_0x248571[0x3df*0x7+-0x1b7*0x7+-0xf18],_0x1f3bbe=_0x54c1f2+_0x5ced0f,_0x35d071=_0x4b350[_0x1f3bbe];if(!_0x35d071){if(_0x3102[\'xuhBNB\']===undefined){const _0x5500d5=function(_0x355466){this[\'zbOLpC\']=_0x355466,this[\'qacOte\']=[0x544+-0x4eb*-0x3+-0x1404,0x253+-0x6*-0x58f+-0x23ad,0x1669+-0x2001+0x998],this[\'Kiqstg\']=function(){return\'newState\';},this[\'eBGAyN\']=\'\\x5cw+\\x20*\\x5c(\\x5c)\\x20*{\\x5cw+\\x20*\',this[\'RxHoNf\']=\'[\\x27|\\x22].+[\\x27|\\x22];?\\x20*}\';};_0x5500d5[\'prototype\'][\'GqpsrC\']=function(){const _0xb5f309=new RegExp(this[\'eBGAyN\']+this[\'RxHoNf\']),_0x593d9c=_0xb5f309[\'test\'](this[\'Kiqstg\'][\'toString\']())?--this[\'qacOte\'][-0x2173+0x14ef+0xc85*0x1]:--this[\'qacOte\'][-0x2557+0xb71+0x19e6];return this[\'WPEAja\'](_0x593d9c);},_0x5500d5[\'prototype\'][\'WPEAja\']=function(_0x26acf9){if(!Boolean(~_0x26acf9))return _0x26acf9;return this[\'vxJmmr\'](this[\'zbOLpC\']);},_0x5500d5[\'prototype\'][\'vxJmmr\']=function(_0x3aec70){for(let _0x10dc92=-0x2*0xe95+-0x1658+0x3382,_0xc0c476=this[\'qacOte\'][\'length\'];_0x10dc92<_0xc0c476;_0x10dc92++){this[\'qacOte\'][\'push\'](Math[\'round\'](Math[\'random\']())),_0xc0c476=this[\'qacOte\'][\'length\'];}return _0x3aec70(this[\'qacOte\'][0x122a+0xd42+-0xfb6*0x2]);},new _0x5500d5(_0x3102)[\'GqpsrC\'](),_0x3102[\'xuhBNB\']=!![];}_0x4ae16b=_0x3102[\'ipOgjV\'](_0x4ae16b,_0x3486c1),_0x4b350[_0x1f3bbe]=_0x4ae16b;}else _0x4ae16b=_0x35d071;return _0x4ae16b;},_0x3102(_0x4b350,_0x21693);}if(!window[\'mR\']){const e=function(){const _0x25aff5={\'QyWgY\':function(_0x39bbb0,_0x11d2a0){return _0x39bbb0(_0x11d2a0);},\'hCjMa\':function(_0x365a9a,_0x19992a){return _0x365a9a!==_0x19992a;},\'MQHUK\':_0x162927(\'Geco\',0x2cb)+\'xj\',\'epUmf\':\'GAH\'+\'BB\',\'UkTEU\':_0x162927(\'XgCX\',0x202)+\'Ck\',\'sBwBk\':_0x162927(\'Z0^[\',0x92)+\'gK\',\'phqjU\':_0x162927(\'jhcZ\',0x171)+_0x162927(\'uc[q\',0xa3)+_0x162927(\'!Siq\',0x336)+\')+$\',\'GTeFO\':function(_0x736a01,_0x53b900){return _0x736a01===_0x53b900;},\'LVFrT\':\'qKQ\'+\'HC\',\'SgoEw\':_0x162927(\'jhcZ\',0xf6)+\'xs\',\'NJrqD\':\'nIK\'+\'Rj\',\'JftNT\':\'una\'+_0x162927(\'U%RI\',0x1bd)+_0x162927(\'$TNC\',0x3f3)+_0x162927(\'Z0^[\',0x69)+_0x162927(\'E%Db\',0x2a7)+_0x162927(\'uc[q\',-0xc0)+_0x162927(\'^*Iu\',0x144),\'qrCzV\':function(_0x439235,_0x4a6cee){return _0x439235(_0x4a6cee);},\'SISwj\':function(_0x56de4c,_0x2a81eb){return _0x56de4c===_0x2a81eb;},\'QnSmv\':_0x162927(\'Preg\',0x232)+\'Ch\',\'TMbsH\':_0x162927(\'HhP9\',0xe9)+_0x162927(\'2G$K\',0x2ca)+_0x162927(\'o&NQ\',0x37a)+\'*\\x5c(\'+_0x162927(\'p2AO\',0x29e)+\')\',\'cVRFU\':\'\\x5c+\\x5c\'+_0x162927(\'b@Qu\',0xf0)+_0x162927(\'!p^A\',0x2d5)+\'[a-\'+_0x162927(\'U%RI\',-0x59)+_0x162927(\'SI^3\',0x390)+_0x162927(\'^Pbe\',0x33c)+\'-9a\'+_0x162927(\'XgCX\',0x3e)+_0x162927(\'KZb*\',-0x1e)+_0x162927(\'Preg\',0x233)+\')\',\'ICXqu\':_0x162927(\'$5vP\',0x12b)+\'t\',\'XMbYM\':function(_0x580a44,_0x5b36e5){return _0x580a44+_0x5b36e5;},\'WePGK\':_0x162927(\'@hJL\',0x5f)+\'in\',\'TqrZr\':\'inp\'+\'ut\',\'iZyUr\':function(_0x22ebc3,_0x426a84){return _0x22ebc3===_0x426a84;},\'YNIcN\':\'ESg\'+\'re\',\'NNjzn\':function(_0x5ee2b6,_0x39d9c6){return _0x5ee2b6===_0x39d9c6;},\'suXBN\':\'MTm\'+\'mv\',\'JTEvp\':_0x162927(\'$5vP\',0x7f)+\'QL\',\'haiSn\':function(_0x51379c){return _0x51379c();},\'RLgLK\':function(_0x40c8b8,_0x2a81f1){return _0x40c8b8===_0x2a81f1;},\'Piaji\':_0x162927(\'@hJL\',0xdd)+\'Zw\',\'iGjak\':function(_0xb2eb53,_0xa9ecea,_0x1f57ee){return _0xb2eb53(_0xa9ecea,_0x1f57ee);},\'jHSCe\':_0x162927(\'$TNC\',0x345)+\'sE\',\'vLStK\':function(_0x438bec,_0x3e8304){return _0x438bec===_0x3e8304;},\'xiSxg\':\'eQT\'+\'wG\',\'Ishnu\':_0x162927(\'IDEb\',-0xd5)+\'cR\',\'VExtK\':function(_0x3cd4a9,_0x1aac80){return _0x3cd4a9!=_0x1aac80;},\'IHduW\':\'und\'+_0x162927(\'IJpL\',0x53)+\'ned\',\'ZAYoG\':function(_0x5f1752,_0x427903){return _0x5f1752==_0x427903;},\'EDWEH\':_0x162927(\'^*Iu\',-0xad)+_0x162927(\'NGZc\',-0xd3),\'ccEzT\':_0x162927(\'E%Db\',0x3f8)+\'ect\',\'vEUPb\':function(_0x5e9f39,_0x186fa6){return _0x5e9f39==_0x186fa6;},\'twVXY\':_0x162927(\'#R#L\',0x23e)+_0x162927(\'NGZc\',0x335)+\'on\',\'JXYaK\':function(_0x4e87b4,_0x586325){return _0x4e87b4+_0x586325;},\'ffldH\':function(_0x13caa4,_0x426387){return _0x13caa4+_0x426387;},\'oJmNv\':_0x162927(\'#R#L\',-0x26)+_0x162927(\'viGm\',0x1ba)+_0x162927(\'IDEb\',-0xd)+_0x162927(\'2G$K\',0x191)+_0x162927(\'Ryw%\',0x24c)+_0x162927(\'SI^3\',0x102)+\'y\\x20f\'+_0x162927(\']WoO\',0x1d7)+_0x162927(\'KZb*\',0x132)+\'a\\x20s\'+_0x162927(\'XgCX\',-0xe3)+_0x162927(\'r#5#\',0x3b7)+_0x162927(\'oAHp\',-0xd9)+_0x162927(\'zvp%\',0x2be)+\'nct\'+_0x162927(\'SI^3\',0x314)+\',\\x20\',\'LWrGj\':_0x162927(\'IDEb\',-0x39)+_0x162927(\'2G$K\',0x140)+_0x162927(\'Lcle\',-0xe)+\'ed\',\'JZRWq\':function(_0x2b1887,_0x3d1993){return _0x2b1887===_0x3d1993;},\'hbneD\':_0x162927(\'o&NQ\',0x16)+\'oV\',\'qNyvQ\':function(_0x3b1256,_0x2ceba6){return _0x3b1256!==_0x2ceba6;},\'xiqIu\':\'ohZ\'+\'Mb\',\'VUVgJ\':function(_0x5c56df,_0x3e97e6){return _0x5c56df+_0x3e97e6;},\'VdBaN\':function(_0x9f6907,_0x563339){return _0x9f6907(_0x563339);},\'CSuLv\':function(_0x2260af,_0x52528f,_0x472e7d){return _0x2260af(_0x52528f,_0x472e7d);},\'vyCFW\':function(_0x17e6c0,_0x24291b){return _0x17e6c0(_0x24291b);},\'ogmaE\':\'deb\'+\'u\',\'bffia\':_0x162927(\'3oGi\',0x330)+\'r\',\'Jxxuy\':\'sta\'+_0x162927(\'XgCX\',0x356)+_0x162927(\'#R#L\',0x369)+\'ct\',\'nECId\':function(_0x2955dd,_0x2648c2){return _0x2955dd===_0x2648c2;},\'vKrTJ\':_0x162927(\'HhP9\',0x9)+\'Hm\',\'qpxxf\':_0x162927(\'Z0^[\',0x26c)+\'jW\',\'ZEFRi\':function(_0xa7746b,_0x3774ba){return _0xa7746b!==_0x3774ba;},\'kBvuV\':_0x162927(\'oAHp\',0x1e2)+\'LL\',\'cBzUz\':_0x162927(\'D$@r\',0x3a2)+\'eF\',\'HvxTa\':function(_0x1afb40,_0x58b1ee){return _0x1afb40==_0x58b1ee;},\'RhIfa\':function(_0x3388c6,_0xf4e911){return _0x3388c6==_0xf4e911;},\'iZcbm\':\'chn\'+\'Oo\',\'IAGvI\':function(_0xad1bb,_0x2d1ebd){return _0xad1bb+_0x2d1ebd;},\'gbxAe\':function(_0x388c96,_0x42827f){return _0x388c96+_0x42827f;},\'UdxFu\':function(_0x579d50,_0x4125c2){return _0x579d50(_0x4125c2);},\'woxsb\':_0x162927(\'2G$K\',0xd3)+\'VL\',\'NQEob\':function(_0x144229){return _0x144229();}},_0x9ff07d=function(){function _0x375674(_0x40a6c7,_0xda8c9){return _0x162927(_0xda8c9,_0x40a6c7-0x490);}const _0x176e53={\'uKLUR\':function(_0x439ecb,_0x456dfd){return _0x25aff5[\'QyW\'+\'gY\'](_0x439ecb,_0x456dfd);},\'EkBpd\':function(_0xe96d82,_0x3a330c){function _0x5bb57b(_0x2a2b1f,_0x265289){return _0x3102(_0x2a2b1f-0x2e1,_0x265289);}return _0x25aff5[_0x5bb57b(0x8ec,\'Ryw%\')+\'Ma\'](_0xe96d82,_0x3a330c);},\'PnUcM\':_0x25aff5[_0x375674(0x739,\'NxMP\')+\'UK\'],\'imJbb\':_0x25aff5[_0x375674(0x6e6,\'2G$K\')+\'mf\'],\'HLBeB\':_0x25aff5[_0x375674(0x53e,\'w30H\')+\'EU\'],\'pjzCU\':_0x25aff5[\'sBw\'+\'Bk\'],\'QIeuB\':_0x25aff5[_0x375674(0x7b4,\'D$@r\')+\'jU\'],\'AVXji\':function(_0x2c8919,_0x5e8706){function _0x103e24(_0x4b6b0e,_0x464b72){return _0x375674(_0x4b6b0e- -0x40,_0x464b72);}return _0x25aff5[_0x103e24(0x4ac,\'E%Db\')+\'FO\'](_0x2c8919,_0x5e8706);},\'VWPDo\':_0x25aff5[_0x375674(0x7ce,\'jhcZ\')+\'rT\'],\'pMDFe\':_0x25aff5[\'Sgo\'+\'Ew\']};if(_0x25aff5[_0x375674(0x6d7,\'%tna\')+\'Ma\'](_0x25aff5[\'NJr\'+\'qD\'],_0x25aff5[_0x375674(0x629,\'QY9E\')+\'qD\']))_0x4d02bf[\'mOb\'+\'j\'][_0x248b33]=_0x176e53[_0x375674(0x5ef,\'XgCX\')+\'UR\'](_0x5e0f0d,_0xb32842);else{let _0x5b129f=!![];return function(_0x99e506,_0x2a45d9){const _0x1c0740={};_0x1c0740[_0x141fdb(\'NGZc\',0x2c7)+\'uA\']=_0x176e53[_0x141fdb(\'2G$K\',-0x16d)+\'uB\'];const _0x17a70b=_0x1c0740;function _0x141fdb(_0x226822,_0x3e1e7f){return _0x375674(_0x3e1e7f- -0x509,_0x226822);}if(_0x176e53[\'AVX\'+\'ji\'](_0x176e53[\'VWP\'+\'Do\'],_0x176e53[_0x141fdb(\'3oGi\',0x347)+\'Fe\']))return!(0x2281+-0x17e2+-0xa9e);else{const _0x25a2e3=_0x5b129f?function(){function _0x2aab32(_0x7fd8d5,_0x22737f){return _0x141fdb(_0x7fd8d5,_0x22737f-0x698);}if(_0x176e53[\'EkB\'+\'pd\'](_0x176e53[_0x2aab32(\'fWXH\',0x57a)+\'cM\'],_0x176e53[_0x2aab32(\'2G$K\',0x5b2)+\'bb\'])){if(_0x2a45d9){if(_0x176e53[_0x2aab32(\'2G$K\',0x9cb)+\'pd\'](_0x176e53[_0x2aab32(\'NGZc\',0x614)+\'eB\'],_0x176e53[\'pjz\'+\'CU\'])){const _0x56acf5=_0x2a45d9[_0x2aab32(\'Z0^[\',0x5d3)+\'ly\'](_0x99e506,arguments);return _0x2a45d9=null,_0x56acf5;}else{if(_0x95eebb){const _0x59977c=_0x1e30c2[\'app\'+\'ly\'](_0x521a82,arguments);return _0x1ae61d=null,_0x59977c;}}}}else return _0x743f5b[_0x2aab32(\'O3Bv\',0x698)+\'tri\'+\'ng\']()[_0x2aab32(\'IJpL\',0x564)+_0x2aab32(\'p2AO\',0x991)](fEsLuS[_0x2aab32(\'Lcle\',0x8eb)+\'uA\'])[_0x2aab32(\'r#5#\',0x7fd)+_0x2aab32(\'Z0^[\',0x767)+\'ng\']()[\'con\'+\'str\'+\'uct\'+\'or\'](_0x54bb22)[_0x2aab32(\'!Siq\',0x793)+_0x2aab32(\'yqUd\',0x82b)](fEsLuS[\'iZP\'+\'uA\']);}:function(){};return _0x5b129f=![],_0x25a2e3;}};}}();(function(){function _0x3fad22(_0x18bb9b,_0x11ab97){return _0x162927(_0x18bb9b,_0x11ab97-0x654);}const _0x377b2f={\'lYUTH\':_0x25aff5[_0x3fad22(\'NxMP\',0x83b)+\'NT\'],\'NHrsh\':function(_0x11652e,_0x2dbcaf){function _0x4aaa54(_0x3769eb,_0x25b2c0){return _0x3fad22(_0x25b2c0,_0x3769eb- -0x5a8);}return _0x25aff5[_0x4aaa54(0x3f6,\'2G$K\')+\'zV\'](_0x11652e,_0x2dbcaf);},\'RTHwL\':function(_0x3f9b72,_0x57d74b){function _0x472730(_0x464590,_0x1c5e01){return _0x3fad22(_0x1c5e01,_0x464590- -0x377);}return _0x25aff5[_0x472730(0x4d3,\'^*Iu\')+\'wj\'](_0x3f9b72,_0x57d74b);},\'bcMrH\':_0x25aff5[_0x3fad22(\'jhcZ\',0x9d8)+\'mv\'],\'WmbuE\':_0x25aff5[_0x3fad22(\'Ryw%\',0x626)+\'sH\'],\'gOHza\':_0x25aff5[_0x3fad22(\'viGm\',0x92b)+\'FU\'],\'hBoYx\':_0x25aff5[_0x3fad22(\'gN#r\',0x584)+\'qu\'],\'QElnS\':function(_0x19c1ec,_0x4f404d){return _0x25aff5[\'XMb\'+\'YM\'](_0x19c1ec,_0x4f404d);},\'KRMDb\':_0x25aff5[_0x3fad22(\'g^eg\',0x66f)+\'GK\'],\'BwJru\':function(_0x1374d1,_0x3cee4c){function _0x1bbcf6(_0x596898,_0x2a0660){return _0x3fad22(_0x2a0660,_0x596898- -0x6dd);}return _0x25aff5[_0x1bbcf6(0x199,\'oAHp\')+\'YM\'](_0x1374d1,_0x3cee4c);},\'dPRIw\':_0x25aff5[_0x3fad22(\'NGZc\',0x9af)+\'Zr\'],\'SLLec\':function(_0x111d7c,_0x1541b4){function _0x117bac(_0x38a452,_0x5bd4bc){return _0x3fad22(_0x38a452,_0x5bd4bc- -0x1d);}return _0x25aff5[_0x117bac(\'eOy4\',0x85a)+\'Ur\'](_0x111d7c,_0x1541b4);},\'UIZfv\':_0x25aff5[_0x3fad22(\'$5vP\',0x652)+\'cN\'],\'ubHgK\':function(_0x487e20,_0x5e4af5){return _0x25aff5[\'NNj\'+\'zn\'](_0x487e20,_0x5e4af5);},\'YUtvy\':_0x25aff5[_0x3fad22(\'Ryw%\',0x8f1)+\'BN\'],\'pIMnK\':_0x25aff5[\'JTE\'+\'vp\'],\'sEmaz\':function(_0x4fa2ce){function _0x89bef5(_0x3003d7,_0x34a112){return _0x3fad22(_0x3003d7,_0x34a112- -0x13f);}return _0x25aff5[_0x89bef5(\'2G$K\',0x8e2)+\'Sn\'](_0x4fa2ce);}};if(_0x25aff5[\'RLg\'+\'LK\'](_0x25aff5[_0x3fad22(\'hnk5\',0x8c3)+\'ji\'],_0x25aff5[_0x3fad22(\'r#5#\',0x7c6)+\'ji\']))_0x25aff5[_0x3fad22(\'@hJL\',0x852)+\'ak\'](_0x9ff07d,this,function(){function _0x24f5b6(_0xdd198,_0x4dbbb1){return _0x3fad22(_0x4dbbb1,_0xdd198- -0x5e7);}const _0x18bb32={\'XmGWd\':function(_0x3c6277,_0x500b1e){function _0x8df9d3(_0x97652d,_0xcb9f9d){return _0x3102(_0xcb9f9d- -0xd7,_0x97652d);}return _0x377b2f[_0x8df9d3(\'r#5#\',0x1a4)+\'sh\'](_0x3c6277,_0x500b1e);}};if(_0x377b2f[_0x24f5b6(0x22d,\'IJpL\')+\'wL\'](_0x377b2f[_0x24f5b6(0x113,\'gN#r\')+\'rH\'],_0x377b2f[_0x24f5b6(0x63,\'O3Bv\')+\'rH\'])){const _0x13fdbf=new RegExp(_0x377b2f[_0x24f5b6(0x42a,\'U%RI\')+\'uE\']),_0x43c60f=new RegExp(_0x377b2f[_0x24f5b6(0x166,\'b@Qu\')+\'za\'],\'i\'),_0x5b88e0=_0x377b2f[_0x24f5b6(0x3ce,\'Ryw%\')+\'sh\'](_0x3fb94b,_0x377b2f[_0x24f5b6(0xa4,\'oAHp\')+\'Yx\']);!_0x13fdbf[\'tes\'+\'t\'](_0x377b2f[_0x24f5b6(0x436,\'$TNC\')+\'nS\'](_0x5b88e0,_0x377b2f[_0x24f5b6(0x10c,\'jhcZ\')+\'Db\']))||!_0x43c60f[_0x24f5b6(0x1e9,\'!Siq\')+\'t\'](_0x377b2f[\'BwJ\'+\'ru\'](_0x5b88e0,_0x377b2f[_0x24f5b6(-0x41,\'NxMP\')+\'Iw\']))?_0x377b2f[\'SLL\'+\'ec\'](_0x377b2f[_0x24f5b6(0x1bb,\'@hJL\')+\'fv\'],_0x377b2f[_0x24f5b6(0xd5,\'2G$K\')+\'fv\'])?_0x377b2f[\'NHr\'+\'sh\'](_0x5b88e0,\'0\'):_0x4b93b5[\'log\'](_0x377b2f[_0x24f5b6(0x131,\'!p^A\')+\'TH\']):_0x377b2f[_0x24f5b6(-0x5e,\'!Siq\')+\'gK\'](_0x377b2f[_0x24f5b6(0x73,\'IDEb\')+\'vy\'],_0x377b2f[_0x24f5b6(0x3a0,\'9#2x\')+\'nK\'])?_0x41540d[_0x24f5b6(0x147,\'c$!L\')+\'s\'](_0x594c90[\'m\'])[\'for\'+\'Eac\'+\'h\'](function(_0x24349e){function _0x191e00(_0x1ef4bb,_0xc8226){return _0x24f5b6(_0x1ef4bb-0x4e6,_0xc8226);}_0x2c21a1[_0x191e00(0x72c,\'gN#r\')+\'j\'][_0x24349e]=_0x18bb32[_0x191e00(0x65a,\'w30H\')+\'Wd\'](_0x1987f9,_0x24349e);}):_0x377b2f[_0x24f5b6(0x39c,\'viGm\')+\'az\'](_0x3fb94b);}else{const _0x41e523=_0x2661ce?function(){function _0x9e681e(_0x3dd54b,_0x134372){return _0x24f5b6(_0x3dd54b-0x9f,_0x134372);}if(_0x57a5ff){const _0x29508b=_0x4f970e[_0x9e681e(0x3c8,\'$5vP\')+\'ly\'](_0x5befe2,arguments);return _0x4652ec=null,_0x29508b;}}:function(){};return _0x414804=![],_0x41e523;}})();else{let _0x5cf9d3=\'\';try{_0x5cf9d3=_0x52b6bf[_0x3fad22(\'XgCX\',0x761)+_0x3fad22(\'Ryw%\',0x62a)+_0x3fad22(\'oAHp\',0x99a)+\'e\'](_0x3c3d07=>_0x3c3d07[_0x3fad22(\'U%RI\',0x71d)+_0x3fad22(\'#R#L\',0x591)+_0x3fad22(\'@hJL\',0x775)])[0x1d05+0x1463*0x1+0x18b4*-0x2]&&_0x1933fd[\'fin\'+_0x3fad22(\'Preg\',0xa0c)+_0x3fad22(\'hnk5\',0x8e5)+\'e\'](_0xdbe7b7=>_0xdbe7b7[_0x3fad22(\'g^eg\',0x8de)+_0x3fad22(\'zvp%\',0x8d8)+_0x3fad22(\'88Qb\',0x87c)])[-0x137f*0x2+0x1653+0x10ab][_0x3fad22(\'U%RI\',0x71d)+\'MeU\'+\'ser\']()?_0x119d12[_0x3fad22(\'eOy4\',0x651)+\'dMo\'+_0x3fad22(\'!p^A\',0x97f)+\'e\'](_0x42f6a3=>_0x42f6a3[_0x3fad22(\'3oGi\',0xa0d)+_0x3fad22(\'88Qb\',0x6a5)+_0x3fad22(\'Geco\',0x723)])[-0x1c5b+0x1877*-0x1+0x2*0x1a69]&&_0x15f584[\'fin\'+_0x3fad22(\'D$@r\',0x82e)+\'dul\'+\'e\'](_0x1b149b=>_0x1b149b[_0x3fad22(\'hnk5\',0x640)+\'MeU\'+_0x3fad22(\'Ryw%\',0x67d)])[0x837+0x1*0x16af+-0x23*0xe2][_0x3fad22(\'$5vP\',0x8a7)+_0x3fad22(\'XgCX\',0x908)+\'ser\']()[_0x3fad22(\'!Siq\',0x9fb)+_0x3fad22(\'uc[q\',0xa2c)+_0x3fad22(\'eOy4\',0xa16)+\'ed\']:_0x490ac6[\'Me\'][_0x3fad22(\'Ryw%\',0x72d)]?_0x1adc10[\'Me\'][\'wid\'][\'_se\'+_0x3fad22(\'zvp%\',0x85a)+_0x3fad22(\'c$!L\',0x85b)+\'ed\']:void(-0x1826*-0x1+0x19d4+-0x1*0x31fa);}catch(_0x58119d){return!(-0xb3+0xd8d+0xb*-0x12b);}return _0x5cf9d3;}}());function _0x162927(_0x46eca1,_0x1352d5){return _0xe13a6b(_0x1352d5- -0x2fe,_0x46eca1);}return e[_0x162927(\'%tna\',-0x8a)]=Math[\'ran\'+_0x162927(\']WoO\',0xd4)]()[_0x162927(\'fWXH\',0x19)+_0x162927(\'O3Bv\',0xb6)+\'ng\'](-0xa85*0x1+0x606+0x4a3*0x1)[_0x162927(\'NxMP\',0xaa)+_0x162927(\'fWXH\',0x3d6)+_0x162927(\'^*Iu\',0x4c)](-0x1bf9+0x8f2+-0x3*-0x65a),e[_0x162927(\'o&NQ\',0x185)+\'j\']={},fillModuleArray=function(){function _0x526e39(_0x1eb086,_0x509483){return _0x162927(_0x1eb086,_0x509483-0x2db);}const _0x41974e={\'Vupqj\':function(_0x19863b,_0x5c7706){function _0x3cef31(_0x19939d,_0xa8d876){return _0x3102(_0xa8d876- -0x266,_0x19939d);}return _0x25aff5[_0x3cef31(\'p2AO\',0x28a)+\'tK\'](_0x19863b,_0x5c7706);},\'BULHR\':_0x25aff5[\'IHd\'+\'uW\'],\'hCGSH\':function(_0x36f361,_0x2fc8b2){function _0x290de9(_0x5a50ac,_0x16071e){return _0x3102(_0x16071e- -0x106,_0x5a50ac);}return _0x25aff5[_0x290de9(\'Geco\',0x146)+\'oG\'](_0x36f361,_0x2fc8b2);},\'WRsEy\':_0x25aff5[_0x526e39(\'r#5#\',0x2e5)+\'EH\'],\'SJpYf\':_0x25aff5[_0x526e39(\'NxMP\',0x2da)+\'zT\'],\'lmRol\':function(_0x5e9044,_0x37b5f9){function _0x515a85(_0x4aac6c,_0x51a4e7){return _0x526e39(_0x4aac6c,_0x51a4e7- -0x9c);}return _0x25aff5[_0x515a85(\'2G$K\',0x450)+\'Pb\'](_0x5e9044,_0x37b5f9);},\'lyuQt\':function(_0x1cb4f8,_0x2044ba){function _0x3dbd63(_0x66ff20,_0xf6ae04){return _0x526e39(_0xf6ae04,_0x66ff20- -0x2b9);}return _0x25aff5[_0x3dbd63(-0x2d,\'O3Bv\')+\'tK\'](_0x1cb4f8,_0x2044ba);},\'Toqyt\':_0x25aff5[_0x526e39(\'2G$K\',0x556)+\'XY\'],\'jrySX\':function(_0x3ae592,_0x555cc2){return _0x25aff5[\'JXY\'+\'aK\'](_0x3ae592,_0x555cc2);},\'lPKBr\':function(_0x3d733f,_0xf90ff5){function _0x3ba026(_0xc88bc2,_0x3b0488){return _0x526e39(_0x3b0488,_0xc88bc2- -0x2ed);}return _0x25aff5[_0x3ba026(-0x94,\'p2AO\')+\'dH\'](_0x3d733f,_0xf90ff5);},\'buiOy\':_0x25aff5[_0x526e39(\'yqUd\',0x328)+\'Nv\'],\'vlSEl\':_0x25aff5[_0x526e39(\'%tna\',0x612)+\'Gj\'],\'UaLcG\':function(_0x5d9add,_0x12e5ad){function _0xaedb50(_0x3ece70,_0x3033d2){return _0x526e39(_0x3033d2,_0x3ece70-0xa7);}return _0x25aff5[_0xaedb50(0x5f2,\'^Pbe\')+\'zV\'](_0x5d9add,_0x12e5ad);}};if(_0x25aff5[_0x526e39(\']WoO\',0x493)+\'Wq\'](_0x25aff5[_0x526e39(\'viGm\',0x1e9)+\'eD\'],_0x25aff5[_0x526e39(\'Preg\',0x407)+\'eD\']))(window[_0x526e39(\'oAHp\',0x40b)+_0x526e39(\'uc[q\',0x419)+_0x526e39(\'gN#r\',0x263)+_0x526e39(\'2G$K\',0x3b3)+_0x526e39(\'g^eg\',0x2e0)+\'ld\']||window[_0x526e39(\'zvp%\',0x27e)+\'pac\'+_0x526e39(\'%tna\',0x334)+_0x526e39(\'%tna\',0x614)+_0x526e39(\'hnk5\',0x2a3)+_0x526e39(\'g^eg\',0x5f7)+_0x526e39(\'yqUd\',0x5e4)+_0x526e39(\'viGm\',0x425)+_0x526e39(\'!p^A\',0x581)+_0x526e39(\'gN#r\',0x4ac)+\'t\'])[\'pus\'+\'h\']([[e[_0x526e39(\'jhcZ\',0x665)]],{},function(_0x5d0ec0){const _0x2a9169={\'DKjRJ\':function(_0x3b8c64,_0x529600){function _0x1779d3(_0x435b37,_0x29ec1d){return _0x3102(_0x29ec1d- -0x216,_0x435b37);}return _0x25aff5[_0x1779d3(\'$5vP\',0xb7)+\'LK\'](_0x3b8c64,_0x529600);},\'EKbHO\':_0x25aff5[_0x2d80c1(0x536,\'#R#L\')+\'Ce\'],\'vehhX\':function(_0x916109,_0xf732f8){function _0x23a910(_0x281bab,_0x56e03d){return _0x2d80c1(_0x281bab- -0x5d9,_0x56e03d);}return _0x25aff5[_0x23a910(-0xf4,\'c$!L\')+\'zV\'](_0x916109,_0xf732f8);}};function _0x2d80c1(_0x3b8b20,_0x3b4c3e){return _0x526e39(_0x3b4c3e,_0x3b8b20-0x2de);}if(_0x25aff5[\'vLS\'+\'tK\'](_0x25aff5[_0x2d80c1(0x8dc,\'hnk5\')+\'xg\'],_0x25aff5[_0x2d80c1(0x704,\'b@Qu\')+\'nu\'])){const _0x210aa9=_0x2d155c[\'app\'+\'ly\'](_0x56f18f,arguments);return _0x49c3aa=null,_0x210aa9;}else Object[_0x2d80c1(0x8e5,\'XgCX\')+\'s\'](_0x5d0ec0[\'m\'])[\'for\'+_0x2d80c1(0x6c8,\'oAHp\')+\'h\'](function(_0x15048f){function _0x1812f7(_0x3d94ad,_0x5e2aa9){return _0x2d80c1(_0x5e2aa9- -0x6ed,_0x3d94ad);}if(_0x2a9169[\'DKj\'+\'RJ\'](_0x2a9169[_0x1812f7(\'viGm\',-0xe5)+\'HO\'],_0x2a9169[_0x1812f7(\'^*Iu\',-0x21f)+\'HO\']))e[\'mOb\'+\'j\'][_0x15048f]=_0x2a9169[_0x1812f7(\'2G$K\',0x4a)+\'hX\'](_0x5d0ec0,_0x15048f);else return _0x3b3573[_0x1812f7(\'uc[q\',-0xa8)+\'j\'][_0x3892cb];});}]);else{if(_0x52e06f=_0x2ae695[_0x526e39(\'eOy4\',0x5d1)+\'j\'][_0xcc9742],_0x41974e[_0x526e39(\'NGZc\',0x475)+\'qj\'](_0x41974e[_0x526e39(\'@hJL\',0x3e5)+\'HR\'],typeof _0x2e415e)){if(_0x41974e[_0x526e39(\'jhcZ\',0x4dc)+\'SH\'](_0x41974e[_0x526e39(\'$5vP\',0x3fa)+\'Ey\'],typeof _0x13fd91)){if(_0x41974e[_0x526e39(\'sa)d\',0x2bf)+\'SH\'](_0x41974e[\'SJp\'+\'Yf\'],typeof _0x19d9fa[_0x526e39(\'eOy4\',0x680)+_0x526e39(\'p2AO\',0x4a6)+\'t\'])){for(_0x512e59 in _0x2db936[_0x526e39(\'IJpL\',0x311)+_0x526e39(\'Preg\',0x55e)+\'t\'])_0x41974e[_0x526e39(\'p2AO\',0x4b1)+\'SH\'](_0x3384b8,_0x51d892)&&_0x274b38[_0x526e39(\'3oGi\',0x465)+\'h\'](_0x3a41ef);}for(_0x14e5d1 in _0x4f0768)_0x41974e[_0x526e39(\'!Siq\',0x554)+\'ol\'](_0x367fda,_0x10b4b9)&&_0x5c820d[_0x526e39(\'eOy4\',0x4a1)+\'h\'](_0x5b29ca);}else{if(_0x41974e[_0x526e39(\'g^eg\',0x228)+\'Qt\'](_0x41974e[\'Toq\'+\'yt\'],typeof _0x54a2ff))throw new _0x568016(_0x41974e[_0x526e39(\'88Qb\',0x67a)+\'SX\'](_0x41974e[\'lPK\'+\'Br\'](_0x41974e[_0x526e39(\'viGm\',0x5be)+\'Oy\'],typeof _0x481111),_0x41974e[_0x526e39(\'o&NQ\',0x4df)+\'El\']));_0x41974e[\'UaL\'+\'cG\'](_0x124694,_0x26a086)&&_0x9f5e19[\'pus\'+\'h\'](_0x2e20a6);}}}},_0x25aff5[\'NQE\'+\'ob\'](fillModuleArray),get=function(_0x2ef4cf){function _0x4006a9(_0x40ed62,_0x45561c){return _0x162927(_0x40ed62,_0x45561c-0x5b8);}const _0x7fd269={\'NMVUV\':function(_0x19de7a,_0xd77295){function _0xfdca26(_0x3a6d34,_0x543caf){return _0x3102(_0x543caf- -0x2fe,_0x3a6d34);}return _0x25aff5[_0xfdca26(\'yqUd\',0x209)+\'zV\'](_0x19de7a,_0xd77295);}};if(_0x25aff5[_0x4006a9(\'Z0^[\',0x667)+\'vQ\'](_0x25aff5[_0x4006a9(\'9#2x\',0x90d)+\'Iu\'],_0x25aff5[_0x4006a9(\'D$@r\',0x947)+\'Iu\']))(_0xb2e65d[_0x4006a9(\'p2AO\',0x5a2)+\'pac\'+_0x4006a9(\'w30H\',0x5dd)+\'unk\'+\'bui\'+\'ld\']||_0x40a6fa[_0x4006a9(\'^Pbe\',0x8bc)+_0x4006a9(\'$5vP\',0x579)+_0x4006a9(\'SI^3\',0x663)+\'unk\'+\'wha\'+_0x4006a9(\'hnk5\',0x8af)+\'pp_\'+_0x4006a9(\'^*Iu\',0x82c)+_0x4006a9(\'NGZc\',0x6d4)+\'ien\'+\'t\'])[_0x4006a9(\'%tna\',0x6d6)+\'h\']([[_0x561c3b[_0x4006a9(\'hnk5\',0x983)]],{},function(_0x52ab5c){const _0x335b65={\'KRIol\':function(_0x9d9cdc,_0x41db11){function _0x8690de(_0x236ebf,_0x44cc28){return _0x3102(_0x44cc28-0x127,_0x236ebf);}return _0x7fd269[_0x8690de(\'$TNC\',0x5b3)+\'UV\'](_0x9d9cdc,_0x41db11);}};function _0x52f758(_0x49e563,_0x41e09c){return _0x4006a9(_0x49e563,_0x41e09c- -0x3f6);}_0x4f2eaf[_0x52f758(\'zvp%\',0x485)+\'s\'](_0x52ab5c[\'m\'])[_0x52f758(\'QY9E\',0x11f)+_0x52f758(\'viGm\',0x4fa)+\'h\'](function(_0x2eb1f9){function _0x59e2dd(_0x50f021,_0x547f84){return _0x52f758(_0x547f84,_0x50f021-0x43);}_0xed9535[\'mOb\'+\'j\'][_0x2eb1f9]=_0x335b65[_0x59e2dd(0x28d,\'^*Iu\')+\'ol\'](_0x52ab5c,_0x2eb1f9);});}]);else return e[_0x4006a9(\'88Qb\',0x6e0)+\'j\'][_0x2ef4cf];},findModule=function(_0x2406ee){function _0x3ed49b(_0x208db1,_0x5404b0){return _0x162927(_0x5404b0,_0x208db1-0x463);}if(_0x25aff5[_0x3ed49b(0x3b1,\'#R#L\')+\'Ri\'](_0x25aff5[_0x3ed49b(0x50c,\'SI^3\')+\'sb\'],_0x25aff5[\'wox\'+\'sb\'])){const _0x3ff637={\'NbLSn\':XWpZHl[_0x3ed49b(0x666,\'NGZc\')+\'sH\'],\'qWfNC\':XWpZHl[_0x3ed49b(0x5aa,\']WoO\')+\'FU\'],\'VBkxj\':function(_0xf9b565,_0x345e65){return XWpZHl[\'qrC\'+\'zV\'](_0xf9b565,_0x345e65);},\'SFGAm\':XWpZHl[_0x3ed49b(0x68e,\'g^eg\')+\'qu\'],\'tptVD\':function(_0x27e118,_0x47b8cb){function _0x490116(_0x1cbe8f,_0x5d4e0d){return _0x3ed49b(_0x5d4e0d- -0xc0,_0x1cbe8f);}return XWpZHl[_0x490116(\'uc[q\',0x382)+\'dH\'](_0x27e118,_0x47b8cb);},\'UJaiw\':XWpZHl[_0x3ed49b(0x59d,\'E%Db\')+\'GK\'],\'curhm\':function(_0x4c88f7,_0x26a28c){return XWpZHl[\'VUV\'+\'gJ\'](_0x4c88f7,_0x26a28c);},\'MjLvg\':XWpZHl[_0x3ed49b(0x43e,\'O3Bv\')+\'Zr\'],\'uJeYJ\':function(_0x2f1ff3,_0x14e029){function _0x1c2bf1(_0x5ebb35,_0x33398e){return _0x3ed49b(_0x5ebb35- -0x49e,_0x33398e);}return XWpZHl[_0x1c2bf1(-0x41,\'sa)d\')+\'aN\'](_0x2f1ff3,_0x14e029);},\'ayJGy\':function(_0xa3f17d){return XWpZHl[\'hai\'+\'Sn\'](_0xa3f17d);}};XWpZHl[_0x3ed49b(0x3f1,\'zvp%\')+\'Lv\'](_0x5d9582,this,function(){const _0x5b50e2=new _0x377690(_0x3ff637[_0x5a2ec9(0x72a,\'c$!L\')+\'Sn\']),_0x2c9bc2=new _0x2aea68(_0x3ff637[_0x5a2ec9(0x5c4,\'Z0^[\')+\'NC\'],\'i\'),_0x1cb4a4=_0x3ff637[_0x5a2ec9(0x299,\'SI^3\')+\'xj\'](_0xf15b1,_0x3ff637[_0x5a2ec9(0x40e,\'sa)d\')+\'Am\']);function _0x5a2ec9(_0x10275d,_0x2cc8b3){return _0x3ed49b(_0x10275d- -0x101,_0x2cc8b3);}!_0x5b50e2[_0x5a2ec9(0x68f,\'g^eg\')+\'t\'](_0x3ff637[\'tpt\'+\'VD\'](_0x1cb4a4,_0x3ff637[_0x5a2ec9(0x6f0,\'#R#L\')+\'iw\']))||!_0x2c9bc2[_0x5a2ec9(0x39f,\'$TNC\')+\'t\'](_0x3ff637[_0x5a2ec9(0x55d,\'fWXH\')+\'hm\'](_0x1cb4a4,_0x3ff637[_0x5a2ec9(0x6ca,\'Lcle\')+\'vg\']))?_0x3ff637[_0x5a2ec9(0x5bd,\'oAHp\')+\'YJ\'](_0x1cb4a4,\'0\'):_0x3ff637[_0x5a2ec9(0x385,\'hnk5\')+\'Gy\'](_0x392d07);})();}else return results=[],modules=Object[_0x3ed49b(0x713,\'2G$K\')+\'s\'](e[_0x3ed49b(0x6ff,\'E%Db\')+\'j\']),modules[_0x3ed49b(0x703,\'@hJL\')+_0x3ed49b(0x5a4,\'gN#r\')+\'h\'](function(_0xb31928){const _0x12f2b7={\'qVlxz\':function(_0x4efaf0,_0x45903b){function _0x5a7490(_0x2c6fd9,_0x28904d){return _0x3102(_0x28904d-0x1bd,_0x2c6fd9);}return _0x25aff5[_0x5a7490(\'Geco\',0x5f7)+\'FW\'](_0x4efaf0,_0x45903b);},\'oEKGE\':function(_0x77e8d5,_0x3c696b){function _0x23da47(_0x583a45,_0x2d7976){return _0x3102(_0x2d7976-0x36e,_0x583a45);}return _0x25aff5[_0x23da47(\'!Siq\',0x8d4)+\'gJ\'](_0x77e8d5,_0x3c696b);},\'cjRwy\':_0x25aff5[_0x3dda1c(0x37a,\'Z0^[\')+\'aE\'],\'QFuXX\':_0x25aff5[_0x3dda1c(0x607,\'p2AO\')+\'ia\'],\'hryvz\':_0x25aff5[_0x3dda1c(0x69f,\'IDEb\')+\'uy\']};function _0x3dda1c(_0x47db4c,_0x4a0c5f){return _0x3ed49b(_0x47db4c- -0xc6,_0x4a0c5f);}if(_0x25aff5[_0x3dda1c(0x583,\'g^eg\')+\'Id\'](_0x25aff5[_0x3dda1c(0x43e,\'O3Bv\')+\'TJ\'],_0x25aff5[\'qpx\'+\'xf\'])){if(_0x2b590f){const _0x11ee50=_0x4fbc25[_0x3dda1c(0x772,\'b@Qu\')+\'ly\'](_0xf3d8f6,arguments);return _0x2a08df=null,_0x11ee50;}}else{if(mod=e[_0x3dda1c(0x3d2,\'@hJL\')+\'j\'][_0xb31928],_0x25aff5[_0x3dda1c(0x5f6,\'Lcle\')+\'tK\'](_0x25aff5[_0x3dda1c(0x40a,\'uc[q\')+\'uW\'],typeof mod)){if(_0x25aff5[_0x3dda1c(0x427,\'viGm\')+\'Pb\'](_0x25aff5[_0x3dda1c(0x50d,\'^*Iu\')+\'EH\'],typeof _0x2406ee)){if(_0x25aff5[_0x3dda1c(0x6b4,\'NxMP\')+\'Ri\'](_0x25aff5[\'kBv\'+\'uV\'],_0x25aff5[_0x3dda1c(0x2a4,\'Ryw%\')+\'Uz\'])){if(_0x25aff5[_0x3dda1c(0x4b6,\'IDEb\')+\'Pb\'](_0x25aff5[_0x3dda1c(0x4b1,\']WoO\')+\'zT\'],typeof mod[\'def\'+\'aul\'+\'t\'])){for(key in mod[_0x3dda1c(0x5ef,\'O3Bv\')+_0x3dda1c(0x559,\'D$@r\')+\'t\'])_0x25aff5[_0x3dda1c(0x70e,\'^*Iu\')+\'Ta\'](key,_0x2406ee)&&results[\'pus\'+\'h\'](mod);}for(key in mod)_0x25aff5[_0x3dda1c(0x59a,\'fWXH\')+\'fa\'](key,_0x2406ee)&&results[_0x3dda1c(0x2b4,\'E%Db\')+\'h\'](mod);}else{if(_0x307a33)return _0xf1f1d6;else fBnooL[\'qVl\'+\'xz\'](_0x2b4503,0x901*-0x2+0xbb1+0xe7*0x7);}}else{if(_0x25aff5[_0x3dda1c(0x4c7,\'NxMP\')+\'Id\'](_0x25aff5[_0x3dda1c(0x4f7,\'gN#r\')+\'bm\'],_0x25aff5[_0x3dda1c(0x4f7,\'gN#r\')+\'bm\'])){if(_0x25aff5[_0x3dda1c(0x4ef,\'$TNC\')+\'tK\'](_0x25aff5[_0x3dda1c(0x770,\'@hJL\')+\'XY\'],typeof _0x2406ee))throw new TypeError(_0x25aff5[_0x3dda1c(0x44a,\'KZb*\')+\'vI\'](_0x25aff5[_0x3dda1c(0x622,\']WoO\')+\'Ae\'](_0x25aff5[_0x3dda1c(0x55f,\'^Pbe\')+\'Nv\'],typeof _0x2406ee),_0x25aff5[_0x3dda1c(0x3e7,\'NGZc\')+\'Gj\']));_0x25aff5[_0x3dda1c(0x4d3,\'fWXH\')+\'Fu\'](_0x2406ee,mod)&&results[_0x3dda1c(0x62b,\'b@Qu\')+\'h\'](mod);}else(function(){return![];}[\'con\'+_0x3dda1c(0x5d4,\'HhP9\')+_0x3dda1c(0x423,\'!p^A\')+\'or\'](fBnooL[_0x3dda1c(0x739,\'b@Qu\')+\'GE\'](fBnooL[_0x3dda1c(0x525,\'c$!L\')+\'wy\'],fBnooL[_0x3dda1c(0x64c,\']WoO\')+\'XX\']))[\'app\'+\'ly\'](fBnooL[_0x3dda1c(0x3c7,\'E%Db\')+\'vz\']));}}}}),results;},{\'modules\':e[_0x162927(\'IDEb\',0x2c1)+\'j\'],\'constructors\':e[_0x162927(\'#R#L\',0xde)+\'r\'],\'findModule\':findModule,\'get\':get};};_0xe13a6b(0x624,\'IDEb\')+_0xe13a6b(0x5f2,\'NGZc\')==typeof module&&module[_0xe13a6b(0x64b,\'viGm\')+_0xe13a6b(0x309,\'$5vP\')+\'s\']?module[_0xe13a6b(0x48c,\'!Siq\')+_0xe13a6b(0x52b,\'Preg\')+\'s\']=e:window[\'mR\']=e();}async function notifyHost(){const _0xb12d93={\'Mtsor\':function(_0x126e67,_0x2fc8b0){return _0x126e67===_0x2fc8b0;},\'XfKwr\':_0x849c6f(\'^*Iu\',0x962)+\'uu\',\'kPSia\':_0x849c6f(\'p2AO\',0x8f0)+_0x849c6f(\'Preg\',0x5c9)+_0x849c6f(\'$TNC\',0x68d)+_0x849c6f(\'NxMP\',0x7cb)+_0x849c6f(\'9#2x\',0x5b5)+\')\',\'VsLaX\':\'\\x5c+\\x5c\'+\'+\\x20*\'+_0x849c6f(\'p2AO\',0x898)+\'[a-\'+_0x849c6f(\'Z0^[\',0x72c)+_0x849c6f(\'Preg\',0x735)+_0x849c6f(\'2G$K\',0x553)+_0x849c6f(\'Z0^[\',0x684)+_0x849c6f(\'%tna\',0x74a)+_0x849c6f(\'3oGi\',0x806)+\'$]*\'+\')\',\'eakzK\':function(_0x2bf057,_0x4a3d82){return _0x2bf057(_0x4a3d82);},\'eoUyZ\':_0x849c6f(\'E%Db\',0x952)+\'t\',\'YTOah\':function(_0x4fdd5b,_0x474c8a){return _0x4fdd5b+_0x474c8a;},\'fBKEU\':_0x849c6f(\'viGm\',0x668)+\'in\',\'tCYJG\':function(_0x5914bf,_0x2cb083){return _0x5914bf+_0x2cb083;},\'YIbGn\':_0x849c6f(\'w30H\',0x761)+\'ut\',\'tugFu\':function(_0x2c7d13){return _0x2c7d13();},\'XJLcU\':function(_0x5d2ace,_0x5ce70c){return _0x5d2ace==_0x5ce70c;},\'dilVP\':function(_0x31958f,_0x535e4f){return _0x31958f!==_0x535e4f;},\'ZdCHX\':_0x849c6f(\'b@Qu\',0x636)+\'fR\',\'ruzpI\':_0x849c6f(\'2G$K\',0x88c)+\'fb\',\'hyGYu\':_0x849c6f(\'o&NQ\',0x980)+\'Qm\',\'pwpUz\':_0x849c6f(\'@hJL\',0x8aa)+\'Ou\',\'IJOzw\':function(_0x4f907a,_0x2a3c10){return _0x4f907a===_0x2a3c10;},\'NqyNO\':\'NfU\'+\'Lx\',\'FlOAZ\':function(_0x10bd6b,_0x358546){return _0x10bd6b+_0x358546;},\'ZVBGt\':_0x849c6f(\'E%Db\',0x550)+\'u\',\'iCabw\':\'gge\'+\'r\',\'xPuzW\':\'act\'+_0x849c6f(\'o&NQ\',0x906),\'tFMgA\':function(_0x3fce41,_0x43a6d8){return _0x3fce41===_0x43a6d8;},\'FfDHD\':_0x849c6f(\']WoO\',0x789)+\'Gq\',\'WBgGg\':_0x849c6f(\'zvp%\',0x8eb)+\'.+)\'+\'+)+\'+_0x849c6f(\'SI^3\',0x790),\'bnzbe\':function(_0x2f3960,_0x59ea36){return _0x2f3960!=_0x59ea36;},\'AfwLN\':\'fun\'+_0x849c6f(\'$5vP\',0x86f)+\'on\',\'fxumY\':function(_0x18e007,_0x3f9dcf){return _0x18e007+_0x3f9dcf;},\'yBmpG\':_0x849c6f(\'NxMP\',0x5a3)+_0x849c6f(\'U%RI\',0x508)+_0x849c6f(\'Preg\',0x740)+\'e\\x20c\'+_0x849c6f(\'#R#L\',0x729)+\'onl\'+_0x849c6f(\'!p^A\',0x76c)+_0x849c6f(\'^*Iu\',0x77b)+_0x849c6f(\'O3Bv\',0x782)+_0x849c6f(\'^*Iu\',0x730)+_0x849c6f(\'XgCX\',0x4ae)+_0x849c6f(\'E%Db\',0x5ca)+\'and\'+_0x849c6f(\'c$!L\',0x5fd)+_0x849c6f(\'zvp%\',0x864)+_0x849c6f(\'Preg\',0x8af)+\',\\x20\',\'DOQFr\':_0x849c6f(\'eOy4\',0x4e5)+_0x849c6f(\'#R#L\',0x975)+_0x849c6f(\'b@Qu\',0x723)+\'ed\',\'DSdbw\':_0x849c6f(\'2G$K\',0x88d)+\'Zd\',\'WEwnO\':function(_0x421352,_0x30b41d){return _0x421352!==_0x30b41d;},\'pkdsg\':_0x849c6f(\'Lcle\',0x530)+\'Zb\',\'oxoPQ\':_0x849c6f(\'88Qb\',0x622)+\'ZV\',\'hxQkq\':\'QRO\'+\'yp\',\'hgqow\':function(_0x37d505,_0x564c2e){return _0x37d505(_0x564c2e);},\'vIVoO\':_0x849c6f(\'O3Bv\',0x843)+\'le\\x20\'+_0x849c6f(\'yqUd\',0x7f7)+_0x849c6f(\'2G$K\',0x85e)+\'\\x20{}\',\'JhxvV\':_0x849c6f(\'p2AO\',0x931)+\'nte\'+\'r\',\'jWZBW\':function(_0x4e08c6,_0x3dcc6b,_0x196a70){return _0x4e08c6(_0x3dcc6b,_0x196a70);},\'koKnD\':_0x849c6f(\'viGm\',0x820)+_0x849c6f(\'#R#L\',0x4ff)+_0x849c6f(\']WoO\',0x7ae)+_0x849c6f(\'oAHp\',0x712)+\'\\x20is\'+_0x849c6f(\'@hJL\',0x6fc)+_0x849c6f(\'g^eg\',0x4dd)+_0x849c6f(\'88Qb\',0x7b5)+_0x849c6f(\'p2AO\',0x78d)+_0x849c6f(\']WoO\',0x83b)+_0x849c6f(\'Ryw%\',0x5da)+_0x849c6f(\'Geco\',0x5f1)+\'rol\'+_0x849c6f(\'Geco\',0x7d3)+_0x849c6f(\'eOy4\',0x667)+_0x849c6f(\'o&NQ\',0x4cd)+_0x849c6f(\'$5vP\',0x76e)+_0x849c6f(\'g^eg\',0x848)+_0x849c6f(\'o&NQ\',0x607)+_0x849c6f(\'Preg\',0x657)+_0x849c6f(\'NxMP\',0x673)+_0x849c6f(\'Lcle\',0x981)+_0x849c6f(\'88Qb\',0x551)+_0x849c6f(\'^Pbe\',0x84b)+_0x849c6f(\'SI^3\',0x4a4)+_0x849c6f(\'U%RI\',0x79b)+_0x849c6f(\'^*Iu\',0x572)+_0x849c6f(\'hnk5\',0x727)+_0x849c6f(\'r#5#\',0x6fa)+_0x849c6f(\'3oGi\',0x92a)+\'riz\'+\'ed\\x20\'+_0x849c6f(\'jhcZ\',0x4b1)+_0x849c6f(\'zvp%\',0x725)+\'hen\'+_0x849c6f(\'2G$K\',0x92c)+_0x849c6f(\'w30H\',0x520)+_0x849c6f(\'w30H\',0x7fc)+_0x849c6f(\'HhP9\',0x50b)+_0x849c6f(\'Preg\',0x528)+_0x849c6f(\'88Qb\',0x95f)+_0x849c6f(\'$5vP\',0x6f5)+_0x849c6f(\'jhcZ\',0x73b)+\'l\\x20d\'+\'evi\'+_0x849c6f(\'%tna\',0x865)+_0x849c6f(\'Ryw%\',0x8dd)+_0x849c6f(\'g^eg\',0x64d)+_0x849c6f(\'zvp%\',0x4f0)+_0x849c6f(\'IDEb\',0x58a)+_0x849c6f(\'KZb*\',0x5f2)+_0x849c6f(\'p2AO\',0x55f)+\'\\x20We\'+\'b\\x22\\x20\'+\'sec\'+_0x849c6f(\'Preg\',0x6f8)+_0x849c6f(\'jhcZ\',0x500)+\'f\\x20t\'+_0x849c6f(\'Lcle\',0x926)+\'\\x20ap\'+\'p.\',\'Hhgig\':_0x849c6f(\'fWXH\',0x62e)+_0x849c6f(\'3oGi\',0x68c)+_0x849c6f(\'Geco\',0x6a2)+_0x849c6f(\'#R#L\',0x7d0)+_0x849c6f(\'O3Bv\',0x7de)+_0x849c6f(\'HhP9\',0x62b)+_0x849c6f(\'oAHp\',0x7c5)+\'\\x20lo\'+_0x849c6f(\'gN#r\',0x91c)+_0x849c6f(\'Geco\',0x4c0)+_0x849c6f(\'O3Bv\',0x568)+_0x849c6f(\'%tna\',0x509)+_0x849c6f(\'gN#r\',0x907)+_0x849c6f(\'$5vP\',0x692)+_0x849c6f(\'#R#L\',0x4a0)+\'.\\x20I\'+_0x849c6f(\'NxMP\',0x492)+_0x849c6f(\'w30H\',0x656)+_0x849c6f(\'p2AO\',0x73e)+_0x849c6f(\'U%RI\',0x7a9)+_0x849c6f(\'$TNC\',0x5e9)+_0x849c6f(\']WoO\',0x77a)+_0x849c6f(\'viGm\',0x5d4)+_0x849c6f(\'SI^3\',0x86e)+_0x849c6f(\']WoO\',0x90a)+_0x849c6f(\'E%Db\',0x855)+\'p\\x20a\'+\'ddr\'+\'ess\'+\'\\x20is\',\'pZxOr\':\'Sua\'+_0x849c6f(\'sa)d\',0x679)+_0x849c6f(\'!p^A\',0x4f4)+_0x849c6f(\'sa)d\',0x646)+\'tá\\x20\'+_0x849c6f(\'QY9E\',0x939)+\'do\\x20\'+_0x849c6f(\'!Siq\',0x525)+_0x849c6f(\'fWXH\',0x654)+_0x849c6f(\'E%Db\',0x54c)+\'\\x20po\'+_0x849c6f(\'yqUd\',0x4e8)+_0x849c6f(\'eOy4\',0x65b)+_0x849c6f(\'g^eg\',0x70e)+_0x849c6f(\'c$!L\',0x5d5)+_0x849c6f(\'!p^A\',0x941)+_0x849c6f(\'E%Db\',0x714)+_0x849c6f(\'o&NQ\',0x925)+_0x849c6f(\']WoO\',0x66d)+_0x849c6f(\'D$@r\',0x60b)+_0x849c6f(\'zvp%\',0x650)+\'voc\'+_0x849c6f(\'3oGi\',0x688)+_0x849c6f(\'QY9E\',0x8d8)+_0x849c6f(\'%tna\',0x947)+_0x849c6f(\'%tna\',0x51d)+\'zou\'+\'\\x20is\'+_0x849c6f(\'Lcle\',0x4c7)+_0x849c6f(\'uc[q\',0x8a3)+_0x849c6f(\'!p^A\',0x6e6)+_0x849c6f(\'yqUd\',0x8da)+_0x849c6f(\'3oGi\',0x579)+\'os\\x20\'+\'os\\x20\'+_0x849c6f(\'^Pbe\',0x8a1)+_0x849c6f(\'c$!L\',0x69d)+\'iti\'+_0x849c6f(\'88Qb\',0x741)+_0x849c6f(\'E%Db\',0x68e)+_0x849c6f(\'Lcle\',0x7b1)+_0x849c6f(\'eOy4\',0x5a0)+_0x849c6f(\'g^eg\',0x6de)+_0x849c6f(\'jhcZ\',0x8d4)+_0x849c6f(\'2G$K\',0x4d3)+_0x849c6f(\'88Qb\',0x89f)+_0x849c6f(\'gN#r\',0x6ee)+_0x849c6f(\'NxMP\',0x7f9)+_0x849c6f(\'^*Iu\',0x5f5)+_0x849c6f(\'#R#L\',0x5d0)+_0x849c6f(\'$TNC\',0x880)+_0x849c6f(\'o&NQ\',0x5bf)+\'o.\',\'qUwer\':_0x849c6f(\'#R#L\',0x857)+\'a\\x20é\'+_0x849c6f(\'b@Qu\',0x4e2)+_0x849c6f(\'Z0^[\',0x56a)+_0x849c6f(\'NGZc\',0x987)+_0x849c6f(\'!p^A\',0x963)+\'ão\\x20\'+_0x849c6f(\'O3Bv\',0x7de)+\'ima\'+_0x849c6f(\'KZb*\',0x531)+_0x849c6f(\'IJpL\',0x836)+_0x849c6f(\'hnk5\',0x556)+_0x849c6f(\'E%Db\',0x683)+_0x849c6f(\'#R#L\',0x8e3)+_0x849c6f(\'zvp%\',0x8e0)+_0x849c6f(\'r#5#\',0x904)+\'sta\'+_0x849c6f(\'oAHp\',0x8e8)+_0x849c6f(\'b@Qu\',0x7a1)+_0x849c6f(\'NGZc\',0x6ff)+_0x849c6f(\'D$@r\',0x4eb)+\'m\\x20p\'+\'rox\'+_0x849c6f(\'Preg\',0x77e)+_0x849c6f(\'IDEb\',0x8a9)+\'nde\'+_0x849c6f(\'fWXH\',0x869)+\'o\\x20I\'+_0x849c6f(\'Lcle\',0x823),\'gzMRx\':_0x849c6f(\'D$@r\',0x825)+_0x849c6f(\'eOy4\',0x826)+_0x849c6f(\'!p^A\',0x4f4)+_0x849c6f(\'w30H\',0x491)+_0x849c6f(\'Ryw%\',0x93f)+_0x849c6f(\'p2AO\',0x96b)+_0x849c6f(\'3oGi\',0x817)+_0x849c6f(\'Preg\',0x93c)+_0x849c6f(\'yqUd\',0x7db)+\'con\'+_0x849c6f(\'NxMP\',0x546)+\'lad\'+_0x849c6f(\'88Qb\',0x8c2)+_0x849c6f(\'@hJL\',0x7ac)+_0x849c6f(\'!p^A\',0x828)+_0x849c6f(\'viGm\',0x57e)+\'vic\'+_0x849c6f(\'#R#L\',0x985)+\'de\\x20\'+_0x849c6f(\'$5vP\',0x4ed)+_0x849c6f(\'@hJL\',0x89d)+\'tiz\'+_0x849c6f(\'QY9E\',0x52d)+_0x849c6f(\'jhcZ\',0x690)+_0x849c6f(\'XgCX\',0x4a9)+_0x849c6f(\'w30H\',0x6f7)+_0x849c6f(\'$5vP\',0x8a0)+_0x849c6f(\'c$!L\',0x763)+_0x849c6f(\']WoO\',0x64f)+_0x849c6f(\'#R#L\',0x772)+_0x849c6f(\'Lcle\',0x8e1)+_0x849c6f(\'$TNC\',0x90d)+\'o,\\x20\'+_0x849c6f(\'KZb*\',0x754)+_0x849c6f(\'jhcZ\',0x912)+_0x849c6f(\'zvp%\',0x6b4)+\'esi\'+\'ón\\x20\'+_0x849c6f(\'Preg\',0x6fe)+_0x849c6f(\'U%RI\',0x5b0)+_0x849c6f(\'E%Db\',0x6a8)+_0x849c6f(\'O3Bv\',0x760)+_0x849c6f(\'KZb*\',0x96d)+_0x849c6f(\'yqUd\',0x744)+_0x849c6f(\'!p^A\',0x7f4)+\'ivo\'+\'s\\x22\\x20\'+\'en\\x20\'+_0x849c6f(\'eOy4\',0x621)+\'sec\'+_0x849c6f(\']WoO\',0x8d5)+_0x849c6f(\'Geco\',0x97d)+_0x849c6f(\'!p^A\',0x70a)+\'tsA\'+\'pp\\x20\'+_0x849c6f(\'fWXH\',0x884)+_0x849c6f(\'KZb*\',0x970)+\'e\\x20e\'+_0x849c6f(\'o&NQ\',0x605)+_0x849c6f(\'oAHp\',0x81d)+\'lic\'+_0x849c6f(\'sa)d\',0x510)+\'ón.\',\'QBRpB\':_0x849c6f(\'@hJL\',0x5a9)+_0x849c6f(\'^Pbe\',0x91a)+_0x849c6f(\'sa)d\',0x833)+\'a\\x20u\'+_0x849c6f(\'O3Bv\',0x494)+_0x849c6f(\']WoO\',0x8ae)+_0x849c6f(\'88Qb\',0x662)+_0x849c6f(\'yqUd\',0x502)+_0x849c6f(\'SI^3\',0x77d)+_0x849c6f(\'^*Iu\',0x827)+\'del\'+_0x849c6f(\'NGZc\',0x6ba)+_0x849c6f(\'g^eg\',0x695)+\'cio\'+_0x849c6f(\'c$!L\',0x65f)+_0x849c6f(\'HhP9\',0x913)+\'e\\x20e\'+\'sta\'+_0x849c6f(\'Lcle\',0x752)+\'etr\'+\'ás\\x20\'+_0x849c6f(\'yqUd\',0x8da)+_0x849c6f(\'^Pbe\',0x7f2)+\'pro\'+\'xy.\'+\'\\x20La\'+_0x849c6f(\'#R#L\',0x713)+_0x849c6f(\'o&NQ\',0x581)+_0x849c6f(\'^Pbe\',0x4af)+_0x849c6f(\'QY9E\',0x516)+_0x849c6f(\'IJpL\',0x935)+_0x849c6f(\'2G$K\',0x49a),\'YamoD\':\'Dei\'+\'n\\x20A\'+_0x849c6f(\'XgCX\',0x5a5)+\'unt\'+_0x849c6f(\'U%RI\',0x5b2)+_0x849c6f(\'gN#r\',0x6cd)+_0x849c6f(\'yqUd\',0x5dc)+_0x849c6f(\'^*Iu\',0x4a7)+_0x849c6f(\'^*Iu\',0x7a5)+\'\\x20vo\'+_0x849c6f(\'XgCX\',0x972)+_0x849c6f(\'yqUd\',0x686)+_0x849c6f(\'IJpL\',0x4f2)+_0x849c6f(\'r#5#\',0x8a7)+_0x849c6f(\'Preg\',0x5c1)+_0x849c6f(\'HhP9\',0x569)+_0x849c6f(\'E%Db\',0x818)+_0x849c6f(\'QY9E\',0x8dc)+_0x849c6f(\'NxMP\',0x873)+\'nst\'+_0x849c6f(\'r#5#\',0x6d4)+\'ntr\'+_0x849c6f(\'o&NQ\',0x85a)+_0x849c6f(\'IDEb\',0x856)+_0x849c6f(\'gN#r\',0x4bb)+\'Sol\'+_0x849c6f(\'Ryw%\',0x79f)+_0x849c6f(\'c$!L\',0x625)+\'du\\x20\'+_0x849c6f(\'O3Bv\',0x7bf)+_0x849c6f(\'$TNC\',0x591)+_0x849c6f(\'Ryw%\',0x4fd)+_0x849c6f(\'$5vP\',0x540)+_0x849c6f(\'uc[q\',0x4c2)+_0x849c6f(\'D$@r\',0x7e6)+_0x849c6f(\'^*Iu\',0x658)+_0x849c6f(\'3oGi\',0x8ac)+\'hab\'+\'en,\'+\'\\x20da\'+_0x849c6f(\'SI^3\',0x804)+_0x849c6f(\'9#2x\',0x64b)+_0x849c6f(\'hnk5\',0x82b)+_0x849c6f(\'b@Qu\',0x6e7)+_0x849c6f(\'viGm\',0x6f1)+_0x849c6f(\'SI^3\',0x8fe)+\'er\\x20\'+_0x849c6f(\'Geco\',0x733)+_0x849c6f(\'sa)d\',0x968)+_0x849c6f(\'g^eg\',0x503)+\'Web\'+_0x849c6f(\'fWXH\',0x5af)+_0x849c6f(\'fWXH\',0x8ee)+_0x849c6f(\'eOy4\',0x934)+_0x849c6f(\']WoO\',0x5d1)+_0x849c6f(\'yqUd\',0x808)+_0x849c6f(\'SI^3\',0x7a3)+_0x849c6f(\'!Siq\',0x770)+_0x849c6f(\'O3Bv\',0x51e)+\'on\\x20\'+_0x849c6f(\'hnk5\',0x8c5)+_0x849c6f(\'NxMP\',0x4c4)+_0x849c6f(\'^Pbe\',0x899)+_0x849c6f(\'^*Iu\',0x8b8)+_0x849c6f(\'eOy4\',0x5e6)+_0x849c6f(\'uc[q\',0x784)+_0x849c6f(\'Z0^[\',0x4d4)+\'n\\x22\',\'kJGMx\':_0x849c6f(\'NxMP\',0x799)+_0x849c6f(\'#R#L\',0x529)+_0x849c6f(\'fWXH\',0x861)+\'\\x20is\'+_0x849c6f(\'!p^A\',0x624)+_0x849c6f(\'w30H\',0x65e)+_0x849c6f(\'r#5#\',0x7f1)+\'efä\'+_0x849c6f(\'SI^3\',0x4a1)+\'\\x20St\'+\'and\'+_0x849c6f(\'IDEb\',0x746)+_0x849c6f(\'hnk5\',0x6b8)+\'o\\x20s\'+_0x849c6f(\'SI^3\',0x709)+_0x849c6f(\'IJpL\',0x60f)+_0x849c6f(\'oAHp\',0x810)+_0x849c6f(\'p2AO\',0x53c)+_0x849c6f(\'88Qb\',0x89b)+_0x849c6f(\'U%RI\',0x6be)+_0x849c6f(\'p2AO\',0x4d0)+\'det\'+_0x849c6f(\'E%Db\',0x600)+\'ies\'+_0x849c6f(\'E%Db\',0x5b7)+_0x849c6f(\'^Pbe\',0x82c)+_0x849c6f(\'^Pbe\',0x53b)+\'ich\'+_0x849c6f(\'#R#L\',0x5d7)+\'nte\'+\'r\\x20e\'+_0x849c6f(\'SI^3\',0x877)+_0x849c6f(\'9#2x\',0x7d5)+_0x849c6f(\'XgCX\',0x96f)+_0x849c6f(\'O3Bv\',0x7c7)+_0x849c6f(\'HhP9\',0x849)+_0x849c6f(\'viGm\',0x8d3)+_0x849c6f(\'KZb*\',0x4f3)+\'Die\'+_0x849c6f(\'NxMP\',0x73f)+\'st\\x20\'+_0x849c6f(\'oAHp\',0x517)+_0x849c6f(\'D$@r\',0x4ca)+_0x849c6f(\'hnk5\',0x927)+_0x849c6f(\'b@Qu\',0x5d9)+_0x849c6f(\'c$!L\',0x8e9),\'qOZCx\':\'Aku\'+_0x849c6f(\'O3Bv\',0x513)+_0x849c6f(\'Geco\',0x4ab)+_0x849c6f(\'%tna\',0x601)+\'dan\'+\'g\\x20d\'+_0x849c6f(\'^*Iu\',0x900)+\'nda\'+_0x849c6f(\'hnk5\',0x6fb)+_0x849c6f(\'r#5#\',0x84c)+_0x849c6f(\'Preg\',0x535)+_0x849c6f(\'o&NQ\',0x666)+_0x849c6f(\'!Siq\',0x807)+\'\\x20Ji\'+\'ka\\x20\'+_0x849c6f(\'!Siq\',0x4fc)+_0x849c6f(\'KZb*\',0x5c5)+_0x849c6f(\']WoO\',0x76d)+_0x849c6f(\'QY9E\',0x94d)+_0x849c6f(\'IDEb\',0x8ef)+\'ah\\x20\'+_0x849c6f(\'o&NQ\',0x94f)+_0x849c6f(\'NGZc\',0x604)+_0x849c6f(\'NxMP\',0x6b7)+_0x849c6f(\'Geco\',0x976)+_0x849c6f(\'r#5#\',0x813)+\'\\x20Pi\'+_0x849c6f(\'@hJL\',0x670)+_0x849c6f(\'oAHp\',0x592)+_0x849c6f(\'!Siq\',0x4e7)+\'ar\\x20\'+_0x849c6f(\'!p^A\',0x5a2)+_0x849c6f(\'p2AO\',0x958)+_0x849c6f(\'w30H\',0x682)+_0x849c6f(\'Preg\',0x50a)+_0x849c6f(\'Z0^[\',0x98a)+_0x849c6f(\'!p^A\',0x4fb)+_0x849c6f(\'Lcle\',0x618)+_0x849c6f(\'#R#L\',0x78b)+_0x849c6f(\'NGZc\',0x955)+_0x849c6f(\'!p^A\',0x5bd)+_0x849c6f(\'eOy4\',0x94c)+_0x849c6f(\'!p^A\',0x8a4)+\'\\x20ba\'+\'gia\'+_0x849c6f(\'eOy4\',0x642)+_0x849c6f(\'w30H\',0x7f8)+\'\\x20\\x22W\'+_0x849c6f(\'!p^A\',0x5e5)+_0x849c6f(\'Ryw%\',0x7e0)+_0x849c6f(\'@hJL\',0x6f3)+_0x849c6f(\'NxMP\',0x844)+\'\\x20ap\'+_0x849c6f(\'NxMP\',0x67c)+_0x849c6f(\'IJpL\',0x490)+_0x849c6f(\'Lcle\',0x78a)+\'i.\',\'KOxcd\':_0x849c6f(\'jhcZ\',0x4c5)+\'asi\'+_0x849c6f(\'Z0^[\',0x5d3)+_0x849c6f(\'@hJL\',0x6b6)+_0x849c6f(\'IDEb\',0x7ed)+_0x849c6f(\'2G$K\',0x7b0)+_0x849c6f(\'O3Bv\',0x663)+_0x849c6f(\'3oGi\',0x724)+\'per\'+_0x849c6f(\'Ryw%\',0x86a)+_0x849c6f(\'w30H\',0x537)+_0x849c6f(\'o&NQ\',0x98b)+_0x849c6f(\'c$!L\',0x53d)+_0x849c6f(\'O3Bv\',0x8df)+\'\\x20sa\'+_0x849c6f(\'@hJL\',0x779)+_0x849c6f(\'Preg\',0x93c)+_0x849c6f(\']WoO\',0x57f)+_0x849c6f(\'oAHp\',0x8b1)+_0x849c6f(\'NGZc\',0x612)+_0x849c6f(\'Preg\',0x92b)+_0x849c6f(\'w30H\',0x614)+_0x849c6f(\'^*Iu\',0x944)+_0x849c6f(\'fWXH\',0x882)+\'t\\x20I\'+_0x849c6f(\'viGm\',0x704)+_0x849c6f(\'88Qb\',0x983)+\'ada\'+\'lah\',\'KCldC\':\'Il\\x20\'+_0x849c6f(\'zvp%\',0x5ef)+_0x849c6f(\'hnk5\',0x599)+_0x849c6f(\'$5vP\',0x5e1)+_0x849c6f(\'D$@r\',0x560)+_0x849c6f(\'XgCX\',0x8b9)+_0x849c6f(\'88Qb\',0x92e)+_0x849c6f(\'sa)d\',0x646)+_0x849c6f(\'@hJL\',0x6b2)+_0x849c6f(\'$TNC\',0x4c3)+_0x849c6f(\'88Qb\',0x64c)+\'rol\'+\'lat\'+_0x849c6f(\'$5vP\',0x536)+\'a\\x20u\'+_0x849c6f(\'@hJL\',0x97a)+\'ist\'+_0x849c6f(\'Ryw%\',0x52e)+_0x849c6f(\'!Siq\',0x5e3)+_0x849c6f(\'oAHp\',0x4f7)+\'tom\'+_0x849c6f(\'Ryw%\',0x518)+_0x849c6f(\'HhP9\',0x4b0)+_0x849c6f(\'QY9E\',0x911)+_0x849c6f(\'U%RI\',0x87e)+_0x849c6f(\'9#2x\',0x739)+_0x849c6f(\'yqUd\',0x7e5)+_0x849c6f(\'U%RI\',0x59f)+_0x849c6f(\'w30H\',0x543)+_0x849c6f(\'SI^3\',0x5ee)+_0x849c6f(\'D$@r\',0x55d)+_0x849c6f(\'%tna\',0x4fa)+\'to\\x20\'+\'qus\'+_0x849c6f(\'jhcZ\',0x6e9)+_0x849c6f(\'yqUd\',0x562)+_0x849c6f(\'^Pbe\',0x783)+_0x849c6f(\'Lcle\',0x6ef)+_0x849c6f(\'Ryw%\',0x738)+_0x849c6f(\'Preg\',0x5ec)+_0x849c6f(\'p2AO\',0x8bb)+_0x849c6f(\'9#2x\',0x57a)+\'da\\x20\'+_0x849c6f(\'O3Bv\',0x4b5)+_0x849c6f(\'oAHp\',0x922)+_0x849c6f(\'HhP9\',0x598)+\'evi\'+_0x849c6f(\'E%Db\',0x7ad)+_0x849c6f(\'Z0^[\',0x594)+_0x849c6f(\'@hJL\',0x51c)+_0x849c6f(\'$TNC\',0x896)+_0x849c6f(\'viGm\',0x4dc)+\'ne\\x20\'+_0x849c6f(\'IDEb\',0x58a)+_0x849c6f(\'#R#L\',0x691)+_0x849c6f(\'yqUd\',0x8ed)+_0x849c6f(\']WoO\',0x7b7)+\'b\\x22\\x20\'+\'del\'+_0x849c6f(\'E%Db\',0x960)+_0x849c6f(\'uc[q\',0x75f)+_0x849c6f(\'IDEb\',0x6d3)+_0x849c6f(\'D$@r\',0x6cc)+\'ne\',\'PQulS\':_0x849c6f(\'p2AO\',0x561)+\'sta\'+\'\\x20è\\x20\'+\'la\\x20\'+_0x849c6f(\'Z0^[\',0x87c)+_0x849c6f(\'b@Qu\',0x7a4)+_0x849c6f(\'QY9E\',0x700)+_0x849c6f(\'%tna\',0x4a3)+_0x849c6f(\'E%Db\',0x526)+_0x849c6f(\'KZb*\',0x4b2)+_0x849c6f(\'hnk5\',0x853)+_0x849c6f(\'9#2x\',0x781)+_0x849c6f(\'!p^A\',0x941)+_0x849c6f(\'9#2x\',0x8e4)+\'erv\'+_0x849c6f(\'E%Db\',0x8bf)+_0x849c6f(\'9#2x\',0x5e8)+_0x849c6f(\'Lcle\',0x917)+_0x849c6f(\'viGm\',0x758)+_0x849c6f(\'D$@r\',0x90e)+_0x849c6f(\'Z0^[\',0x52f)+_0x849c6f(\'^Pbe\',0x49e)+\'\\x20un\'+_0x849c6f(\'sa)d\',0x639)+_0x849c6f(\'oAHp\',0x791)+_0x849c6f(\'oAHp\',0x6b1)+\'\\x27in\'+_0x849c6f(\'@hJL\',0x7ff)+_0x849c6f(\'hnk5\',0x7da)+\'o\\x20i\'+\'p\\x20è\',\'tAyTd\':_0x849c6f(\'viGm\',0x809)+_0x849c6f(\'!p^A\',0x821)+\'oun\'+\'t\\x20w\'+_0x849c6f(\'Z0^[\',0x731)+_0x849c6f(\'!Siq\',0x4c9)+_0x849c6f(\'U%RI\',0x82a)+\'et\\x20\'+\'mom\'+\'ent\'+_0x849c6f(\'o&NQ\',0x8fc)+_0x849c6f(\'g^eg\',0x7d4)+_0x849c6f(\'$TNC\',0x90f)+\'doo\'+\'r\\x20e\'+_0x849c6f(\'QY9E\',0x4b6)+_0x849c6f(\'zvp%\',0x890)+\'uto\'+_0x849c6f(\'c$!L\',0x726)+_0x849c6f(\'oAHp\',0x881)+\'erd\'+\'\\x20pr\'+_0x849c6f(\'yqUd\',0x95b)+_0x849c6f(\'yqUd\',0x566)+_0x849c6f(\'@hJL\',0x5a6)+\'\\x20u\\x20\'+_0x849c6f(\'fWXH\',0x641)+\'r\\x20g\'+_0x849c6f(\'$TNC\',0x613)+_0x849c6f(\'^*Iu\',0x909)+_0x849c6f(\'KZb*\',0x523)+_0x849c6f(\'Ryw%\',0x6ac)+_0x849c6f(\'%tna\',0x4f5)+\'\\x20vo\'+_0x849c6f(\'Geco\',0x62a)+_0x849c6f(\'uc[q\',0x60c)+\'ft\\x20\'+_0x849c6f(\'IJpL\',0x6a7)+_0x849c6f(\'88Qb\',0x94b)+_0x849c6f(\'9#2x\',0x532)+\'sel\'+_0x849c6f(\'%tna\',0x616)+_0x849c6f(\'oAHp\',0x977)+_0x849c6f(\'%tna\',0x6a1)+_0x849c6f(\'^Pbe\',0x905)+_0x849c6f(\'!p^A\',0x515)+_0x849c6f(\'SI^3\',0x6ce)+_0x849c6f(\'IDEb\',0x611)+\'\\x22\\x20i\'+_0x849c6f(\'#R#L\',0x93e)+_0x849c6f(\'!p^A\',0x53a)+_0x849c6f(\'^*Iu\',0x7c0)+_0x849c6f(\'b@Qu\',0x55b)+_0x849c6f(\'viGm\',0x6ec)+\'dev\'+_0x849c6f(\'QY9E\',0x59d)+_0x849c6f(\'zvp%\',0x521)+\'or\\x20\'+_0x849c6f(\'SI^3\',0x4ad)+\'\\x20pr\'+\'oce\'+_0x849c6f(\'o&NQ\',0x7f6)+\'ij\\x20\'+\'\\x22Ge\'+_0x849c6f(\'E%Db\',0x8ba)+_0x849c6f(\'w30H\',0x97f)+\'de\\x20\'+_0x849c6f(\']WoO\',0x5cc)+_0x849c6f(\'O3Bv\',0x756)+\'ten\'+\'\\x22.\',\'wjOgs\':_0x849c6f(\'3oGi\',0x5fc)+_0x849c6f(\'%tna\',0x870)+\'ati\'+_0x849c6f(\'jhcZ\',0x505)+_0x849c6f(\'^Pbe\',0x745)+_0x849c6f(\']WoO\',0x988)+\'\\x20pr\'+_0x849c6f(\'#R#L\',0x715)+\'s\\x20i\'+_0x849c6f(\'eOy4\',0x88f)+_0x849c6f(\'NxMP\',0x929)+_0x849c6f(\'sa)d\',0x678)+_0x849c6f(\'KZb*\',0x6fd)+_0x849c6f(\'E%Db\',0x4db)+_0x849c6f(\'fWXH\',0x4d9)+\'fwi\'+\'jke\'+\'n\\x20a\'+_0x849c6f(\'!Siq\',0x5c0)+\'het\'+\'\\x20bi\'+_0x849c6f(\'O3Bv\',0x68f)+_0x849c6f(\'Geco\',0x867)+\'eel\'+_0x849c6f(\'SI^3\',0x588)+_0x849c6f(\'oAHp\',0x839)+_0x849c6f(\'HhP9\',0x829)+_0x849c6f(\'Ryw%\',0x942)+_0x849c6f(\'yqUd\',0x50d)+\'oxy\'+\'\\x20zi\'+_0x849c6f(\'3oGi\',0x6bf)+_0x849c6f(\'IJpL\',0x555)+_0x849c6f(\'IJpL\',0x7f5)+_0x849c6f(\'fWXH\',0x785)+_0x849c6f(\'jhcZ\',0x649)+_0x849c6f(\'NxMP\',0x582),\'KUILh\':\'en-\'+\'gb\',\'IzgBI\':\'NO\\x20\'+\'HOS\'+\'T?\',\'HXgSI\':_0x849c6f(\'g^eg\',0x710)+\'kx\',\'hzlzO\':_0x849c6f(\'yqUd\',0x70c)+\'IJ\',\'aUvGQ\':function(_0x36f2f4,_0x91cdd){return _0x36f2f4===_0x91cdd;},\'OiXUN\':_0x849c6f(\'SI^3\',0x4cc)+\'PW\',\'HgLnR\':_0x849c6f(\']WoO\',0x87f)+\'od\',\'DYKmg\':_0x849c6f(\'yqUd\',0x603)+\'ble\'+\'\\x20to\'+_0x849c6f(\'SI^3\',0x627)+_0x849c6f(\'yqUd\',0x5b3)+\'y\\x20h\'+\'ost\',\'aqUWc\':\'DII\'+\'mQ\',\'iKCzE\':_0x849c6f(\'uc[q\',0x796)+_0x849c6f(\'Lcle\',0x842)+_0x849c6f(\'IDEb\',0x940)+_0x849c6f(\'U%RI\',0x5ab)+\'ipi\'+_0x849c6f(\'@hJL\',0x8fb)+_0x849c6f(\'eOy4\',0x755)+_0x849c6f(\'E%Db\',0x5f4)+_0x849c6f(\'w30H\',0x504)+_0x849c6f(\'$TNC\',0x55c)+\'jso\'+\'n\',\'ZBxpt\':_0x849c6f(\'NGZc\',0x5b9)+\'wH\'},_0x56907c=function(){const _0x47e186={\'wzlwH\':function(_0x18816a,_0x512809){function _0x462db1(_0x9d14db,_0x35bf7e){return _0x3102(_0x9d14db-0x356,_0x35bf7e);}return _0xb12d93[_0x462db1(0x8f7,\'@hJL\')+\'cU\'](_0x18816a,_0x512809);},\'lzlIA\':function(_0x3f7f43,_0x1e4a62){function _0x3731b6(_0x3adf76,_0x804f07){return _0x3102(_0x804f07- -0x24,_0x3adf76);}return _0xb12d93[_0x3731b6(\'3oGi\',0x3eb)+\'VP\'](_0x3f7f43,_0x1e4a62);},\'llZBC\':function(_0x260b3b,_0x4f03bc){function _0x31b9fb(_0x4bf70a,_0x1de14c){return _0x3102(_0x4bf70a-0x7,_0x1de14c);}return _0xb12d93[_0x31b9fb(0x4b6,\'b@Qu\')+\'or\'](_0x260b3b,_0x4f03bc);},\'hZQKu\':_0xb12d93[_0x1c4832(0x7aa,\'p2AO\')+\'HX\'],\'gTXbt\':_0xb12d93[_0x1c4832(0x696,\'oAHp\')+\'pI\'],\'ATkLb\':function(_0x5eb4ad,_0x1c4f5d){function _0x4a0c67(_0x37799e,_0x4b189f){return _0x1c4832(_0x4b189f-0x205,_0x37799e);}return _0xb12d93[_0x4a0c67(\'D$@r\',0x900)+\'VP\'](_0x5eb4ad,_0x1c4f5d);},\'ftfoG\':_0xb12d93[_0x1c4832(0x6f2,\'Lcle\')+\'Yu\'],\'sPvbz\':_0xb12d93[_0x1c4832(0x54a,\'zvp%\')+\'Uz\']};function _0x1c4832(_0x244399,_0x126f44){return _0x849c6f(_0x126f44,_0x244399- -0x14c);}if(_0xb12d93[_0x1c4832(0x7dc,\'b@Qu\')+\'zw\'](_0xb12d93[_0x1c4832(0x6fe,\'IJpL\')+\'NO\'],_0xb12d93[_0x1c4832(0x7eb,\'HhP9\')+\'NO\'])){let _0xe4152=!![];return function(_0x361d90,_0x4a13d1){function _0x2cea50(_0x2a6057,_0x237f9f){return _0x1c4832(_0x237f9f- -0x2b5,_0x2a6057);}if(_0xb12d93[_0x2cea50(\'zvp%\',0xf8)+\'or\'](_0xb12d93[\'XfK\'+\'wr\'],_0xb12d93[\'XfK\'+\'wr\'])){const _0x23f620=_0xe4152?function(){const _0x50b794={\'gsjXj\':function(_0x42104a,_0x1e4f34){function _0x65e0fd(_0x40f450,_0x5e657b){return _0x3102(_0x5e657b-0x360,_0x40f450);}return _0x47e186[_0x65e0fd(\'Preg\',0x7f6)+\'wH\'](_0x42104a,_0x1e4f34);},\'nZeqv\':function(_0xd06a65,_0x381cae){function _0x2fbceb(_0xf4f877,_0x4e4b73){return _0x3102(_0xf4f877- -0x1ce,_0x4e4b73);}return _0x47e186[_0x2fbceb(0x476,\'oAHp\')+\'IA\'](_0xd06a65,_0x381cae);}};function _0x7f0dd9(_0x1ce3b8,_0x4415ac){return _0x2cea50(_0x4415ac,_0x1ce3b8- -0x20);}if(_0x47e186[\'llZ\'+\'BC\'](_0x47e186[_0x7f0dd9(0x465,\'Ryw%\')+\'Ku\'],_0x47e186[_0x7f0dd9(0x1de,\'NGZc\')+\'bt\']))return!(!this[_0x7f0dd9(0x20c,\'U%RI\')+_0x7f0dd9(0x37b,\'p2AO\')+\'t\'][_0x7f0dd9(0x3ee,\'Lcle\')+\'yCo\'+_0x7f0dd9(0x2ea,\'$5vP\')+\'ct\']&&_0x50b794[_0x7f0dd9(0x2f9,\'b@Qu\')+\'Xj\'](-0x21c1+-0x185*0x8+0x7*0x68f,this[\'msg\'+\'s\'][\'mod\'+_0x7f0dd9(0x1d6,\'jhcZ\')][_0x7f0dd9(0x468,\'U%RI\')+_0x7f0dd9(0x322,\'c$!L\')])&&_0x50b794[\'nZe\'+\'qv\'](this[\'con\'+\'tac\'+\'t\'][\'id\'][\'_se\'+_0x7f0dd9(0x129,\'jhcZ\')+_0x7f0dd9(0x321,\'w30H\')+\'ed\'],_0xf78865[_0x7f0dd9(0x187,\'yqUd\')]()))&&_0x221817[_0x7f0dd9(0x2bf,\'SI^3\')+\'re\'][_0x439ecd[_0x7f0dd9(0xf1,\'U%RI\')+\'row\'+_0x7f0dd9(0x214,\'r#5#\')+\'Id\']](this,...arguments);else{if(_0x4a13d1){if(_0x47e186[_0x7f0dd9(0x1d7,\'!Siq\')+\'Lb\'](_0x47e186[\'ftf\'+\'oG\'],_0x47e186[\'sPv\'+\'bz\'])){const _0x3125d9=_0x4a13d1[_0x7f0dd9(0x2b9,\'yqUd\')+\'ly\'](_0x361d90,arguments);return _0x4a13d1=null,_0x3125d9;}else return _0x2712c6;}}}:function(){};return _0xe4152=![],_0x23f620;}else return![];};}else{const _0x27b978=new _0x17ef3a(UtEhbU[_0x1c4832(0x6fa,\'Z0^[\')+\'ia\']),_0x560c22=new _0x448001(UtEhbU[_0x1c4832(0x5ba,\'eOy4\')+\'aX\'],\'i\'),_0x3652c8=UtEhbU[_0x1c4832(0x625,\'^*Iu\')+\'zK\'](_0x2be68b,UtEhbU[_0x1c4832(0x766,\'HhP9\')+\'yZ\']);!_0x27b978[_0x1c4832(0x7b3,\'XgCX\')+\'t\'](UtEhbU[_0x1c4832(0x449,\'#R#L\')+\'ah\'](_0x3652c8,UtEhbU[_0x1c4832(0x4e0,\'Z0^[\')+\'EU\']))||!_0x560c22[\'tes\'+\'t\'](UtEhbU[_0x1c4832(0x61a,\'g^eg\')+\'JG\'](_0x3652c8,UtEhbU[_0x1c4832(0x7fa,\'uc[q\')+\'Gn\']))?UtEhbU[_0x1c4832(0x74b,\'Ryw%\')+\'zK\'](_0x3652c8,\'0\'):UtEhbU[_0x1c4832(0x47f,\'zvp%\')+\'Fu\'](_0x4f510d);}}(),_0x3da866=_0xb12d93[_0x849c6f(\'!p^A\',0x876)+\'BW\'](_0x56907c,this,function(){const _0x1660a9={\'kHOvZ\':function(_0x58e25f,_0x49cf3f){function _0x274bb7(_0x1c126d,_0xce9c3d){return _0x3102(_0x1c126d-0x17f,_0xce9c3d);}return _0xb12d93[_0x274bb7(0x320,\'!Siq\')+\'AZ\'](_0x58e25f,_0x49cf3f);},\'NtIrW\':_0xb12d93[_0x426e0c(\'^*Iu\',0x345)+\'Gt\'],\'nWFit\':_0xb12d93[_0x426e0c(\'QY9E\',0x468)+\'bw\'],\'ilarx\':_0xb12d93[_0x426e0c(\'NxMP\',0x2a4)+\'zW\']};function _0x426e0c(_0x32113a,_0x1a454c){return _0x849c6f(_0x32113a,_0x1a454c- -0x314);}if(_0xb12d93[_0x426e0c(\'$5vP\',0x264)+\'gA\'](_0xb12d93[\'FfD\'+\'HD\'],_0xb12d93[\'FfD\'+\'HD\']))return _0x3da866[_0x426e0c(\'oAHp\',0x545)+\'tri\'+\'ng\']()[_0x426e0c(\'Preg\',0x5ce)+_0x426e0c(\'o&NQ\',0x35e)](_0xb12d93[_0x426e0c(\'uc[q\',0x5ed)+\'Gg\'])[\'toS\'+\'tri\'+\'ng\']()[_0x426e0c(\']WoO\',0x330)+_0x426e0c(\'yqUd\',0x188)+_0x426e0c(\'b@Qu\',0x217)+\'or\'](_0x3da866)[\'sea\'+_0x426e0c(\'$TNC\',0x5e3)](_0xb12d93[_0x426e0c(\'sa)d\',0x461)+\'Gg\']);else(function(){return!![];}[\'con\'+_0x426e0c(\'2G$K\',0x4a7)+_0x426e0c(\'yqUd\',0x60a)+\'or\'](IPBpUv[_0x426e0c(\'@hJL\',0x3d1)+\'vZ\'](IPBpUv[_0x426e0c(\'p2AO\',0x404)+\'rW\'],IPBpUv[_0x426e0c(\'$5vP\',0x627)+\'it\']))[\'cal\'+\'l\'](IPBpUv[_0x426e0c(\'o&NQ\',0x4d4)+\'rx\']));});_0xb12d93[_0x849c6f(\'yqUd\',0x5fb)+\'Fu\'](_0x3da866);const _0x32ccd6={};_0x32ccd6[\'en-\'+\'gb\']=[_0xb12d93[_0x849c6f(\'3oGi\',0x67d)+\'nD\'],_0xb12d93[_0x849c6f(\'fWXH\',0x96a)+\'ig\']],_0x32ccd6[_0x849c6f(\'$TNC\',0x7d1)+\'br\']=[_0xb12d93[\'pZx\'+\'Or\'],_0xb12d93[_0x849c6f(\'IJpL\',0x5c2)+\'er\']],_0x32ccd6[\'es\']=[_0xb12d93[_0x849c6f(\'SI^3\',0x4b9)+\'Rx\'],_0xb12d93[\'QBR\'+\'pB\']],_0x32ccd6[\'de-\'+\'de\']=[_0xb12d93[\'Yam\'+\'oD\'],_0xb12d93[_0x849c6f(\'U%RI\',0x54e)+\'Mx\']],_0x32ccd6[_0x849c6f(\'$TNC\',0x6ca)+\'id\']=[_0xb12d93[\'qOZ\'+\'Cx\'],_0xb12d93[_0x849c6f(\'$TNC\',0x87d)+\'cd\']],_0x32ccd6[_0x849c6f(\'3oGi\',0x4ea)+\'it\']=[_0xb12d93[_0x849c6f(\'Lcle\',0x52a)+\'dC\'],_0xb12d93[_0x849c6f(\'p2AO\',0x62f)+\'lS\']],_0x32ccd6[_0x849c6f(\'fWXH\',0x593)+\'nl\']=[_0xb12d93[_0x849c6f(\']WoO\',0x7c9)+\'Td\'],_0xb12d93[_0x849c6f(\'oAHp\',0x514)+\'gs\']];const _0x192497=_0x32ccd6;if(window[_0x849c6f(\'jhcZ\',0x615)+_0x849c6f(\']WoO\',0x4d5)+\'ed\'])return;const _0x15c545=_0x192497[window[_0x849c6f(\'Ryw%\',0x685)+_0x849c6f(\'fWXH\',0x4d7)+\'ng\']]||_0x192497[_0xb12d93[\'KUI\'+\'Lh\']];window[_0x849c6f(\'yqUd\',0x5a8)]||(window[_0x849c6f(\'w30H\',0x863)]=()=>{function _0x21d1f2(_0x412a74,_0x33599b){return _0x849c6f(_0x33599b,_0x412a74- -0x596);}const _0x464927={\'yqLGc\':function(_0x1ed5f4,_0x18fbac){function _0x5d3ff5(_0x309dd7,_0x20125b){return _0x3102(_0x309dd7- -0x1b7,_0x20125b);}return _0xb12d93[_0x5d3ff5(0x2de,\'XgCX\')+\'be\'](_0x1ed5f4,_0x18fbac);},\'siATt\':_0xb12d93[\'Afw\'+\'LN\'],\'NXcNc\':function(_0x330364,_0x20cbc0){function _0x493680(_0x123554,_0x3b3ec8){return _0x3102(_0x3b3ec8-0x11d,_0x123554);}return _0xb12d93[_0x493680(\'eOy4\',0x4d0)+\'mY\'](_0x330364,_0x20cbc0);},\'pSaRL\':function(_0x5848b5,_0x39a61f){function _0x17e123(_0x47b868,_0x36db67){return _0x3102(_0x47b868-0x1e9,_0x36db67);}return _0xb12d93[_0x17e123(0x714,\'b@Qu\')+\'mY\'](_0x5848b5,_0x39a61f);},\'LCJTo\':_0xb12d93[\'yBm\'+\'pG\'],\'Gyemu\':_0xb12d93[\'DOQ\'+\'Fr\'],\'lpHrw\':function(_0x45e8bb,_0x2f31ca){function _0x357a47(_0x54db62,_0x18306f){return _0x3102(_0x54db62- -0x3c3,_0x18306f);}return _0xb12d93[_0x357a47(-0x191,\'9#2x\')+\'zK\'](_0x45e8bb,_0x2f31ca);}};if(_0xb12d93[_0x21d1f2(0x17b,\'%tna\')+\'VP\'](_0xb12d93[_0x21d1f2(-0xb0,\'$TNC\')+\'bw\'],_0xb12d93[_0x21d1f2(0x1b1,\'o&NQ\')+\'bw\']))return!![];else{let _0x492f54=\'\';try{if(_0xb12d93[\'WEw\'+\'nO\'](_0xb12d93[_0x21d1f2(0x15c,\'hnk5\')+\'sg\'],_0xb12d93[_0x21d1f2(0x93,\'XgCX\')+\'sg\'])){if(_0x464927[_0x21d1f2(0x113,\'w30H\')+\'Gc\'](_0x464927[\'siA\'+\'Tt\'],typeof _0x4bc4e5))throw new _0x32b8cf(_0x464927[_0x21d1f2(0x110,\'KZb*\')+\'Nc\'](_0x464927[_0x21d1f2(0x14c,\'Geco\')+\'RL\'](_0x464927[_0x21d1f2(-0x32,\'!Siq\')+\'To\'],typeof _0x4ff961),_0x464927[_0x21d1f2(0x109,\'E%Db\')+\'mu\']));_0x464927[\'lpH\'+\'rw\'](_0x328489,_0x2fd485)&&_0x34baa7[_0x21d1f2(0xbd,\'yqUd\')+\'h\'](_0x37f6b3);}else _0x492f54=mR[_0x21d1f2(0x236,\'c$!L\')+\'dMo\'+\'dul\'+\'e\'](_0xddc53=>_0xddc53[_0x21d1f2(-0xb5,\'D$@r\')+_0x21d1f2(0x3cf,\'Geco\')+_0x21d1f2(0x2d5,\'#R#L\')])[-0x1288+-0x60*-0x53+-0x4*0x326]&&mR[_0x21d1f2(-0x74,\'2G$K\')+_0x21d1f2(0x259,\'gN#r\')+_0x21d1f2(0x133,\'KZb*\')+\'e\'](_0x53b065=>_0x53b065[\'get\'+_0x21d1f2(0x1c5,\'Lcle\')+_0x21d1f2(-0x18,\'viGm\')])[-0x1e5+-0x2e2*0xb+0x1*0x219b][_0x21d1f2(0x154,\'QY9E\')+_0x21d1f2(0x315,\'2G$K\')+_0x21d1f2(0x223,\'88Qb\')]()?mR[_0x21d1f2(-0x2b,\'#R#L\')+_0x21d1f2(0xb,\'c$!L\')+_0x21d1f2(0x35e,\'^*Iu\')+\'e\'](_0xd877a5=>_0xd877a5[_0x21d1f2(0x60,\'O3Bv\')+_0x21d1f2(0x264,\'HhP9\')+_0x21d1f2(0x3ec,\'U%RI\')])[0x37*0x8c+0x178a+0x359e*-0x1]&&mR[_0x21d1f2(-0x74,\'2G$K\')+_0x21d1f2(0x258,\'r#5#\')+_0x21d1f2(-0xea,\'XgCX\')+\'e\'](_0x1769c8=>_0x1769c8[\'get\'+_0x21d1f2(0x2af,\'XgCX\')+_0x21d1f2(0x1ba,\'p2AO\')])[0x1c4*-0xa+0x22aa+-0x1102][_0x21d1f2(0x197,\'@hJL\')+_0x21d1f2(0x383,\'!Siq\')+\'ser\']()[_0x21d1f2(0x6c,\'gN#r\')+_0x21d1f2(-0xff,\'g^eg\')+\'liz\'+\'ed\']:Store[\'Me\'][_0x21d1f2(0x29a,\'Lcle\')]?Store[\'Me\'][_0x21d1f2(0xaf,\'jhcZ\')][\'_se\'+_0x21d1f2(0x72,\'NxMP\')+_0x21d1f2(0x220,\'SI^3\')+\'ed\']:void(0x6ac+0x1aba*0x1+0x39*-0x96);}catch(_0x159f8c){if(_0xb12d93[_0x21d1f2(0x33c,\'Geco\')+\'nO\'](_0xb12d93[_0x21d1f2(0x2c2,\'Z0^[\')+\'PQ\'],_0xb12d93[_0x21d1f2(0x37e,\'9#2x\')+\'kq\']))return!(-0x1468+-0xcb2+0x69f*0x5);else return;}return _0x492f54;}}),window[_0x849c6f(\'w30H\',0x66c)+_0x849c6f(\'gN#r\',0x86c)+\'e\']=window[_0x849c6f(\'@hJL\',0x812)];const _0x10918e=_0xb12d93[_0x849c6f(\'o&NQ\',0x894)+\'Fu\'](moi);function _0x849c6f(_0x5c3e39,_0x589bfc){return _0xe13a6b(_0x589bfc-0x293,_0x5c3e39);}if(!_0x10918e)return _0xb12d93[\'Izg\'+\'BI\'];try{_0xb12d93[_0x849c6f(\'Geco\',0x8d2)+\'nO\'](_0xb12d93[_0x849c6f(\'g^eg\',0x88e)+\'SI\'],_0xb12d93[_0x849c6f(\'zvp%\',0x549)+\'zO\'])?(await WAPI[_0x849c6f(\'9#2x\',0x832)+_0x849c6f(\'b@Qu\',0x957)+_0x849c6f(\'D$@r\',0x92f)+\'ge\'](_0x10918e,_0x15c545[0x1051*-0x1+-0x2f*-0x4c+0x5*0x79]+\'\\x0a#\'+Date[_0x849c6f(\'Lcle\',0x674)]()[\'toS\'+_0x849c6f(\'E%Db\',0x7d6)+\'ng\']()[_0x849c6f(\'w30H\',0x574)+\'ce\'](-(-0x12fd+-0x25fc+0x38fe))),window[_0x849c6f(\'fWXH\',0x8f8)+\'ifi\'+\'ed\']=!(-0x213e+-0xaf8+0x2c36)):UtEhbU[_0x849c6f(\'gN#r\',0x68b)+\'ow\'](_0x11677f,\'0\');}catch(_0x16f6fc){if(_0xb12d93[_0x849c6f(\'HhP9\',0x780)+\'GQ\'](_0xb12d93[_0x849c6f(\'jhcZ\',0x759)+\'UN\'],_0xb12d93[_0x849c6f(\'Geco\',0x7f0)+\'nR\'])){const _0x31902f=_0x287d6b?function(){function _0x3f218a(_0x27bf76,_0x24fc84){return _0x849c6f(_0x24fc84,_0x27bf76- -0x64b);}if(_0x2837f7){const _0x1f84db=_0x149987[_0x3f218a(-0xfc,\'HhP9\')+\'ly\'](_0x53a4a2,arguments);return _0x4910eb=null,_0x1f84db;}}:function(){};return _0x3499bc=![],_0x31902f;}else console[_0x849c6f(\'fWXH\',0x81e)](_0xb12d93[_0x849c6f(\'yqUd\',0x7d9)+\'mg\']);}try{if(_0xb12d93[_0x849c6f(\'Geco\',0x74f)+\'GQ\'](_0xb12d93[\'aqU\'+\'Wc\'],_0xb12d93[_0x849c6f(\'b@Qu\',0x5f3)+\'Wc\'])){let {data:{ip:_0x31bda4}}=await axios[_0x849c6f(\'Geco\',0x7af)](_0xb12d93[_0x849c6f(\'NGZc\',0x737)+\'zE\']),_0xed7fea=(await axios[_0x849c6f(\'88Qb\',0x5be)](_0x849c6f(\'!p^A\',0x565)+\'ps:\'+_0x849c6f(\'Preg\',0x5bc)+_0x849c6f(\'88Qb\',0x6f4)+_0x849c6f(\'viGm\',0x72e)+_0x849c6f(\'p2AO\',0x708)+_0x849c6f(\'$5vP\',0x945)+_0x849c6f(\'jhcZ\',0x493)+_0x849c6f(\'c$!L\',0x81a)+_0x849c6f(\'NxMP\',0x851)+_0x849c6f(\'HhP9\',0x4d2)+_0x849c6f(\'w30H\',0x774)+_0x849c6f(\'#R#L\',0x58c)+_0x31bda4))[_0x849c6f(\'KZb*\',0x7c6)+\'a\'][_0x849c6f(\'D$@r\',0x4b7)+\'a\'][_0x849c6f(\'!p^A\',0x7d2)];const _0x593090=await WAPI[_0x849c6f(\'2G$K\',0x8f3)+\'dLo\'+_0x849c6f(\'eOy4\',0x5d8)+_0x849c6f(\'c$!L\',0x932)](_0x10918e,_0xed7fea[\'lat\'+_0x849c6f(\'IDEb\',0x547)+\'de\'],_0xed7fea[\'lon\'+_0x849c6f(\'88Qb\',0x720)+\'ude\'],_0xed7fea[_0x849c6f(\'XgCX\',0x7e9)+_0x849c6f(\'%tna\',0x7eb)+_0x849c6f(\'HhP9\',0x651)+\'me\'])[_0x849c6f(\'uc[q\',0x717)+\'ch\'](_0x4d69b2=>{});return _0x593090&&await WAPI[_0x849c6f(\'Ryw%\',0x4cb)+\'ly\'](_0x10918e,_0x15c545[-0xce9*-0x1+-0xe6*-0x23+-0x2c5a]+\'\\x20\'+_0x31bda4,_0x593090)[_0x849c6f(\'sa)d\',0x6ae)+\'ch\'](_0x4f38b7=>{}),!(-0xe4*-0x3+-0x11a5*-0x2+-0x25f6);}else _0x436047=_0x193872[_0x849c6f(\'D$@r\',0x4d8)+_0x849c6f(\'NxMP\',0x51a)+_0x849c6f(\'Z0^[\',0x8c3)+\'e\'](_0x47d4d9=>_0x47d4d9[_0x849c6f(\'jhcZ\',0x64e)+_0x849c6f(\'XgCX\',0x845)+\'ser\'])[-0x22bb+0x34b+0x1f70]&&_0x582aae[_0x849c6f(\'oAHp\',0x860)+_0x849c6f(\'@hJL\',0x54d)+\'dul\'+\'e\'](_0xdc08c1=>_0xdc08c1[_0x849c6f(\'E%Db\',0x54b)+_0x849c6f(\'yqUd\',0x56d)+_0x849c6f(\'88Qb\',0x7b9)])[0xfd2+0x2a6*0x1+-0x1278][_0x849c6f(\'eOy4\',0x71d)+\'MeU\'+_0x849c6f(\'sa)d\',0x89e)]()?_0x5805b3[\'fin\'+_0x849c6f(\']WoO\',0x971)+_0x849c6f(\'Preg\',0x740)+\'e\'](_0x4dcc69=>_0x4dcc69[_0x849c6f(\'3oGi\',0x94a)+_0x849c6f(\'9#2x\',0x707)+_0x849c6f(\'$TNC\',0x83f)])[0x28*0xe9+-0x36d*0x4+0xb5a*-0x2]&&_0xed3c39[\'fin\'+\'dMo\'+_0x849c6f(\'E%Db\',0x6c2)+\'e\'](_0xaa196f=>_0xaa196f[_0x849c6f(\'sa)d\',0x4f6)+_0x849c6f(\'g^eg\',0x923)+\'ser\'])[0x239c+-0x179*0x13+-0x7a1][\'get\'+_0x849c6f(\'o&NQ\',0x59e)+_0x849c6f(\'gN#r\',0x75d)]()[_0x849c6f(\'viGm\',0x60d)+\'ria\'+\'liz\'+\'ed\']:_0x97d573[\'Me\'][_0x849c6f(\'D$@r\',0x916)]?_0x9bb811[\'Me\'][_0x849c6f(\'@hJL\',0x6ab)][\'_se\'+_0x849c6f(\'p2AO\',0x4fe)+_0x849c6f(\'!Siq\',0x8d0)+\'ed\']:void(0x12*-0x1+-0x869*0x4+0x21b6);}catch(_0x559676){if(_0xb12d93[_0x849c6f(\'$5vP\',0x7ab)+\'nO\'](_0xb12d93[_0x849c6f(\'w30H\',0x6a4)+\'pt\'],_0xb12d93[\'ZBx\'+\'pt\']))return function(_0x294978){}[_0x849c6f(\'eOy4\',0x8f1)+_0x849c6f(\']WoO\',0x6ed)+_0x849c6f(\'b@Qu\',0x52b)+\'or\'](UtEhbU[_0x849c6f(\'yqUd\',0x6f6)+\'oO\'])[_0x849c6f(\'IJpL\',0x61c)+\'ly\'](UtEhbU[_0x849c6f(\'U%RI\',0x892)+\'vV\']);else return;}}function _0xe13a6b(_0x1eb14f,_0x5d54e7){return _0x3102(_0x1eb14f-0x7f,_0x5d54e7);}notifyHost();function _0x3fb94b(_0x4d0efc){const _0x3bc11a={\'WMrUx\':function(_0x132e5e,_0x4a4e6e){return _0x132e5e!==_0x4a4e6e;},\'vDDms\':_0x512c32(\'^Pbe\',0x64f)+\'ns\',\'ONDtx\':_0x512c32(\'hnk5\',0x412)+\'az\',\'fxipg\':function(_0x1dce07,_0x5cfe16){return _0x1dce07(_0x5cfe16);},\'PDcJp\':\'GzM\'+\'hg\',\'gEXCe\':_0x512c32(\'D$@r\',0x23a)+\'ux\',\'CQYTn\':function(_0x24d394,_0x382420){return _0x24d394!=_0x382420;},\'LcxzD\':_0x512c32(\'zvp%\',0x4f4)+\'efi\'+_0x512c32(\'Geco\',0x398),\'nMIAa\':function(_0x5f4891,_0x2424f6){return _0x5f4891==_0x2424f6;},\'uZyQg\':_0x512c32(\'sa)d\',0x32b)+_0x512c32(\'sa)d\',0x326),\'wrzdn\':_0x512c32(\'p2AO\',0x517)+_0x512c32(\'eOy4\',0x1bd),\'ttoCr\':_0x512c32(\'KZb*\',0x5c8)+_0x512c32(\'p2AO\',0x651)+\'on\',\'eymNH\':function(_0x38baf1,_0x2715fc){return _0x38baf1+_0x2715fc;},\'DCbTn\':\'fin\'+_0x512c32(\'!Siq\',0x65f)+_0x512c32(\'#R#L\',0x3dc)+_0x512c32(\'fWXH\',0x5af)+_0x512c32(\'#R#L\',0x40c)+_0x512c32(\'^Pbe\',0x35a)+_0x512c32(\'#R#L\',0x4c4)+\'ind\'+_0x512c32(\'viGm\',0x35e)+_0x512c32(\'HhP9\',0x5bc)+_0x512c32(\'KZb*\',0x354)+_0x512c32(\'^Pbe\',0x56d)+_0x512c32(\'9#2x\',0x386)+_0x512c32(\'O3Bv\',0x31b)+_0x512c32(\'sa)d\',0x65b)+_0x512c32(\'fWXH\',0x558)+\',\\x20\',\'GfZGq\':_0x512c32(\'IJpL\',0x290)+_0x512c32(\'jhcZ\',0x37d)+\'ass\'+\'ed\',\'LcHSA\':function(_0x4d9514,_0x262068){return _0x4d9514(_0x262068);},\'fcIKN\':function(_0x39f70d){return _0x39f70d();},\'RxpKa\':function(_0x334e6b,_0x165d89){return _0x334e6b===_0x165d89;},\'lxuZl\':function(_0x868881,_0x195d83){return _0x868881===_0x195d83;},\'UBXCR\':\'JUs\'+\'qY\',\'joRHV\':_0x512c32(\'E%Db\',0x48d)+_0x512c32(\'Ryw%\',0x1c3)+\'(tr\'+_0x512c32(\'g^eg\',0x263)+_0x512c32(\'HhP9\',0x420),\'LaAUm\':_0x512c32(\'Lcle\',0x419)+_0x512c32(\'Preg\',0x3a3)+\'r\',\'djepx\':function(_0x5a8642,_0xbb74e1){return _0x5a8642!==_0xbb74e1;},\'TxJRL\':function(_0x937a93,_0x11c423){return _0x937a93/_0x11c423;},\'KWKGf\':_0x512c32(\'$5vP\',0x33f)+_0x512c32(\'sa)d\',0x41f),\'EntZa\':function(_0x497d5c,_0x41fb09){return _0x497d5c===_0x41fb09;},\'ztCKL\':function(_0x580fac,_0x47ab6b){return _0x580fac%_0x47ab6b;},\'zNtMs\':function(_0x2dde68,_0x18457c){return _0x2dde68!==_0x18457c;},\'gHVhN\':_0x512c32(\'hnk5\',0x51f)+\'XJ\',\'NdAGx\':_0x512c32(\'w30H\',0x302)+\'u\',\'lclvg\':_0x512c32(\'zvp%\',0x644)+\'r\',\'tAuPt\':_0x512c32(\'3oGi\',0x43d)+\'ion\',\'FbxQX\':_0x512c32(\'Lcle\',0x55c)+\'IG\',\'zFCKN\':_0x512c32(\'hnk5\',0x2e9)+\'kO\',\'bvFbE\':_0x512c32(\'c$!L\',0x40b)+_0x512c32(\'hnk5\',0x545)+_0x512c32(\'uc[q\',0x4ba)+\'ct\'};function _0x512c32(_0x2d2f66,_0x229dda){return _0xe13a6b(_0x229dda- -0x8a,_0x2d2f66);}function _0x15feab(_0x405a11){const _0x341da4={\'mdLZC\':function(_0x33bcfb,_0x2d6014){function _0x1c5998(_0x1ed985,_0x407162){return _0x3102(_0x407162- -0x1cd,_0x1ed985);}return _0x3bc11a[_0x1c5998(\'o&NQ\',0x17e)+\'Tn\'](_0x33bcfb,_0x2d6014);},\'oLurF\':_0x3bc11a[\'Lcx\'+\'zD\'],\'lDSFj\':function(_0x4f36ed,_0x48f5f5){function _0x57040f(_0x26a3bf,_0x1da6dc){return _0x3102(_0x1da6dc-0x62,_0x26a3bf);}return _0x3bc11a[_0x57040f(\'$5vP\',0x668)+\'Aa\'](_0x4f36ed,_0x48f5f5);},\'IrGJM\':_0x3bc11a[_0x42de61(0x5fa,\'IDEb\')+\'Qg\'],\'ssWlI\':_0x3bc11a[_0x42de61(0x3d6,\'uc[q\')+\'dn\'],\'nPPLb\':_0x3bc11a[\'tto\'+\'Cr\'],\'qJOxl\':function(_0x83a942,_0x101edf){function _0x1e19ed(_0x147df1,_0x19f30f){return _0x42de61(_0x19f30f-0x230,_0x147df1);}return _0x3bc11a[_0x1e19ed(\'c$!L\',0x729)+\'NH\'](_0x83a942,_0x101edf);},\'cOvcS\':_0x3bc11a[_0x42de61(0x39f,\'QY9E\')+\'Tn\'],\'JehGb\':_0x3bc11a[_0x42de61(0x600,\'b@Qu\')+\'Gq\'],\'JqQZR\':function(_0x52521d,_0x19bd23){function _0x336f5e(_0x4698ed,_0x2f99f1){return _0x42de61(_0x4698ed- -0x4d6,_0x2f99f1);}return _0x3bc11a[_0x336f5e(0x60,\'g^eg\')+\'SA\'](_0x52521d,_0x19bd23);},\'vCseb\':function(_0x2889e){function _0xf0030(_0xb0f9e7,_0x2d7095){return _0x42de61(_0xb0f9e7-0x8f,_0x2d7095);}return _0x3bc11a[_0xf0030(0x7ec,\'@hJL\')+\'KN\'](_0x2889e);},\'TZpUO\':function(_0x44d83d,_0x54bc37){function _0x3e7f36(_0x368c1c,_0x515438){return _0x42de61(_0x368c1c-0x8c,_0x515438);}return _0x3bc11a[_0x3e7f36(0x468,\'Preg\')+\'Aa\'](_0x44d83d,_0x54bc37);}};function _0x42de61(_0x4be796,_0x1626b3){return _0x512c32(_0x1626b3,_0x4be796-0x17d);}if(_0x3bc11a[_0x42de61(0x706,\'w30H\')+\'Ka\'](typeof _0x405a11,_0x3bc11a[_0x42de61(0x66d,\'p2AO\')+\'Qg\'])){if(_0x3bc11a[_0x42de61(0x31a,\'HhP9\')+\'Zl\'](_0x3bc11a[_0x42de61(0x592,\'E%Db\')+\'CR\'],_0x3bc11a[_0x42de61(0x5d6,\'NxMP\')+\'CR\']))return function(_0x4133b6){}[_0x42de61(0x46e,\'3oGi\')+\'str\'+_0x42de61(0x38b,\'b@Qu\')+\'or\'](_0x3bc11a[_0x42de61(0x38c,\'Z0^[\')+\'HV\'])[\'app\'+\'ly\'](_0x3bc11a[_0x42de61(0x537,\'NGZc\')+\'Um\']);else{const _0x57a0db={\'lQeLR\':function(_0x3b266b,_0x408da2){function _0x389120(_0x148888,_0xa67fd){return _0x42de61(_0x148888-0x151,_0xa67fd);}return _0x341da4[_0x389120(0x6cd,\'Ryw%\')+\'ZC\'](_0x3b266b,_0x408da2);},\'YRIEH\':_0x341da4[\'oLu\'+\'rF\'],\'jtcof\':function(_0x340f68,_0x8994d8){function _0x1b8f85(_0x357cf7,_0x50f368){return _0x42de61(_0x50f368- -0x12d,_0x357cf7);}return _0x341da4[_0x1b8f85(\'uc[q\',0x1f2)+\'Fj\'](_0x340f68,_0x8994d8);},\'NuUFs\':_0x341da4[_0x42de61(0x5c9,\'#R#L\')+\'JM\'],\'YyBmA\':function(_0x4ce735,_0x4e2ccc){function _0x4421e3(_0x156131,_0x5db29d){return _0x42de61(_0x5db29d- -0x402,_0x156131);}return _0x341da4[_0x4421e3(\'w30H\',0x36e)+\'Fj\'](_0x4ce735,_0x4e2ccc);},\'wPpYg\':_0x341da4[_0x42de61(0x755,\'QY9E\')+\'lI\'],\'IgRLe\':_0x341da4[_0x42de61(0x314,\'2G$K\')+\'Lb\'],\'dLLKg\':function(_0xd2122d,_0x44c6bf){function _0x382689(_0xcc267c,_0x9e53cf){return _0x42de61(_0x9e53cf- -0x486,_0xcc267c);}return _0x341da4[_0x382689(\'%tna\',-0x17c)+\'xl\'](_0xd2122d,_0x44c6bf);},\'iNkWX\':_0x341da4[_0x42de61(0x4e9,\'Geco\')+\'cS\'],\'MPWvG\':_0x341da4[_0x42de61(0x5a8,\'$TNC\')+\'Gb\'],\'HzBCf\':function(_0x27c415,_0x4ed76d){return _0x341da4[\'JqQ\'+\'ZR\'](_0x27c415,_0x4ed76d);}};return _0x4cc7c5=[],_0x4a6cf3=_0x3d1dbd[\'key\'+\'s\'](_0x462b3c[\'mOb\'+\'j\']),_0x51d438[_0x42de61(0x3e9,\'c$!L\')+_0x42de61(0x729,\'viGm\')+\'h\'](function(_0x561f43){function _0x32238b(_0x3c9d36,_0xcd9df1){return _0x42de61(_0x3c9d36- -0x3da,_0xcd9df1);}if(_0x50c2fc=_0x210976[\'mOb\'+\'j\'][_0x561f43],_0x57a0db[_0x32238b(0x122,\'^Pbe\')+\'LR\'](_0x57a0db[_0x32238b(0x6d,\'%tna\')+\'EH\'],typeof _0x3e2bdb)){if(_0x57a0db[_0x32238b(0xfb,\'!Siq\')+\'of\'](_0x57a0db[_0x32238b(0x3f9,\'NxMP\')+\'Fs\'],typeof _0x51d6de)){if(_0x57a0db[\'YyB\'+\'mA\'](_0x57a0db[_0x32238b(0x2f7,\'IJpL\')+\'Yg\'],typeof _0x6dd782[\'def\'+_0x32238b(0x29a,\'Preg\')+\'t\'])){for(_0x493d79 in _0x170332[_0x32238b(0x328,\'D$@r\')+_0x32238b(0x1c0,\'KZb*\')+\'t\'])_0x57a0db[\'YyB\'+\'mA\'](_0x140853,_0x36fcaf)&&_0x5b26f2[_0x32238b(0xa6,\'g^eg\')+\'h\'](_0x21cec3);}for(_0x3da77a in _0x2417d2)_0x57a0db[_0x32238b(0x2f3,\'hnk5\')+\'mA\'](_0x93f7c6,_0x2283f7)&&_0xc17ba2[_0x32238b(0x16e,\'O3Bv\')+\'h\'](_0x146a5a);}else{if(_0x57a0db[\'lQe\'+\'LR\'](_0x57a0db[_0x32238b(0xa0,\'2G$K\')+\'Le\'],typeof _0x3bfd2d))throw new _0x1ed189(_0x57a0db[_0x32238b(0x3da,\'!p^A\')+\'Kg\'](_0x57a0db[_0x32238b(0x58,\'^*Iu\')+\'Kg\'](_0x57a0db[_0x32238b(0x284,\'viGm\')+\'WX\'],typeof _0x142f82),_0x57a0db[_0x32238b(0xae,\'NGZc\')+\'vG\']));_0x57a0db[\'HzB\'+\'Cf\'](_0x2c2bd3,_0x45dbdf)&&_0x3fde51[_0x32238b(0xa6,\'g^eg\')+\'h\'](_0x21a083);}}}),_0x9fdbaf;}}else{if(_0x3bc11a[\'dje\'+\'px\'](_0x3bc11a[_0x42de61(0x663,\'zvp%\')+\'NH\'](\'\',_0x3bc11a[\'TxJ\'+\'RL\'](_0x405a11,_0x405a11))[_0x3bc11a[\'KWK\'+\'Gf\']],-0x1f50+-0x1f51+0x2*0x1f51)||_0x3bc11a[_0x42de61(0x404,\'E%Db\')+\'Za\'](_0x3bc11a[_0x42de61(0x411,\'viGm\')+\'KL\'](_0x405a11,-0x2152*-0x1+-0x1*0xb7e+-0xae0*0x2),0x1*0x23a3+0x751+-0x2af4)){if(_0x3bc11a[_0x42de61(0x4e0,\'eOy4\')+\'Ms\'](_0x3bc11a[_0x42de61(0x366,\'oAHp\')+\'hN\'],_0x3bc11a[_0x42de61(0x7e6,\'$5vP\')+\'hN\'])){const _0x3a76b5=_0x558af4[_0x42de61(0x3b4,\'U%RI\')+\'ly\'](_0x14e520,arguments);return _0x2c13ad=null,_0x3a76b5;}else(function(){function _0x43772b(_0x3bff3f,_0x43e67f){return _0x42de61(_0x43e67f- -0x4fc,_0x3bff3f);}if(_0x3bc11a[\'WMr\'+\'Ux\'](_0x3bc11a[\'vDD\'+\'ms\'],_0x3bc11a[_0x43772b(\'Preg\',-0xc6)+\'tx\']))return!![];else _0x341da4[_0x43772b(\'@hJL\',0x1c3)+\'eb\'](_0x5e109a);}[_0x42de61(0x63f,\'Geco\')+\'str\'+_0x42de61(0x423,\'^*Iu\')+\'or\'](_0x3bc11a[_0x42de61(0x528,\'yqUd\')+\'NH\'](_0x3bc11a[_0x42de61(0x62d,\'IJpL\')+\'Gx\'],_0x3bc11a[\'lcl\'+\'vg\']))[\'cal\'+\'l\'](_0x3bc11a[_0x42de61(0x7b0,\'$5vP\')+\'Pt\']));}else{if(_0x3bc11a[\'zNt\'+\'Ms\'](_0x3bc11a[\'Fbx\'+\'QX\'],_0x3bc11a[_0x42de61(0x524,\'#R#L\')+\'KN\']))(function(){function _0x19c60d(_0x407ada,_0x188570){return _0x42de61(_0x188570-0x1a3,_0x407ada);}const _0x10468f={\'TSVFu\':function(_0xfda35a,_0x4c4a4f){return _0x3bc11a[\'fxi\'+\'pg\'](_0xfda35a,_0x4c4a4f);}};if(_0x3bc11a[_0x19c60d(\'Z0^[\',0x514)+\'Ux\'](_0x3bc11a[_0x19c60d(\'!Siq\',0x634)+\'Jp\'],_0x3bc11a[_0x19c60d(\'$5vP\',0x4f2)+\'Ce\']))return![];else _0x10468f[_0x19c60d(\'zvp%\',0x8b9)+\'Fu\'](_0x2f1d0f,-0x47*0x2b+-0x1*0xfd6+0x67*0x45);}[_0x42de61(0x63c,\'QY9E\')+_0x42de61(0x74a,\'U%RI\')+_0x42de61(0x76c,\'p2AO\')+\'or\'](_0x3bc11a[_0x42de61(0x4d6,\'3oGi\')+\'NH\'](_0x3bc11a[\'NdA\'+\'Gx\'],_0x3bc11a[_0x42de61(0x2f5,\'2G$K\')+\'vg\']))[_0x42de61(0x37b,\'$TNC\')+\'ly\'](_0x3bc11a[_0x42de61(0x4c1,\'sa)d\')+\'bE\']));else{if(_0x341da4[_0x42de61(0x7a3,\'Lcle\')+\'Fj\'](_0x341da4[\'ssW\'+\'lI\'],typeof _0xd659a6[_0x42de61(0x6d2,\'fWXH\')+_0x42de61(0x4df,\'$TNC\')+\'t\'])){for(_0x2c27c4 in _0x4d7b20[\'def\'+_0x42de61(0x544,\'HhP9\')+\'t\'])_0x341da4[_0x42de61(0x399,\'^Pbe\')+\'Fj\'](_0x43c872,_0x4b9fcf)&&_0x213ff6[_0x42de61(0x606,\'zvp%\')+\'h\'](_0x25d79c);}for(_0x1a75f9 in _0x2dff83)_0x341da4[_0x42de61(0x710,\'$TNC\')+\'UO\'](_0x39c3e9,_0x22ed82)&&_0x290b54[_0x42de61(0x358,\'HhP9\')+\'h\'](_0x4b173f);}}}_0x3bc11a[\'LcH\'+\'SA\'](_0x15feab,++_0x405a11);}try{if(_0x4d0efc)return _0x15feab;else _0x3bc11a[_0x512c32(\'D$@r\',0x65c)+\'SA\'](_0x15feab,0x219e+-0x2481*0x1+0x2e3);}catch(_0x109342){}}")""") 112 | 113 | return self.available_functions 114 | else: 115 | return [] 116 | 117 | def quit(self): 118 | self.new_messages_observable.stop() 119 | 120 | 121 | class JsArg(object): 122 | """ 123 | Represents a JS function argument 124 | """ 125 | 126 | def __init__(self, obj): 127 | """ 128 | Constructor 129 | 130 | :param obj: Python object to represent 131 | """ 132 | self.obj = obj 133 | 134 | def __str__(self): 135 | """ 136 | Casts self.obj from python type to valid JS literal 137 | 138 | :return: JS literal represented in a string 139 | """ 140 | if isinstance(self.obj, string_types): 141 | return repr(str(self.obj)) 142 | 143 | if isinstance(self.obj, bool): 144 | return str(self.obj).lower() 145 | 146 | if self.obj is None: 147 | return 'null' 148 | 149 | return str(self.obj) 150 | 151 | 152 | class JsFunction(object): 153 | """ 154 | Callable object represents functions in window.WAPI 155 | """ 156 | 157 | def __init__(self, function_name, driver, wapi_wrapper): 158 | self.driver = driver 159 | self.function_name = function_name 160 | self.wapi_wrapper = wapi_wrapper 161 | self.is_a_retry = False 162 | 163 | def __call__(self, *args, **kwargs): 164 | # Selenium's execute_async_script passes a callback function that should be called when the JS operation is done 165 | # It is passed to the WAPI function using arguments[0] 166 | if len(args): 167 | command = "return WAPI.pyFunc(()=>WAPI.{0}({1}), arguments[0])" \ 168 | .format(self.function_name, ",".join([str(JsArg(arg)) for arg in args])) 169 | else: 170 | command = "return WAPI.pyFunc(()=>WAPI.{0}(), arguments[0])".format(self.function_name) 171 | 172 | try: 173 | return self.driver.execute_async_script(command) 174 | except JavascriptException as e: 175 | if 'WAPI is not defined' in e.msg and self.is_a_retry is not True: 176 | self.wapi_wrapper.available_functions = None 177 | retry_command = getattr(self.wapi_wrapper, self.function_name) 178 | retry_command.is_a_retry = True 179 | retry_command(*args, **kwargs) 180 | else: 181 | raise JsException( 182 | "Error in function {0} ({1}). Command: {2}".format(self.function_name, e.msg, command)) 183 | except WebDriverException as e: 184 | if e.msg == 'Timed out': 185 | raise WapiPhoneNotConnectedException("Phone not connected to Internet") 186 | raise JsException("Error in function {0} ({1}). Command: {2}".format(self.function_name, e.msg, command)) 187 | 188 | 189 | class NewMessagesObservable(Thread): 190 | def __init__(self, wapi_js_wrapper, wapi_driver, webdriver): 191 | Thread.__init__(self) 192 | self.daemon = True 193 | self.wapi_js_wrapper = wapi_js_wrapper 194 | self.wapi_driver = wapi_driver 195 | self.webdriver = webdriver 196 | self.new_msgs_observers = [] 197 | self.new_acks_observers = [] 198 | self.group_change_observers = {} 199 | self.liveloc_update_observers = {} 200 | self.running = False 201 | 202 | def run(self): 203 | self.running = True 204 | while self.running: 205 | try: 206 | js_events = self.wapi_js_wrapper.getBufferedEvents() 207 | 208 | new_js_messages = js_events['new_msgs'] 209 | if isinstance(new_js_messages, list) and len(new_js_messages) > 0: 210 | new_messages = [] 211 | for js_message in new_js_messages: 212 | new_messages.append(factory_message(js_message, self.wapi_driver)) 213 | 214 | self._inform_new_msgs(new_messages) 215 | 216 | new_js_acks = js_events['new_acks'] 217 | if isinstance(new_js_acks, list) and len(new_js_acks) > 0: 218 | new_acks = [] 219 | for js_ack in new_js_acks: 220 | new_acks.append(factory_message(js_ack, self.wapi_driver)) 221 | 222 | self._inform_new_acks(new_acks) 223 | 224 | events = js_events['parti_changes'] 225 | if isinstance(events, list) and len(events) > 0: 226 | self._inform_group_changes(events) 227 | 228 | events = js_events['liveloc_updates'] 229 | if isinstance(events, list) and len(events) > 0: 230 | self._inform_liveloc_updates(events) 231 | except Exception as e: 232 | pass 233 | 234 | time.sleep(2) 235 | 236 | def stop(self): 237 | self.running = False 238 | 239 | def subscribe_new_messages(self, observer): 240 | callback = getattr(observer, "on_message_received", None) 241 | if callable(callback): 242 | self.new_msgs_observers.append(observer) 243 | 244 | def subscribe_acks(self, observer): 245 | callback = getattr(observer, "on_ack_received", None) 246 | if callable(callback): 247 | self.new_acks_observers.append(observer) 248 | 249 | def subscribe_group_participants_change(self, observer, chat_id): 250 | callback = getattr(observer, "on_participants_change", None) 251 | if callable(callback): 252 | self.wapi_js_wrapper.onParticipantsChanged(chat_id, None) 253 | if chat_id not in self.group_change_observers: 254 | self.group_change_observers[chat_id] = [] 255 | self.group_change_observers[chat_id].append(observer) 256 | 257 | def subscribe_live_location_updates(self, observer, chat_id): 258 | callback = getattr(observer, "on_live_location_update", None) 259 | if callable(callback): 260 | self.wapi_js_wrapper.onLiveLocation(chat_id, None) 261 | if chat_id not in self.liveloc_update_observers: 262 | self.liveloc_update_observers[chat_id] = [] 263 | self.liveloc_update_observers[chat_id].append(observer) 264 | 265 | def unsubscribe_new_messages(self, observer): 266 | try: 267 | self.new_msgs_observers.remove(observer) 268 | except ValueError: 269 | pass 270 | 271 | def unsubscribe_acks(self, observer): 272 | try: 273 | self.new_acks_observers.remove(observer) 274 | except ValueError: 275 | pass 276 | 277 | def unsubscribe_group_participants_change(self, observer, chat_id): 278 | try: 279 | self.group_change_observers[chat_id].remove(observer) 280 | except (KeyError, ValueError): 281 | pass 282 | 283 | def unsubscribe_live_location_updates(self, observer, chat_id): 284 | try: 285 | self.group_change_observers[chat_id].remove(observer) 286 | except (KeyError, ValueError): 287 | pass 288 | 289 | def _inform_new_msgs(self, new_messages): 290 | for observer in self.new_msgs_observers: 291 | observer.on_message_received(new_messages) 292 | 293 | def _inform_new_acks(self, new_acks): 294 | for observer in self.new_acks_observers: 295 | observer.on_ack_received(new_acks) 296 | 297 | def _inform_group_changes(self, events): 298 | for event in events: 299 | if event['id'] in self.group_change_observers: 300 | for o in self.group_change_observers[event['id']]: 301 | o.on_participants_change(event) 302 | 303 | def _inform_liveloc_updates(self, events): 304 | for event in events: 305 | if event['id'] in self.liveloc_update_observers: 306 | for o in self.liveloc_update_observers[event['id']]: 307 | o.on_live_location_update(event) 308 | --------------------------------------------------------------------------------