├── .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 | 
11 |

12 | []()
13 | []()
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 |
--------------------------------------------------------------------------------