[
  {
    "path": ".circleci/config.yml",
    "content": "# Python CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-python/ for more details\n#\nversion: 2\njobs:\n  build:\n    docker:\n      - image: circleci/python:3.7-node-browsers-legacy\n\n    steps:\n      - checkout\n\n      # Download and cache dependencies\n      - restore_cache:\n          keys:\n          - v1-dependencies-{{ checksum \"requirements.txt\" }}\n          # fallback to using the latest cache if no exact match is found\n          - v1-dependencies-\n\n      - run:\n          name: install dependencies\n          command: |\n            python3 -m venv venv\n            . venv/bin/activate\n            pip install --upgrade pip\n            pip install -r requirements.txt\n\n      - save_cache:\n          paths:\n            - ./venv\n          key: v1-dependencies-{{ checksum \"requirements.txt\" }}\n\n      - run:\n           name: run tests\n           command: |\n             . venv/bin/activate\n             python3 -m unittest discover -s tests\n\n#          name: run linting and metrics\n#          command: |\n#            . venv/bin/activate\n#            flake8 . --count --exit-zero --max-complexity=10 --max-line-length=500 --statistics --ignore=C901,E251,E722,E231,E902 --exclude=.git,.venv,.gitignore\n\n      - store_artifacts:\n          path: test-reports\n          destination: test-reports\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n# repo:     rpotter12/whatsapp-play\n# filename: FUNDING.YML\n\ngithub: [rpotter12] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: [\"https://paypal.me/rpotter12\"] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/IssueTemplate/BugReportTemplate.md",
    "content": "---\r\nname: Bug report\r\nabout: Create a report to help us improve\r\ntitle: ''\r\nlabels: ''\r\nassignees: ''\r\n\r\n---\r\n\r\n**Describe the bug**\r\nA clear and concise description of what the bug is.\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. Go to \r\n2. Click on \r\n3. Scroll down to \r\n4. See error\r\n\r\n**Expected behavior**\r\nA clear and concise description of what you expected to happen.\r\n\r\n**Screenshots**\r\nIf applicable, add screenshots to help explain your problem.\r\n\r\n**Desktop (please complete the following information):**\r\n- OS:\r\n- Browser\r\n- Version \r\n\r\n**Additional context**\r\nAdd any other context about the problem here."
  },
  {
    "path": ".github/IssueTemplate/FeatureRequestTemplate.md",
    "content": "---\r\nname: Feature request\r\nabout: Suggest an idea for this project\r\ntitle: ''\r\nlabels: ''\r\nassignees: ''\r\n\r\n---\r\n\r\n**Is your feature request related to a problem? Please describe.**\r\nA clear and concise description of what the problem is.\r\n\r\n**Describe the solution you'd like**\r\nA clear and concise description of what you want to happen.\r\n\r\n**Describe alternatives you've considered**\r\nA clear and concise description of any alternative solutions or features you've considered.\r\n\r\n**Additional context**\r\nAdd any other context or screenshots about the feature request here."
  },
  {
    "path": ".github/Pull_Request_Template.md",
    "content": "## Issue that this pull request solves\n\nCloses: # (issue number)\n\n## Proposed changes\n\nBrief description of what is fixed or changed\n\n## Types of changes\n\n_Put an `x` in the boxes that apply_\n\n- [ ] Bugfix (non-breaking change which fixes an issue)\n- [ ] New feature (non-breaking change which adds functionality)\n- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)\n- [ ] Documentation update (Documentation content changed)\n- [ ] Other (please describe): \n\n## Checklist\n\n_Put an `x` in the boxes that apply_\n\n- [ ] My code follows the style guidelines of this project\n- [ ] I have performed a self-review of my own code\n- [ ] I have commented my code, particularly in hard-to-understand areas\n- [ ] I have made corresponding changes to the documentation\n- [ ] My changes generate no new warnings\n\n## Screenshots\n\nPlease attach the screenshots of the changes made in case of change in user interface\n\n## Other information\n\nAny other information that is important to this pull request\n"
  },
  {
    "path": ".github/workflows/pylint.yml",
    "content": "name: Pylint\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python 3.8\n      uses: actions/setup-python@v1\n      with:\n        python-version: 3.8\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install pylint\n    - name: Analysing the code with pylint\n      run: |\n        # pylint `ls -R|grep .py$|xargs`\n        pylint wplay/about_changer.py\n        pylint wplay/broadcast_message.py\n        pylint wplay/chat_intermediator.py\n        pylint wplay/chatbot.py\n        pylint wplay/download_media.py\n        pylint wplay/get_media.py\n        pylint wplay/get_news.py\n        pylint wplay/message_blast.py\n        pylint wplay/message_service.py\n        pylint wplay/message_timer.py\n        pylint wplay/online_tracker.py\n        pylint wplay/profile_download.py\n        pylint wplay/save_chat.py\n        pylint wplay/schedule_message.py\n        pylint wplay/target_info.py\n        pylint wplay/telegram_bot.py\n        pylint wplay/terminal_chat.py\n        pylint wplay/text_to_speech.py\n"
  },
  {
    "path": ".github/workflows/python-app.yml",
    "content": "# This workflow will install Python dependencies, run tests and lint with a single version of Python\n# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions\n\nname: Python application\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python 3.9\n      uses: actions/setup-python@v2\n      with:\n        python-version: 3.9\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install flake8 pytest\n        pip install wplay\n        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi\n    - name: Lint with flake8\n      run: |\n        # stop the build if there are Python syntax errors or undefined names\n        # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics\n        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide\n        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics\n    - name: Test with pytest\n      run: |\n        pytest\n"
  },
  {
    "path": ".gitignore",
    "content": "# Personal Data\n.userData\ntracking_data\ndata\ntelegram_token.pkl\n\n# Byte-compiled / optimized / DLL files\nwplay.egg-info/\n__pycache__/\n*.pyc\n__pycache__/\nsrc/\\.DS_Store\nsrc/__pycache__/\nsrc/logs/\nsrc/test/__pycache__/\n\\.DS_Store\n\\.idea/\nsrc/XDG_CACHE_HOME/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nImplementationTests\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# Visual Studio\n.vs\n\n# Visual Studio Code\n.vscode\n\n\n# React\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": ".gitpod/.gitpod.Dockerfile",
    "content": "FROM custom\n                    \nUSER gitpod\n\n# Install custom tools, runtime, etc. using apt-get\n# For example, the command below would install \"bastet\" - a command line tetris clone:\n#\n# RUN sudo apt-get -q update && #     sudo apt-get install -yq bastet && #     sudo rm -rf /var/lib/apt/lists/*\n#\n# More information: https://www.gitpod.io/docs/config-docker/\n"
  },
  {
    "path": ".gitpod/.gitpod.yml",
    "content": "tasks:\n  - init: pip install -r ./requirements.txt\nimage:\n  file: .gitpod.Dockerfile\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\n\n#sudo: false\n\npython:\n  - 3.6\n  - 3.7\n  - 3.8\n  - pypy3\n\ninstall:\n  - python -m pip install -U pip\n  - pip install -r requirements.txt\n  - pip install unittest2\n  - python setup.py install\n  - pip install coverage\n\nscript:\n  - python3 -m unittest discover -s tests\n  - coverage run -m unittest discover -s tests\n\nafter_success:\n#  - codecov\n  - bash <(curl -s https://codecov.io/bash)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at rohitpotter12@gmail.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing guidelines\n\nTo start contributing to [whatsapp-play](https://github.com/rpotter12/whatsapp-play) project, please first discuss the change you wish to make via creating an issue \nin the issue section or any other method with the owners of this repository before making a change.<br />\nWe have a code of conduct, please follow it in all your interactions with the project.<br />\n\n# Contributing to whatsapp-play\n\n:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:\n\nWe love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:\n\n- Discussing the current state of the code\n- [Reporting a bug]( https://github.com/rpotter12/whatsapp-play/blob/master/.github/IssueTemplate/BugReportTemplate.md)\n- [Submitting a fix](https://github.com/rpotter12/whatsapp-play/blob/master/.github/Pull_Request_Template.md)\n- [Proposing new features]( https://github.com/rpotter12/whatsapp-play/blob/master/.github/IssueTemplate/FeatureRequestTemplate.md)\n\n# Points to remember\n\n1.Ensure any install or build dependencies are checked and verified before the end when releasing a build. <br />\n2.Update the README.md with details of changes including environment variables, various file parameters and container details **if needed**.<br />\n3.Try adding screenshots in your pull request of the changes you made. <br />\n4.You may be able to merge the pull request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the maintainer or reviewer to merge it for you.\n\n# Steps to follow :scroll:\n\n### 1. Fork it :fork_and_knife:\n\nYou can get your own fork/copy of [whatsapp-play]( https://github.com/rpotter12/whatsapp-play) by using the <kbd><b>Fork</b></kbd></a> button.\n\n [![Fork Button](https://help.github.com/assets/images/help/repository/fork_button.jpg)](https://github.com/rpotter12/whatsapp-play)\n\n### 2. Clone it :busts_in_silhouette:\n\nYou need to clone (download) it to local machine using\n\n```sh\ngit clone https://github.com/Your_Username/whatsapp-play.git\n```\n\n> This makes a local copy of repository in your machine.\n\nOnce you have cloned the ` whatsapp-play ` repository in GitHub, move to that folder first using change directory command.\n\n```sh\n# This will change directory to a folder whatsapp-play\ncd whatsapp-play\n```\n\nMove to this folder for all other commands.\n\n### 3. Set it up :arrow_up:\n\nRun the following commands to see that *your local copy* has a reference to *your forked remote repository* in GitHub :octocat:\n\n```sh\ngit remote -v\norigin  https://github.com/Your_Username/whatsapp-play.git (fetch)\norigin  https://github.com/Your_Username/whatsapp-play.git (push)\n```\n\nNow, add a reference to the original [whatsapp-play](https://github.com/rpotter12/whatsapp-play) repository using\n\n```sh\ngit remote add upstream https://github.com/rpotter12/whatsapp-play.git\n```\n\n> This adds a new remote named ***upstream***.\n\nSee the changes using\n\n```sh\ngit remote -v\norigin    https://github.com/Your_Username/whatsapp-play.git (fetch)\norigin    https://github.com/Your_Username/whatsapp-play.git (push)\nupstream  https://github.com/rpotter12/whatsapp-play.git (fetch)\nupstream  https://github.com/rpotter12/whatsapp-play.git (push)\n```\n\n### 4. Sync it :recycle:\n\nAlways keep your local copy of repository updated with the original repository.\nBefore making any changes and/or in an appropriate interval, run the following commands *carefully* to update your local repository.\n\n```sh\n# Fetch all remote repositories and delete any deleted remote branches\ngit fetch --all --prune\n\n# Switch to `master` branch\ngit checkout master\n\n# Reset local `master` branch to match `upstream` repository's `master` branch\ngit reset --hard upstream/master\n\n# Push changes to your forked `whatsapp-play` repo\ngit push origin master\n```\n\n### 5. Ready Steady Go :turtle: :rabbit2:\n\nOnce you have completed these steps, you are ready to start contributing by checking our `Help Wanted` Issues and creating [pull requests](https://github.com/rpotter12/whatsapp-play/pulls).\n\n### 6. Create a new branch :bangbang:\n\nWhenever you are going to make contribution. Please create separate branch using command and keep your `master` branch clean (i.e. synced with remote branch).\n\n```sh\n# It will create a new branch with name Branch_Name and will switch to that branch.\ngit checkout -b Branch_Name\n```\n\nCreate a separate branch for contribution and try to use same name of branch as of folder.\n\nTo switch to desired branch\n\n```sh\n# To switch from one folder to other\ngit checkout Branch_Name\n```\n\nTo add the changes to the branch. Use\n\n```sh\n# To add all files to branch Branch_Name\ngit add .\n```\n\nType in a message relevant for the code reviewer using\n\n```sh\n# This message gets associated with all files you have changed\ngit commit -m 'relevant message'\n```\n\nNow, Push your awesome work to your remote repository using\n\n```sh\n# To push your work to your remote repository\ngit push -u origin Branch_Name\n```\n\nFinally, go to your repository in browser and click on `compare and pull requests`.\nUse our [pull request template format]( https://github.com/rpotter12/whatsapp-play/blob/master/.github/Pull_Request_Template.md)\nThen add a title and description to your pull request that explains your precious effort. \n\nSit and relax till we review your PR, you've made your contribution to our project.\n\n:tada: :confetti_ball: :smiley: _**Happy Contributing**_ :smiley: :confetti_ball: :tada:\n"
  },
  {
    "path": "Dockerfile",
    "content": "#To build docker image from this file run\n#docker build .\n#on terminal\n\nFROM python:3.6-alpine\n#LABEL MAINTAINER\n\n# Copying files\nCOPY wplay/ /whatsapp-play/wplay\nCOPY setup.py /whatsapp-play/setup.py\nCOPY README.md /whatsapp-play/README.md\nCOPY requirements.txt /whatsapp-play/requirements.txt\n\n# Dependencies\nWORKDIR /whatsapp-play\nRUN apk add build-base\nRUN apk add make\nRUN apk add gcc musl-dev libffi-dev openssl-dev\nRUN pip install cryptography==2.9.0\nRUN apk add --no-cache libffi-dev\nRUN apk add build-base \nRUN apk add py3-pip \nRUN apk add python3-dev\nRUN pip install cffi==1.14.0\nRUN pip install -r requirements.txt\n\n#ENTRYPOINT echo \"Hello, welcome to whatsapp-play\"\nENTRYPOINT [\"python3 -m wplay -h\"]\n\nCMD [ \"python\"]"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Rohit Potter\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <img src=\"images/logo.png\">\n            \n# whatsapp-play\n\n[![Downloads](https://pepy.tech/badge/wplay)](https://pepy.tech/project/wplay)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/749acf4cad424fbeb96a412963aa83ea)](https://app.codacy.com/app/rpotter12/whatsapp-play?utm_source=github.com&utm_medium=referral&utm_content=rpotter12/whatsapp-play&utm_campaign=Badge_Grade_Settings)\n[![PyPi](https://img.shields.io/pypi/v/wplay)](https://pypi.org/project/wplay/)\n![CircleCI](https://circleci.com/gh/rpotter12/whatsapp-play/tree/master.svg?style=svg&circle-token=2b67dd21e60a01fdd36a670629574479aeb2f5c4)\n[![Docker](https://img.shields.io/docker/cloud/build/rpotter12/whatsapp-play)](https://hub.docker.com/repository/docker/rpotter12/whatsapp-play/general)\n[![Build Status](https://travis-ci.org/rpotter12/whatsapp-play.svg?branch=master)](https://travis-ci.org/rpotter12/whatsapp-play)\n[![codecov](https://codecov.io/gh/rpotter12/whatsapp-play/branch/master/graph/badge.svg)](https://codecov.io/gh/rpotter12/whatsapp-play)\n[![twitter](https://img.shields.io/twitter/url/https/github.com/rpotter12/whatsapp-play.svg?style=social)](https://twitter.com/rpotter121998)\n[![HitCount](http://hits.dwyl.io/rpotter12/whatsapp-play.svg)](http://hits.dwyl.io/rpotter12/whatsapp-play)\n[![Gitter](https://badges.gitter.im/whatsapp-play/community.svg)](https://gitter.im/whatsapp-play/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)\n[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/rpotter12/whatsapp-play) \n\n</div>\n\n\nA command line software through which you can play with your WhatsApp. The software aims to provide all the facilities to use and implement a multitude of WhatsApp features. Blog link: [https://github.com/rpotter12/rpotter12.github.io/blob/master/blogs/blog3-tracking-26-07-2019.md](https://github.com/rpotter12/rpotter12.github.io/blob/master/blogs/blog3-tracking-26-07-2019.md)\n\n## Features\n  - Online/offline Tracker\n  - Terminal Chat\n  - Chat Intermediator\n  - Message Service\n  - Telegram Bot\n  - Message Blast\n  - Message Timer\n  - Save Chat\n  - Schedule Message\n  - About Changer\n  - Get News (Get API from <https://newsapi.org/> and paste it in newsapi = NewsApiClient(api_key=\"YOUR API KEY\"))\n  - Get Profile Photos\n  - Broadcast Message\n  - Target Info (Profile Info)\n  - Download Media\n\n---\n\n## Installation\n\n### Install whatsapp-play from PyPI: <br />\nWindows: `python -m pip install wplay` <br />\nUnix/Linux/Mac: `python3 -m pip install wplay` <br />\n**Installation Video:** [Simple Installation Link](https://youtu.be/HS6ksu6rCxQ)\n\n### Alternate way - Run whatsapp-play from source code: <br />\n**Windows**<br />\n```\n$ git clone https://github.com/rpotter12/whatsapp-play.git\n$ cd whatsapp-play\n$ python -m pip install -r requirements.txt\n$ python -m wplay -h\n```\n\n**Unix/Linux/Mac**<br />\n```\n$ git clone https://github.com/rpotter12/whatsapp-play.git\n$ cd whatsapp-play\n$ python3 -m pip install -r requirements.txt\n$ python3 -m wplay -h\n```\n\n## Usage\n<img src=\"images/usage.png\"><br />\n\nFor detailed usage of command visit: [https://github.com/rpotter12/whatsapp-play/wiki/Usage](https://github.com/rpotter12/whatsapp-play/wiki/Usage)\n\n## Contribute\n\nThe easiest way to contribute to **Whatsapp-Play** is by starring the repository and opening more and more [issues](https://github.com/rpotter12/whatsapp-play/issues) for features you'd like to see in future. <br />\n\nFirst step is to create a fork and clone, then you can solve the [issues](https://github.com/rpotter12/whatsapp-play/issues) listed and help us find new ones. Then try debugging with Visual Studio Code it is necessary to create a launcher with the arguments. <br />\n\nSteps to create a launcher with arguments follow the steps bellow: <br />\n1. Click in 'Debug' tab\n1. Click in 'Add Configuration'\n1. Select 'Module'\n1. Type 'wplay' and press Enter\n1. A json file will be opened. Inside configurations add the args, for example: \"args\":[\"-wb\",\"name\"] \n\n**Debug Tutorial Video:** [Debug Tutorial Link](https://youtu.be/NyJgUGvyWnY)<br />\nCheck more about contribution guidelines [here](https://www.github.com/rpotter12/whatsapp-play/CONTRIBUTION.md)\n\n## Disclaimer\nThis software is for educational purpose only. Keeping eye on a innocent person can make person's life stressful.\n\n## License\n[![License](https://img.shields.io/github/license/rpotter12/whatsapp-play.svg)](https://github.com/rpotter12/whatsapp-play/blob/master/README.md)\n\n***If you like the project, support us by star***\n"
  },
  {
    "path": "builds/build.bat",
    "content": "python setup.py sdist bdist_wheel\n"
  },
  {
    "path": "builds/build.sh",
    "content": "#!/usr/bin/env bash\npython3 setup.py sdist bdist_wheel"
  },
  {
    "path": "requirements.txt",
    "content": "argparse>=1.4.0\nbeautifulsoup4>=4.8.1\ncolorama>=0.4.3\nDateTime>=4.3\ndecorator>=4.4.2\nflake8>=3.7.9\ngoogle>=2.0.3\ngTTS==2.1.1\nnewsapi-python>=0.2.6\nphonenumbers==8.10.2\nplaysound>=1.2.2\nprompt_toolkit==1.0.14\npsutil>=5.7.0\npycodestyle>=2.6.0\npycparser>=2.20\npyee>=7.0.2\npyfiglet>=0.8.post1\npyflakes>=2.2.0\nPygments>=2.6.1\npyppeteer>=0.2.2\npython-dotenv>=0.12.0\npython-telegram-bot>=12.7\nrequests>=2.22.0\ntransitions>=0.7.2\nurllib3>=1.25.8\nwebsockets>=8.1\nwhaaaaat>=0.5.2\n"
  },
  {
    "path": "setup.py",
    "content": "from setuptools import setup, find_packages\n\nwith open(\"README.md\", \"r\") as f:\n    long_description = f.read()\n\nsetup(\n    name=\"wplay\",\n    version=\"8.0.5\",\n    install_requires=[\"argparse >= 1.4.0\",\n                      \"beautifulsoup4>=4.8.1\",\n                      \"colorama>=0.4.3\",\n                      \"dateTime>=4.3\",\n                      \"decorator>=4.4.2\",\n                      \"flake8>=3.7.9\",\n                      \"google>=2.0.3\",\n                      \"gTTS==2.1.1\",\n                      \"newsapi-python>=0.2.6\",\n                      \"phonenumbers==8.10.2\",\n                      \"playsound>=1.2.2\",\n                      \"prompt_toolkit==1.0.14\",\n                      \"psutil>=5.7.0\",\n                      \"pyfiglet>=0.8.post1\",\n                      \"pyflakes>=2.2.0\",\n                      \"Pygments>=2.6.1\",\n                      \"pyppeteer>=0.0.25\",\n                      \"python-dotenv==0.12.0\",\n                      \"python-telegram-bot>=11.1.0\",\n                      \"requests>=2.22.0\",\n                      \"transitions>=0.7.2\",\n                      \"urllib3>=1.25.8\",\n                      \"websockets>=8.1\",\n                      \"whaaaaat>=0.5.2\",\n                      ],\n    packages=find_packages(),\n    description=\"command line software to play with your WhatsApp\",\n    long_description=long_description,\n    long_description_content_type=\"text/markdown\",\n    author=\"Rohit Potter, Alexandre Calil\",\n    author_email=\"rohitpotter12@gmail.com, alexandrecalilmf@gmail.com\",\n    license=\"MIT\",\n    python_requires=\">=3.6\",\n    url=\"https://github.com/rpotter12/whatsapp-play/\",\n    download_url=\"https://pypi.org/project/wplay/\",\n    keywords=[\n        \"whatsapp\",\n        \"whatsapp cli\",\n        \"whatsapp api\",\n        \"whatsapp service\"\n        \"whatsapp chat\",\n        \"message blast\",\n        \"message timer\",\n        \"whatsapp terminal\",\n        \"whatsapp news\",\n        \"whatsapp schedule\",\n        \"tracker\",\n        \"online tracking\",\n        \"save-chat\"\n    ],\n    classifiers=[\n        \"Programming Language :: Python\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3 :: Only\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Operating System :: OS Independent\",\n    ],\n    entry_points={\"console_scripts\": [\"wplay = wplay.__main__:main\"]},\n)\n"
  },
  {
    "path": "tests/test_func.py",
    "content": "#region imports\nimport functools\nimport os\nimport shutil\nimport stat\nimport subprocess\nimport sys\nimport time\n\nimport psutil\nfrom psutil import MACOS\nfrom psutil import POSIX\nfrom psutil import WINDOWS\n\nfrom psutil._compat import which\n#endregion\n\n\n__all__ = [\n    # constants\n    'DEVNULL' , 'PYTHON_EXE', 'TESTFILE_PREFIX' , 'TESTFN',\n    # subprocesses\n    'get_test_subprocess',\n    # fs utils\n    'safe_rmpath' ,\n    # sync primitives\n    'wait_for_pid', 'wait_for_file',\n]\n\n\nTESTFILE_PREFIX = '$testfn'\nif os.name == 'java':\n    # Jython disallows @ in module names\n    TESTFILE_PREFIX = '$psutil-test-'\nelse:\n    TESTFILE_PREFIX = '@psutil-test-'\nTESTFN = os.path.join(os.path.realpath(os.getcwd()), TESTFILE_PREFIX)\nTESTFN = TESTFN + str(os.getpid())\n\n_TESTFN = TESTFN + '-internal'\n\n\ndef _get_py_exe():\n    def attempt(exe):\n        try:\n            subprocess.check_call(\n                [exe, \"-V\"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        except Exception:\n            return None\n        else:\n            return exe\n\n    if MACOS:\n        exe = \\\n            attempt(sys.executable) or \\\n            attempt(os.path.realpath(sys.executable)) or \\\n            attempt(which(\"python%s.%s\" % sys.version_info[:2])) or \\\n            attempt(psutil.Process().exe())\n        if not exe:\n            raise ValueError(\"can't find python exe real abspath\")\n        return exe\n    else:\n        exe = os.path.realpath(sys.executable)\n        assert os.path.exists(exe), exe\n        return exe\n\n\nPYTHON_EXE = _get_py_exe()\nDEVNULL = open(os.devnull, 'r+')\n\n_subprocesses_started = set()\n\n\ndef get_test_subprocess(cmd=None, **kwds):\n    \"\"\"Creates a python subprocess which does nothing for 30 secs and\n    return it as subprocess.Popen instance.\n    If \"cmd\" is specified that is used instead of python.\n    By default stdin and stdout are redirected to /dev/null.\n    It also attemps to make sure the process is in a reasonably\n    initialized state.\n    The process is registered for cleanup on reap_children().\n    \"\"\"\n    kwds.setdefault(\"stdin\", DEVNULL)\n    kwds.setdefault(\"stdout\", DEVNULL)\n    kwds.setdefault(\"cwd\", os.getcwd())\n    kwds.setdefault(\"env\", os.environ)\n    if WINDOWS:\n        # Prevents the subprocess to open error dialogs. This will also\n        # cause stderr to be suppressed, which is suboptimal in order\n        # to debug broken tests.\n        CREATE_NO_WINDOW = 0x8000000\n        kwds.setdefault(\"creationflags\", CREATE_NO_WINDOW)\n    if cmd is None:\n        safe_rmpath(_TESTFN)\n        pyline = \"from time import sleep;\" \\\n                 \"open(r'%s', 'w').close();\" \\\n                 \"sleep(30);\" % _TESTFN\n        cmd = [PYTHON_EXE, \"-c\", pyline]\n        sproc = subprocess.Popen(cmd, **kwds)\n        _subprocesses_started.add(sproc)\n        #wait_for_file(_TESTFN, delete=True, empty=True)\n    else:\n        sproc = subprocess.Popen(cmd, **kwds)\n        _subprocesses_started.add(sproc)\n        wait_for_pid(sproc.pid)\n    return sproc\n\n\ndef wait_for_pid(pid):\n    \"\"\"Wait for pid to show up in the process list then return.\n    Used in the test suite to give time the sub process to initialize.\n    \"\"\"\n    psutil.Process(pid)\n    if WINDOWS:\n        # give it some more time to allow better initialization\n        time.sleep(0.01)\n\n\ndef wait_for_file(fname, delete=True, empty=False):\n    \"\"\"Wait for a file to be written on disk with some content.\"\"\"\n    with open(fname, \"rb\") as f:\n        data = f.read()\n    if not empty:\n        assert data\n    if delete:\n        safe_rmpath(fname)\n    return data\n\n\ndef safe_rmpath(path):\n    \"Convenience function for removing temporary test files or dirs\"\n    def retry_fun(fun):\n        # On Windows it could happen that the file or directory has\n        # open handles or references preventing the delete operation\n        # to succeed immediately, so we retry for a while. See:\n        # https://bugs.python.org/issue33240\n        stop_at = time.time() + 1\n        while time.time() < stop_at:\n            try:\n                return fun()\n            except FileNotFoundError:\n                pass\n\n    try:\n        st = os.stat(path)\n        if stat.S_ISDIR(st.st_mode):\n            fun = functools.partial(shutil.rmtree, path)\n        else:\n            fun = functools.partial(os.remove, path)\n        if POSIX:\n            fun()\n        else:\n            retry_fun(fun)\n    except FileNotFoundError:\n        pass"
  },
  {
    "path": "tests/test_helpers.py",
    "content": "'''\nTo run tests run\npython3 -m unittest discover -s tests\non terminal\n'''\n#region imports\nimport unittest\nfrom os import path\nfrom wplay.utils import helpers\n#endregion\n\n\n#region class\nclass testPath(unittest.TestCase):\n\n    def test_paths_exist(self):\n        # self.assertTrue(path.exists(helpers.audio_file_folder_path))\n        # self.assertTrue(path.exists(helpers.chatbot_image_folder_path))\n        self.assertTrue(path.exists(helpers.data_folder_path))\n        self.assertTrue(path.exists(helpers.logs_path))\n        self.assertTrue(path.exists(helpers.log_file_path))\n        # self.assertTrue(path.exists(helpers.media_path))\n        # self.assertTrue(path.exists(helpers.messages_json_folder_path))\n        # self.assertTrue(path.exists(helpers.messages_json_path))\n        # self.assertTrue(path.exists(helpers.open_messages_json_path))\n        # self.assertTrue(path.exists(helpers.profile_photos_path))\n        # self.assertTrue(path.exists(helpers.save_chat_folder_path))\n        self.assertTrue(path.exists(helpers.test_log_file_path))\n        # self.assertTrue(path.exists(helpers.tracking_folder_path))\n        # self.assertTrue(path.exists(helpers.user_data_folder_path))\n\nif __name__ == '__main__':\n\tunittest.main()\n#endregion\n"
  },
  {
    "path": "tests/test_kill_child_process.py",
    "content": "'''\nTo run tests run\npython3 -m unittest discover -s tests\non terminal\n'''\n#region imports\nimport unittest\nimport psutil\nimport test_func\nfrom wplay.utils.helpers import kill_child_processes\n#endregion\n\n\n#region class for test_kill_child_processes function\nclass Testkill(unittest.TestCase):\n\n    def test_kill(self):\n        sproc = test_func.get_test_subprocess()\n        test_pid = sproc.pid\n        p = psutil.Process(test_pid)\n        kill_child_processes(test_pid)\n        p.wait()\n        self.assertFalse(psutil.pid_exists(test_pid))\n\nif __name__ == '__main__':\n\tunittest.main()\n#endregion\n"
  },
  {
    "path": "tests/test_logger.py",
    "content": "'''\nTo run tests run\npython3 -m unittest discover -s tests\non terminal\n'''\n#region imports\nimport unittest\nfrom wplay.utils.Logger import Logger\nfrom pathlib import Path\n#endregion\n\n\n#region LOGGER create\nlogger = Logger(Path(__file__).name)\n#endregion\n\n\n#region class for Logger function\nclass CaptureLogsExample(unittest.TestCase):\n\n    def test_assert_logs(self):\n        \"\"\"Verify logs using built-in self.assertLogs().\"\"\"\n        logger.error(\"Testing logg class\")\n        self.assertTrue(logger, 'test_logger.py - ERROR - Testing logg class')\n\nif __name__ == '__main__':\n\tunittest.main()\n#endregion"
  },
  {
    "path": "wplay/__init__.py",
    "content": ""
  },
  {
    "path": "wplay/__main__.py",
    "content": "# region IMPORTS\nimport argparse\nimport asyncio\nimport sys\nimport os\nfrom pathlib import Path\n\nfrom pyfiglet import Figlet\n\nfrom wplay import online_tracker\nfrom wplay import message_blast\nfrom wplay import message_timer\nfrom wplay import terminal_chat\nfrom wplay import chat_intermediator\nfrom wplay import broadcast_message\nfrom wplay import save_chat\nfrom wplay import telegram_bot\nfrom wplay import schedule_message\nfrom wplay import about_changer\nfrom wplay import get_news\nfrom wplay import get_media\nfrom wplay import download_media\nfrom wplay import message_service\nfrom wplay import target_info\nfrom wplay import profile_download\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import create_dirs\nfrom wplay.utils.helpers import kill_child_processes\n# endregion\n\n\ndef print_logo(text_logo):\n    figlet = Figlet(font='slant')\n    print(figlet.renderText(text_logo))\n\n\n# parse positional and optional arguments\ndef get_arg_parser():\n    parser = argparse.ArgumentParser(description='WhatsApp-play')\n    parser.add_argument(\n        \"target\",\n        metavar=\"TARGET\",\n        type=str,\n        default=None,\n        nargs=\"?\",\n        help=\"\"\"contact or group name, optional,\n              target can be selected manually except for saving chat\"\"\")\n\n    parser.add_argument('-s', '--sender', help='contact or group name')\n    parser.add_argument('-r', '--receiver', help='contact or group name')\n\n    group = parser.add_mutually_exclusive_group(required=True)\n    group.add_argument(\n        \"-wc\",\n        \"--terminal-chat\",\n        action=\"store_true\",\n        help=\"chatting from command line\")\n\n    group.add_argument(\n        \"-wi\",\n        \"--chat-intermediator\",\n        action=\"store_true\",\n        help='Be an Intermediator from command line -wi -s <sender> -r <receiver> ')\n\n    group.add_argument(\n        \"-wpd\",\n        \"--profile-download\",\n        action=\"store_true\",\n        help='Download the peofile picture of target')\n\n    group.add_argument(\n        \"-wms\",\n        \"--message-service\",\n        action=\"store_true\",\n        help=\"send messages from a JSON file\")\n\n    group.add_argument(\n        \"-wb\",\n        \"--message-blast\",\n        action=\"store_true\",\n        help=\"message blast to a person\")\n\n    group.add_argument(\n        \"-wti\",\n        \"--message-timer\",\n        action=\"store_true\",\n        help=\"send messages from time to time\")\n\n    group.add_argument(\n        \"-wt\",\n        \"--online-tracker\",\n        action=\"store_true\",\n        help=\"track online status of person\")\n\n    group.add_argument(\n        \"-wtb\",\n        \"--telegram-bot\",\n        action=\"store_true\",\n        help=\"sends tracking status to telegram bot\")\n\n    group.add_argument(\n        \"-wsc\",\n        \"--save-chat\",\n        action=\"store_true\",\n        help=\"save all chats from Google Drive, target is necessary\")\n\n    group.add_argument(\n        \"-ws\",\n        \"--schedule-message\",\n        action=\"store_true\",\n        help=\"send the message at scheduled time\")\n\n    group.add_argument(\n        \"-wa\",\n        \"--about-changer\",\n        action=\"store_true\",\n        help=\"Changes the about section\")\n\n    group.add_argument(\n        \"-wgn\",\n        \"--get-news\",\n        action=\"store_true\",\n        help=\"Get news in whatsapp group\")\n\n    group.add_argument(\n        \"-wgp\",\n        \"--get-profile-photos\",\n        action=\"store_true\",\n        help=\"Get profile photo of all your contacts\")\n\n    group.add_argument(\n        \"-wbc\",\n        \"--broadcast\",\n        action=\"store_true\",\n        help=\"Broadcast message\")\n\n    group.add_argument(\n        \"-wtf\",\n        \"--target-info\",\n        action=\"store_true\",\n        help=\"finds the information about target's contact\")\n\n    group.add_argument(\n        \"-wd\",\n        \"--download-media\",\n        action=\"store_true\",\n        help=\"Download the media of the target's contact\")\n\n    return parser\n\n\n# functions for different arguments\nasync def get_and_match_args(parser):\n    args = parser.parse_args()\n    if args.online_tracker:\n        await online_tracker.tracker(args.target)\n\n    elif args.message_service:\n        await message_service.message_service()\n\n    elif args.telegram_bot:\n        telegram_bot.telegram_status(args.target)\n\n    elif args.terminal_chat:\n        await terminal_chat.chat(args.target)\n\n    elif args.chat_intermediator:\n        await chat_intermediator.intermediary(args.sender, args.receiver)\n\n    elif args.profile_download:\n        await profile_download.get_profile_picture(args.target)\n\n    elif args.broadcast:\n        await broadcast_message.broadcast()\n\n    elif args.message_blast:\n        await message_blast.message_blast(args.target)\n\n    elif args.message_timer:\n        await message_timer.message_timer(args.target)\n\n    elif args.schedule_message:\n        await schedule_message.schedule_message(args.target)\n\n    elif args.about_changer:\n        await about_changer.about_changer()\n\n    elif args.get_news:\n        await get_news.get_news(args.target)\n\n    elif args.get_profile_photos:\n        await get_media.get_profile_photos()\n\n    elif args.target_info:\n        await target_info.target_info(args.target)\n\n    elif args.download_media:\n        await download_media.download_media(args.target)\n\n    elif args.save_chat:\n        await save_chat.save_chat(args.target)\n\n\nasync def main():\n    print_logo(\"wplay\")\n    create_dirs()\n    parser = get_arg_parser()\n    try:\n        await get_and_match_args(parser)\n        sys.exit(0)\n    except KeyboardInterrupt:\n        sys.exit(0)\n\n\ntry:\n    asyncio.get_event_loop().run_until_complete(main())\nexcept KeyboardInterrupt:\n    pass\nexcept AssertionError:\n    try:\n        for task in asyncio.all_tasks():\n            task.cancel()\n    except RuntimeError:\n        exit()\n    exit()\nfinally:\n    kill_child_processes(os.getpid())\n"
  },
  {
    "path": "wplay/about_changer.py",
    "content": "# region IMPORTS\nimport time\nfrom pathlib import Path\nfrom wplay.utils.helpers import whatsapp_selectors_dict\nfrom wplay.utils import browser_config\nfrom wplay.utils.Logger import Logger\nfrom newsapi.newsapi_client import NewsApiClient\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\n# Asking user\nasync def about_changer():\n    option = input(\"Choose(1/2) \\n1.Write new about \\n2.Change about with latest headline\\n\")\n    if option == '1':\n        await change_about()\n    else:\n        await about_changer_news()\n\n\n# Custom About\nasync def change_about():\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    # opens photo element\n    await page.waitForSelector(whatsapp_selectors_dict['profile_photo_element'], visible=True)\n    await page.click(whatsapp_selectors_dict['profile_photo_element'])\n    await page.waitForSelector(whatsapp_selectors_dict['about_edit_button_element'])\n    await page.click(whatsapp_selectors_dict['about_edit_button_element'])\n    status = input(\"Enter your new about: \")\n    __logger.info(\"Writing About\")\n    # Write about\n    await page.type(whatsapp_selectors_dict['about_text_area'], status)\n    await page.keyboard.press('Enter')\n    print(\"About changed to {}\".format(status))\n\n\nasync def get_api_key():\n    __logger.info(\"Getting key\")\n    print(\"Visit https://newsapi.org/ to get your own API key\")\n    key = input(\"Enter you API KEY : \")\n    get_api_key.newsapi = NewsApiClient(api_key='{}'.format(key))\n\n\n# News in About\nasync def about_changer_news():\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    await get_api_key()\n    query: str = str(input(\"What's the news theme? : \"))\n    # opens photo element\n    await page.waitForSelector(whatsapp_selectors_dict['profile_photo_element'], visible=True)\n    await page.click(whatsapp_selectors_dict['profile_photo_element'])\n    news = ''\n    while True:\n        current_news = str(fetch_news(query))\n        print(current_news)\n        if news != current_news:\n            # Click on edit about button\n            await page.waitForSelector(whatsapp_selectors_dict['about_edit_button_element'])\n            await page.click(whatsapp_selectors_dict['about_edit_button_element'])\n            for _ in range(140):\n                await page.keyboard.press('Backspace')\n            news = current_news\n            __logger.info(\"Updating About latest news\")\n            # Write about\n            await page.type(whatsapp_selectors_dict['about_text_area'], news)\n            await page.keyboard.press('Enter')\n        # News get updated every 15 minutes by newsapi.org\n        # Added extra minute for buffer period\n        # For free account : max limit is 500 request/day\n        time.sleep(905)\n\n\ndef fetch_news(query):\n    __logger.info(\"Fetching news\")\n    top_headlines = get_api_key.newsapi.get_top_headlines(q=query, language='en')\n    return top_headlines['articles'][0]['title']\n"
  },
  {
    "path": "wplay/broadcast_message.py",
    "content": "# region IMPORTS\nfrom tkinter import Tk\nfrom tkinter.filedialog import askopenfile\nfrom pathlib import Path\n\nfrom wplay.utils import browser_config\nfrom wplay.utils.target_search import search_target_by_number\nfrom wplay.utils import io\nfrom typing import List\nfrom wplay.utils.helpers import data_folder_path\nfrom wplay.utils.Logger import Logger\n# endregion\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nclass InvalidNumber(Exception):\n    message = \"Either Number is invalid or no account exist for the number or the number was kept in wrong format :(\\n\"\n\n\ndef ProcessNumbers():\n    __logger.info(\"Processing numbers.\")\n    print(\"Choose a text file containing full numbers with country code, one number per line.\")\n    Tk().withdraw()\n    filename = askopenfile(\n            initialdir=data_folder_path,\n            title='Choose a text file with numbers.',\n            filetypes=[(\"text files\", \"*.txt\")],\n            mode=\"r\"\n            )\n    numbers = filename.readlines()\n    for i in range(len(numbers)):\n        number = numbers[i].strip(\"\\n+\")\n        numbers[i] = number\n    return numbers\n\n\nasync def broadcast():\n    __logger.info(\"Broadcast message.\")\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    numbers = ProcessNumbers()\n    message: List[str] = io.ask_user_for_message_breakline_mode()\n\n    for number in numbers:\n        if await search_target_by_number(page, number):\n            await io.send_message(page, message)\n\n    __logger.info(\"Messages broadcasted successfully!\")\n    print(\"Messages broadcasted successfully!\")\n"
  },
  {
    "path": "wplay/chat_intermediator.py",
    "content": "# region IMPORTS\nfrom wplay import terminal_chat\nfrom wplay.utils.Logger import Logger\n\nfrom pathlib import Path\n# endregion\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def intermediary(sender, receiver):\n    \"\"\"\n    Function to create intermediate between two person.\n    \"\"\"\n    __logger.info(\"Being and Intermediator\")\n    intermediary.rec = receiver\n    await terminal_chat.chat(sender)\n"
  },
  {
    "path": "wplay/chatbot.py",
    "content": "from googlesearch import search\nfrom pyppeteer import launch\nfrom wplay.utils.helpers import chatbot_image_folder_path\n\n\nasync def Bot(last_Message):\n    \"\"\"\n    Function to perform instruction as instructed to bot.\n    \"\"\"\n    print('\\n Bot activated')\n    first_last_Message = \"\".join(last_Message.split())\n    simple_menu = {\n                \"hi\": say_hi,\n                \"help\": _help_commands,\n                \"goodmorning\": say_goodmorning,\n                \"goodnight\": say_goodnight,\n                \"howareyou?\": say_fine,\n            }\n    simple_menu_keys = simple_menu.keys()\n    result = []\n\n    try:\n        command_args = first_last_Message[1:].split(\" \", 1)\n        command_arg = last_Message[1:].split(\" \", 1)\n\n        if len(command_args) == 1 and command_args[0] in simple_menu_keys:\n            return simple_menu[command_args[0]]()\n\n        elif command_arg[0] == 'google':\n            query = \"\".join(command_arg[1])\n            for j in search(query, tld=\"co.in\", num=10, stop=10, pause=2):\n                result.append(j)\n            print(\"Sending links for query\")\n            return result\n\n        elif command_arg[0] == \"image\":\n            query = \"\".join(command_arg[1])\n            await takeScreenshot(query)\n            print(\"Taking screenshot of google image for query\")\n            return \"Sending you screenshot\"\n\n        elif command_arg[0] == \"maps\":\n            query = \"\".join(command_arg[1])\n            map_parameters_list = query.replace(\" \", \"\")\n            map_parameters = map_parameters_list.split(',')\n            base_url = \"https://www.google.com/maps/dir/?api=1&\"\n            custom_url = base_url + \"origin={ori}&destination={dest}&travelmode={t_mode}\".format(ori=map_parameters[0], dest=map_parameters[1], t_mode=map_parameters[2])\n            print(\"Sending link for google maps\")\n            return custom_url\n\n        else:\n            return \"Wrong command. Send me /help to see a list of valid commands\"\n\n    except KeyError as e:\n        print(\"Key Error Exception: {err}\".format(err=str(e)))\n\n\ndef say_hi():\n    print(\"Saying hi\")\n    return \"Wplay chatbot says hi! Hope you are having a nice day...\"\n\n\ndef say_goodmorning():\n    print(\"Saying good morning\")\n    return \"Bot says Good Morning! Have a Good Day...\"\n\n\ndef say_goodnight():\n    print(\"Saying good night\")\n    return \"Bot says Good Night! Sweet Dreams...\"\n\n\ndef say_fine():\n    print(\"Saying I am Fine!\")\n    return \"Bot says I am Fine Thank You! How are you?\"\n\n\ndef _help_commands():\n    print(\"Asking for help\")\n    return \"How may I assist you with help\\n\"\\\n               \"List of commands:\\n\" \\\n               \"/hi (bot says hi), \" \\\n               \"/all_commands (ist of all commands), \" \\\n               \"/good morning, \" \\\n               \"/good night, \" \\\n               \"/how are you? \" \\\n               \"/google {query} \" \\\n               \"/image {query} \" \\\n               \"/maps {origin}, {destination}, {mode:driving/bicycling/transit/two-wheeler/walking}\"\n\n\nasync def takeScreenshot(qry):\n    browser = await launch()\n    page = await browser.newPage()\n    await page.goto('https://www.google.com/search?q={}&source=lnms&tbm=isch'.format(qry))\n    image_path = str(chatbot_image_folder_path / '{}.png'.format(qry))\n    await page.screenshot({'path': image_path})\n    await browser.close()\n"
  },
  {
    "path": "wplay/download_media.py",
    "content": "# region Imports\nfrom pathlib import Path\n\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils.helpers import media_path\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import whatsapp_selectors_dict\nimport time\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def download_media(target):\n    page, browser = await browser_config.configure_browser_and_load_whatsapp()\n\n    if target is not None:\n        try:\n            await target_search.search_and_select_target(page, target)\n        except Exception as e:\n            print(e)\n            await page.reload()\n            await target_search.search_and_select_target_without_new_chat_button(\n                    page,\n                    target\n                    )\n    else:\n        await target_select.manual_select_target(page)\n\n    count = int(input(\"Count of media you want to download: \"))\n\n    # Click on the photo element of the target\n    await page.waitForSelector(whatsapp_selectors_dict['target_name_selector'], visible=True)\n    await page.evaluate(f'''document.querySelector('{whatsapp_selectors_dict['target_name_selector']}').click()''')\n\n    time.sleep(1)\n\n    # Click on the `Media, Link and Docs` text\n    await page.waitForSelector(whatsapp_selectors_dict['media_text'])\n    await page.click(whatsapp_selectors_dict['media_text'])\n\n    # Click on the most recent media element\n    while True:\n        try:\n            await page.evaluate(f'''document.querySelector('{whatsapp_selectors_dict['media_images']}').click()''')\n            break\n        except Exception as e:\n            print(\"\", end='')\n\n    media_arr = {'img': [], 'vid': []}\n\n    # Currently downloads the last 50 medias\n    for _ in range(count):\n        try:\n            try:\n                # If media is an image\n                countTry = 0  # Threshold of how many times to try looking for media\n                while True:\n                    if countTry > 500:\n                        await page.waitForSelector(whatsapp_selectors_dict['left_arrow_button'])\n\n                    img = await page.evaluate(f'''() => [...document.querySelectorAll('{whatsapp_selectors_dict['media_url_img']}')]\n                                                        .map(element => element.src)''')\n                    if img and len(img) == 2:\n                        img = img[-1]\n                        if img not in media_arr:\n                            media_arr['img'].append(img)\n                        break\n                    countTry += 1\n                    time.sleep(0.3)\n            except Exception as e:\n                # If media is a video or gif\n                countTry = 0\n                while True:\n                    vid = await page.evaluate(f'''() => [...document.querySelectorAll('{whatsapp_selectors_dict['media_url_vid']}')]\n                                                        .map(element => element.src)''')\n                    if vid:\n                        vid = vid[-1]\n                        media_arr['vid'].append(vid)\n                        break\n\n            # Go to next media element\n            await page.waitForSelector(whatsapp_selectors_dict['left_arrow_button'])\n            await page.evaluate(f'''document.querySelector('{whatsapp_selectors_dict['left_arrow_button']}').click()''')\n            time.sleep(0.5)\n\n        except Exception as e:\n            print(e)\n\n    count = 0\n    newPage = await browser.newPage()\n    # Downloading media\n    for image in media_arr['img']:\n        try:\n            count += 1\n            print(image)\n            viewSource = await newPage.goto(image)\n            f = open(media_path / f'{count}.jpg', 'wb')\n            f.write(await viewSource.buffer())\n            f.close()\n        except Exception as e:\n            print(\"Error saving image\", e)\n\n    for video in media_arr['vid']:\n        try:\n            count += 1\n            viewSource = await newPage.goto(video)\n            f = open(media_path / f'{count}.mp4', 'wb')\n            f.write(await viewSource.buffer())\n            f.close()\n        except Exception as e:\n            print(\"Error saving video\", e)\n\n    print(\"Saved the media to the 'media' dir\")\n    time.sleep(10)\n"
  },
  {
    "path": "wplay/get_media.py",
    "content": "# region Imports\nimport time\nfrom pathlib import Path\n\nfrom wplay.utils import browser_config\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import profile_photos_path\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def get_profile_photos():\n    \"\"\"\n    Download the profile picture of all the contacts.\n    \"\"\"\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    total_contacts = int(input(\"Please provide total whatsapp contacts: \"))\n    loop = round(total_contacts/7)\n    images_list = []\n\n    await page.waitForSelector('#pane-side > div:nth-child(1) > div > div > div:nth-child(1) > div > div > div > div > img')\n\n    for c in range(loop):\n        for i in range(1, 18):\n            selector = f\"#pane-side > div:nth-child(1) > div > div > div:nth-child({i}) > div > div > div > div > img\"\n            try:\n                await page.waitForSelector(selector, timeout=2000)\n                image_url = await page.evaluate(f'document.querySelector(\"{selector}\").getAttribute(\"src\")')\n                print(f\"{c}:{i}-{image_url}\")\n                if image_url not in images_list:\n                    images_list.append(image_url)\n            except Exception as e:\n                print(e)\n                print(\"No profile image found\")\n        await page.evaluate(\"document.querySelector('#pane-side').scrollBy(0, 500)\")\n\n    for count in range(len(images_list)):\n        try:\n            viewSource = await page.goto(images_list[count])\n            f = open(profile_photos_path / f'{count}.jpg', 'wb')\n            f.write(await viewSource.buffer())\n            f.close()\n        except Exception as e:\n            print(\"Error saving image\")\n\n    print(\"Saved all the images to media_folder.\")\n    time.sleep(5)\n"
  },
  {
    "path": "wplay/get_news.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\nimport time\n\nfrom newsapi.newsapi_client import NewsApiClient\n\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils import io\nfrom wplay.utils.Logger import Logger\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n'''\nVisit https://newsapi.org/ to get your own API key.\n'''\nnewsapi = NewsApiClient(api_key=\"YOUR API KEY\")\n\nasync def get_news(target):\n    \"\"\"\n    Sends news as a message in every two minutes.\n    \"\"\"\n    def fetch_news(country_code):\n        \"\"\"\n        Return the title and url of the news.\n        \"\"\"\n        headlines = newsapi.get_top_headlines(country=country_code, language='en')\n        url = headlines['articles'][0]['url']\n        title = headlines['articles'][0]['title']\n        return title, url\n\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    if target is not None:\n        try:\n            await target_search.search_and_select_target(page, target)\n        except Exception as e:\n            print(e)\n            await target_search.search_and_select_target_without_new_chat_button(page, target)\n    else:\n        await target_select.manual_select_target(page)\n\n    country = input(\"Enter your country code (ex: us or in): \")\n    while True:\n        try:\n            news, source = fetch_news(country)\n            news_ = f\"*{news}* \\n Full News :  {source}\"\n            await io.send_message(page, news_)\n        except Exception as e:\n            print(\"Unable to get the news\", e)\n        time.sleep(120)  # Sends news in every 2 min\n"
  },
  {
    "path": "wplay/message_blast.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\nfrom typing import List\n\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils import io\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import logs_path\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def message_blast(target: str):\n    \"\"\"\n    Sends n number of messages to the target person.\n    \"\"\"\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    if target is not None:\n        await target_search.search_and_select_target_all_ways(page, target)\n    else:\n        await target_select.manual_select_target(page)\n    message: List[str] = io.ask_user_for_message_breakline_mode()\n    number_of_messages: int = int(input(\"Enter the number of messages to blast: \"))\n    __logger.debug(\"Blasting messages\")\n    for _ in range(number_of_messages):\n        await io.send_message(page, message)\n"
  },
  {
    "path": "wplay/message_service.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\nimport threading\nimport time\nimport json\n\nfrom wplay.utils import browser_config\nfrom wplay.utils.target_search import search_target_by_number\nfrom wplay.utils import target_select\nfrom wplay.utils import io\nfrom wplay.utils import helpers\nfrom wplay.utils import verify_internet\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.MessageStack import MessageStack\n# endregion\n\n\n# region LOGGER\nimport logging\n__logger = Logger(Path(__file__).name, logging.DEBUG)\n# endregion\n\n\n\"\"\"\nMessages file structure, your program should add messages this way\ninside the file \"messages.json\" located at user/wplay/messagesJSON folder.\n\n{\n    \"messages\": [\n        {\n            \"uuid\": \"33bf7c667f8011ea96971c3947562893\",\n            \"number\": \"5562999999999\",\n            \"message\": \"*Bold Hello World*\"\n        },\n        {\n            \"uuid\": \"46ca6d284f8058ee89354e2987862869\",\n            \"number\": \"5562888888888\",\n            \"message\": [\"Hello!!!\",\"Multi-line\"]\n        }\n    ]\n}\n\"\"\"\n\n\nasync def message_service():\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    __logger.info(\"Message Service On.\")\n    print(\"Message Service is ON, press CTRL+C to stop.\")\n    print(\"Listening for messages in file 'messages.json' inside user/wplay/messagesJSON folder.\")\n    # Initialize a instance of MessageStack\n    message_stack = MessageStack()\n    # Move all messages from open_messages.json to messages.json when the program starts\n    message_stack.move_all_messages(helpers.open_messages_json_path, helpers.messages_json_path)\n\n    while True:\n        if verify_internet.internet_avalaible():\n            try:\n                # Try to get the message\n                current_msg = next(message_stack.get_message())\n\n                # Move message from messages.json to open_messages.json\n                message_stack.move_message(helpers.messages_json_path, helpers.open_messages_json_path, current_msg['uuid'])\n\n                try:\n                    if await search_target_by_number(page, current_msg['number']):\n                        await io.send_message(page, current_msg['message'])\n                    message_stack.remove_message(current_msg['uuid'], helpers.open_messages_json_path)\n                except ValueError:\n                    __logger.debug(\"Wrong JSON Formatting. Message Deleted.\")\n                    message_stack.remove_message(current_msg['uuid'], helpers.open_messages_json_path)\n                except Exception as e:\n                    # If any error occurs that is not because of wrong data,\n                    # the message will be moved back to messages.json\n                    __logger.error(f'Error handling and sending the message: {str(e)}')\n                    MessageStack().move_message(helpers.open_messages_json_path, helpers.messages_json_path, current_msg['uuid'])\n            except (StopIteration, json.JSONDecodeError):\n                # if there are no messages to catch we will have this 'Warning', will try again after a time\n                time.sleep(1)\n        else:\n            __logger.debug('Internet is not available, trying again after 15 seconds.')\n            time.sleep(15)\n\n        # Move messages from open_messages.json to messages.json that wasn't sended.\n        message_stack.move_all_messages(helpers.open_messages_json_path, helpers.messages_json_path)\n"
  },
  {
    "path": "wplay/message_timer.py",
    "content": "# region IMPORTS\nimport time\nimport random\nfrom pathlib import Path\n\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils import io\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import logs_path\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def message_timer(target):\n    \"\"\"\n    Sends message in a particular time interval.\n    \"\"\"\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    if target is not None:\n        try:\n            await target_search.search_and_select_target(page, target)\n        except Exception as e:\n            print(e)\n            await page.reload()\n            await target_search.search_and_select_target_without_new_chat_button(page, target)\n    else:\n        await target_select.manual_select_target(page)\n    # Region inputs\n    __logger.info(\"Input message information for message timer\")\n    message_type_numbers: int = int(input(\"How many types of messages will you send? \"))\n    messages: list[str] = list()\n    for _ in range(message_type_numbers):\n        messages.append(io.ask_user_for_message_breakline_mode())\n    number_of_messages: int = int(input(\"Enter the number of messages to send: \"))\n    minimumTimeInterval: int = int(input(\"Enter minimum interval number in seconds: \"))\n    maximumTimeInterval: int = int(input(\"Enter maximum interval number in seconds: \"))\n    # Endregion\n\n    random.seed()\n    for _ in range(number_of_messages):\n        if not messages:\n            break\n        await io.send_message(page, messages[random.randrange(0, message_type_numbers)])\n        if minimumTimeInterval != maximumTimeInterval:\n            time.sleep(random.randrange(minimumTimeInterval, maximumTimeInterval))\n        else:\n            time.sleep(minimumTimeInterval)\n"
  },
  {
    "path": "wplay/online_tracker.py",
    "content": "import time\nfrom pathlib import Path\nfrom datetime import datetime\nfrom playsound import playsound\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils import target_data\nfrom wplay.utils.helpers import tracking_folder_path\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import logs_path\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def tracker(target):\n    \"\"\"\n    This function checks the online and offline status of the target person.\n    \"\"\"\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()  # open bot browser and load whatsapp web website\n    if target is not None:  # checks if target is not none then it search for the target and select it\n        try:\n            target_name = await target_search.search_and_select_target(page, target, hide_groups=True)\n        except Exception as e:\n            print(e)\n            await page.reload()\n            target_name = await target_search.search_and_select_target_without_new_chat_button(page, target, hide_groups=True)\n    else:  # if target is none then it allow user to select target manually from browser\n        target_name = await target_select.manual_select_target(page, hide_groups=True)\n\n    # opens status file of the target person\n    status_file: str = open(tracking_folder_path / f'status_{target_name}.txt', 'w').close()\n    status_file: str = open(tracking_folder_path / f'status_{target_name}.txt', 'a')\n\n    # default assignes\n    is_sound_enabled: bool = True\n    last_status: str = 'offline'\n    try:\n        print(f'Tracking: {target_name}')\n        __logger.info(\"Tracking target\")\n        status_file.write(f'Tracking: {target_name}\\n')\n        while True:\n            status: str = await target_data.get_last_seen_from_focused_target(page)  # checks last seen\n            if status == 'online':  # if last seen is online then shows online\n                is_online: bool = True\n            else:  # if nothing is there so shows offline\n                is_online: bool = False\n                status: str = 'offline'\n            # play a notification sound on online\n            if last_status != is_online:\n                if is_online:\n                    try:\n                        if is_sound_enabled:\n                            playsound('plucky.wav')\n                    except Exception as e:\n                        print(\"Error: Couldn't play the sound.\")\n                        is_sound_enabled: bool = False\n                print(\n                    f'{datetime.now().strftime(\"%d/%m/%Y, %H:%M:%S\")}' + f' - Status: {status}'\n                )\n                status_file.write(\n                    f'{datetime.now().strftime(\"%d/%m/%Y, %H:%M:%S\")}' + f' - Status: {status}\\n')\n            last_status: str = is_online\n            time.sleep(0.5)\n    except KeyboardInterrupt:\n        __logger.error(\"User Pressed Ctrl+C\")\n    finally:\n        # save the status and close the file\n        status_file.close()\n        print(f'\\nStatus file saved in: {str(tracking_folder_path/\"status_\")}{target_name}.txt')\n"
  },
  {
    "path": "wplay/profile_download.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\n\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils.helpers import profile_photos_path\nfrom wplay.utils.helpers import whatsapp_selectors_dict\nfrom wplay.utils.Logger import Logger\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def get_profile_picture(target):\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n\n    if target is not None:\n        try:\n            await target_search.search_and_select_target(page, target)\n        except Exception as e:\n            print(e)\n            await page.reload()\n            await target_search.search_and_select_target_without_new_chat_button(page, target)\n    else:\n        target = await target_select.manual_select_target(page)\n    # Getting Profile picture url\n    selector = '#main > header > div > div > img'\n    await page.waitForSelector(selector, timeout=2000)\n    image_url = await page.evaluate(f'document.querySelector(\"{selector}\").getAttribute(\"src\")')\n    try:\n        viewSource = await page.goto(image_url)\n        f = open(profile_photos_path / f'{target}.jpg', 'wb')\n        f.write(await viewSource.buffer())\n        f.close()\n    except Exception as e:\n        print(\"Error saving image\")\n"
  },
  {
    "path": "wplay/save_chat.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\n\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils.helpers import save_chat_folder_path\nfrom wplay.utils.Logger import Logger\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def save_chat(target):\n    \"\"\"\n    Save the whole chat of the target person in .txt file.\n    \"\"\"\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n\n    if target is not None:\n        try:\n            await target_search.search_and_select_target(page, target)\n        except Exception as e:\n            print(e)\n            await page.reload()\n            await target_search.search_and_select_target_without_new_chat_button(page, target)\n    else:\n        target = await target_select.manual_select_target(page)\n\n    # selectors\n    selector_values = \"#main > div > div > div > div > div > div > div > div\"\n    selector_sender = \"#main > div > div > div > div > div > div > div > div > div.copyable-text\"\n\n    # Getting all the messages of the chat\n    try:\n        __logger.info(\"Saving chats with target\")\n        await page.waitForSelector(selector_values)\n        values = await page.evaluate(f'''() => [...document.querySelectorAll('{selector_values}')]\n                                                    .map(element => element.textContent)''')\n        sender = await page.evaluate(f'''() => [...document.querySelectorAll('{selector_sender}')]\n                                                    .map(element => element.getAttribute(\"data-pre-plain-text\"))''')\n\n        final_values = [x[:-8] for x in values]\n        new_list = [a + b for a, b in zip(sender, final_values)]\n\n        # opens chat file of the target person\n        with open(save_chat_folder_path / f'chat_{target}.txt', 'w') as output:\n            for s in new_list:\n                output.write(\"%s\\n\" % s)\n\n    except Exception as e:\n        print(e)\n\n    finally:\n        # save the chat and close the file\n        output.close()\n        print(f'\\nChat file saved in: {str(save_chat_folder_path/\"chat_\")}{target}.txt')\n"
  },
  {
    "path": "wplay/schedule_message.py",
    "content": "# region IMPORTS\nfrom datetime import datetime\nfrom pathlib import Path\nimport time\nimport sys\n\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils import io\nfrom wplay.utils.Logger import Logger\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def schedule_message(target):\n    \"\"\"\n    Sends message to the target person at a scheduled time.\n    \"\"\"\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n    if target is not None:\n        await target_search.search_and_select_target(page, target)\n    else:\n        await target_select.manual_select_target(page)\n    time_ = input(\"Enter the schedule time in HH:MM:SS format-> \")\n    hour, minute, second = time_.split(':')\n    current_time = datetime.now()\n    delta_hour: int = int(hour) - current_time.hour\n    delta_min: int = int(minute) - current_time.minute\n    delta_second: int = int(second) - current_time.second\n    total_seconds: int = delta_hour*3600 + delta_min*60 + delta_second\n    if total_seconds < 0:\n        print(\"Current time is ahead of the scheduled time\")\n        sys.exit()\n    message: list[str] = io.ask_user_for_message_breakline_mode()\n    print(\"Your message is scheduled at : \", time_)\n    time.sleep(total_seconds)\n    await io.send_message(page, message)\n"
  },
  {
    "path": "wplay/settings.cfg",
    "content": "[auth]\ngmail = alias@gmail.com\npassw = yourpassword\ndevid = 0000000000000000\n\n[app]\npkg = com.whatsapp\nsig = 38a0f7d505fe18fec64fbf343ecaaaf310dbd799\n\n[client]\npkg = com.google.android.gms\nsig = 38918a453d07199354f8b19af05ec6562ced5788\nver = 9877000"
  },
  {
    "path": "wplay/target_info.py",
    "content": "# region IMPORTS\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nimport phonenumbers\nfrom phonenumbers import carrier\nfrom phonenumbers import geocoder\nfrom phonenumbers import timezone\nimport re\nimport sys\nfrom pathlib import Path\nfrom wplay.utils.Logger import Logger\n# end IMPORTS\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\ndef formatNumber(InputNumber):\n    return re.sub(r\"(?:\\+)?(?:[^[0-9]*)\", \"\", InputNumber)\n\n\ndef localScan(InputNumber, print_results=True):\n    print(\"Running local scan...\")\n\n    FormattedPhoneNumber = \"+\" + formatNumber(InputNumber)\n\n    try:\n        PhoneNumberObject = phonenumbers.parse(FormattedPhoneNumber, None)\n    except Exception as e:\n        print(e)\n    else:\n        if not phonenumbers.is_valid_number(PhoneNumberObject):\n            return False\n\n        number = phonenumbers.format_number(PhoneNumberObject, phonenumbers.PhoneNumberFormat.E164).replace(\"+\", \"\")\n        numberCountryCode = phonenumbers.format_number(PhoneNumberObject, phonenumbers.PhoneNumberFormat.INTERNATIONAL).split(\" \")[0]\n        numberCountry = phonenumbers.region_code_for_country_code(int(numberCountryCode))\n\n        localNumber = phonenumbers.format_number(PhoneNumberObject, phonenumbers.PhoneNumberFormat.E164).replace(numberCountryCode, \"\")\n        internationalNumber = phonenumbers.format_number(PhoneNumberObject, phonenumbers.PhoneNumberFormat.INTERNATIONAL)\n\n        country = geocoder.country_name_for_number(PhoneNumberObject, \"en\")\n        location = geocoder.description_for_number(PhoneNumberObject, \"en\")\n        carrierName = carrier.name_for_number(PhoneNumberObject, \"en\")\n\n        if print_results:\n            print(\"International format: {}\".format(internationalNumber))\n            print(\"Local format: {}\".format(localNumber))\n            print(\"Country found: {} ({})\".format(country, numberCountryCode))\n            print(\"City/Area: {}\".format(location))\n            print(\"Carrier: {}\".format(carrierName))\n            for timezoneResult in timezone.time_zones_for_number(PhoneNumberObject):\n                print(\"Timezone: {}\".format(timezoneResult))\n\n            if phonenumbers.is_possible_number(PhoneNumberObject):\n                print(\"The number is valid and possible.\")\n            else:\n                print(\"The number is valid but might not be possible.\")\n\n    numberObj = {}\n    numberObj[\"input\"] = InputNumber\n    numberObj[\"default\"] = number\n    numberObj[\"local\"] = localNumber\n    numberObj[\"international\"] = internationalNumber\n    numberObj[\"country\"] = country\n    numberObj[\"countryCode\"] = numberCountryCode\n    numberObj[\"countryIsoCode\"] = numberCountry\n    numberObj[\"location\"] = location\n    numberObj[\"carrier\"] = carrierName\n\n    return numberObj\n\n\ndef scanNumber(InputNumber):\n    print(\"[!] ---- Fetching informations for {} ---- [!]\".format(formatNumber(InputNumber)))\n    number = localScan(InputNumber)\n\n    if not number:\n        print((\"Error: number {} is not valid. Skipping.\".format(formatNumber(InputNumber))))\n        sys.exit()\n\n    print(\"Scan finished.\")\n\n\ndef target_contact_number(num):\n    target_contact_number.phone_number = num\n\n\nasync def target_info(target):\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n\n    if target is not None:\n        try:\n            await target_search.search_and_select_target(page, target)\n        except Exception as e:\n            print(e)\n            await page.reload()\n            await target_search.search_and_select_target_without_new_chat_button(page, target)\n    else:\n        await target_select.manual_select_target(page)\n    \"\"\"\n    # to find location by ip address\n    print('Get you ipinfo token from https://ipinfo.io/account')\n    ip_address = '*'\n    token = str(input(\"Enter your ipinfo token: \"))\n    ip_string = 'curl ipinfo.io/'+ip_address+'?token='+token+''\n    os.system(ip_string)\n    \"\"\"\n    __logger.info(\"Writing target's information\")\n    scanNumber(target_contact_number.phone_number)\n"
  },
  {
    "path": "wplay/telegram_bot.py",
    "content": "# region IMPORTS\nimport tkinter\nfrom tkinter import filedialog\nfrom pathlib import Path\nimport pickle\n\nfrom telegram.ext import CommandHandler, Updater\nfrom wplay.utils.helpers import data_folder_path\nfrom wplay.utils.Logger import Logger\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nstatus_file_path = None\n\n\ndef start_tkinter():\n    root_window = tkinter.Tk()\n    root_window.withdraw()\n\n\ndef ask_where_are_the_status_file():\n    print('Choose a status text file.')\n    status_file_path = filedialog.askopenfile(\n        initialdir=data_folder_path / 'tracking_data',\n        title='Choose a status text file.',\n        filetypes=((\"text files\", \"*.txt\"), (\"all files\", \"*.*\"))\n    )\n    if status_file_path == ():\n        print(\"Error! Choose a status.\")\n        exit()\n    return status_file_path\n\n\ndef startmessage(bot, update):\n    chat_id: int = update.message.chat_id\n    text: str = '''\n        Hi, I am here to send all tracked online status in whatsapp :)\n    '''\n    bot.send_message(chat_id=chat_id, text=text)\n\n\ndef send_status(bot, update):\n    # Display last updated online status message\n    chat_id = update.message.chat_id\n    try:\n        f = open(status_file_path, 'r')\n        file_data = f.readlines()\n        text: Union[str, bytes] = file_data[len(file_data) - 1]\n        bot.send_message(chat_id=chat_id, text=text)\n    except Exception as e:\n        print(e)\n        bot.send_message(chat_id=chat_id, text='oops! An error occurred')\n\n\ndef telegram_status(name):\n    print(name)\n    start_tkinter()\n    global status_file_path\n    status_file_path = ask_where_are_the_status_file()\n    # Add bot token\n    global TOKEN\n    new_token = False\n    token_file_path = \"wplay/telegram_token.pkl\"\n    if Path(token_file_path).exists():\n        user_choice = input(\"Do you want to use last saved token (Y) or enter new token (N): \")\n        if user_choice in \"Yy\":\n            with open(token_file_path, \"rb\") as token_file:\n                TOKEN = pickle.load(token_file)\n        else:\n            new_token = True\n    else:\n        new_token = True\n    if new_token:\n        TOKEN = input(\"Enter token: \")\n        with open(token_file_path, \"wb\") as token_file:\n            pickle.dump(TOKEN, token_file)\n\n    # Added all the essential command handlers\n    updater = Updater(TOKEN, use_context=True)\n    dp = updater.dispatcher\n    dp.add_handler(CommandHandler('start', startmessage))\n    dp.add_handler(CommandHandler('status', send_status))\n    updater.start_polling()\n    updater.idle()\n"
  },
  {
    "path": "wplay/terminal_chat.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\n\nfrom wplay.utils import browser_config\nfrom wplay.utils import target_search\nfrom wplay.utils import target_select\nfrom wplay.utils import io\nfrom wplay.chat_intermediator import intermediary\nfrom wplay import text_to_speech\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import logs_path\nfrom colorama import Fore, Style\nfrom wplay import chatbot\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def chat(target):\n    __logger.info(\"Chatting with target\")\n    page, _ = await browser_config.configure_browser_and_load_whatsapp()\n\n    if target is not None:\n        try:\n            await target_search.search_and_select_target(page, target)\n        except Exception as e:\n            print(e)\n            await page.reload()\n            await target_search.search_and_select_target_without_new_chat_button(page, target)\n    else:\n        target = await target_select.manual_select_target(page)\n\n    # selectors\n    selector_values = \"#main > div > div > div > div > div > div > div > div\"\n    selector_sender = \"#main > div > div > div > div > div > div > div > div > div.copyable-text\"\n\n    # Getting all the messages of the chat\n    try:\n        __logger.info(\"Printing recent chat\")\n        await page.waitForSelector(selector_values)\n        values = await page.evaluate(f'''() => [...document.querySelectorAll('{selector_values}')]\n                                                    .map(element => element.textContent)''')\n        sender = await page.evaluate(f'''() => [...document.querySelectorAll('{selector_sender}')]\n                                                    .map(element => element.getAttribute(\"data-pre-plain-text\"))''')\n        new_values = [x[:-8] for x in values]\n        new_list = [a + b for a, b in zip(sender, new_values)]\n        final_list = new_list[-6:]\n        for s in final_list:\n            print(\"%s\\n\" % s)\n    except Exception as e:\n        print(e)\n\n    print(\"\\033[91m {}\\033[00m\".format(\"\\nType '...' in a new line or alone in the message to change target person.\\nType '#_FILE' to send Image/Video/Documentd etc.\\nType '#_TTS' to convert text to speech and send audio file.\\nType '#_FWD' to foward your last message received\"))\n\n    while True:\n        await getMessages(page, target)\n        message: list[str] = io.ask_user_for_message_breakline_mode()\n\n        # Target Change\n        if \"...\" in message:\n            message.remove('...')\n            await io.send_message(page, message)\n            target = input(\"\\n\\nNew Target Name: \")\n            if target is not None:\n                await target_search.search_and_select_target(page, target)\n            else:\n                await target_select.manual_select_target(page)\n\n        # Be an Intermediator\n        if \"#_FWD\" in message:\n            await target_search.search_and_select_target(page, intermediary.rec)\n            await io.send_message(page, getMessages.foward_message)\n            message = io.ask_user_for_message_breakline_mode()\n\n        # Text to speech\n        if \"#_TTS\" in message:\n            await text_to_speech.text_to_speech(target)\n            await io.send_file(page)\n\n        # File Share:\n        if \"#_FILE\" in message:\n            message.remove(\"#_FILE\")\n            await io.send_file(page)\n\n        await getMessages(page, target)\n        await io.send_message(page, message)\n\n\nasync def getMessages(page, target):\n    \"\"\"\n    Get the last messages of the chats.\n    \"\"\"\n    # selectors\n    selector_values = \"#main > div > div > div > div > div > div > div > div\"\n    selector_sender = \"#main > div > div > div > div > div > div > div > div > div.copyable-text\"\n    try:\n        # Getting all the messages of the chat\n        await page.waitForSelector(selector_values)\n        values = await page.evaluate(f'''() => [...document.querySelectorAll('{selector_values}')]\n                                                    .map(element => element.textContent)''')\n        sender = await page.evaluate(f'''() => [...document.querySelectorAll('{selector_sender}')]\n                                                    .map(element => element.getAttribute(\"data-pre-plain-text\"))''')\n        lastMessage = values[-1]\n        last_message_time = sender[-1].split(',')\n        last_message_time = last_message_time[0].replace('[', '')\n        lastMessage = lastMessage.replace(last_message_time, '')\n    except Exception as e:\n        print(e)\n        lastMessage = \"\"\n    lastOutgoingMessage = \"\"\n    if lastOutgoingMessage != lastMessage:\n        print(Fore.GREEN + f\"{target}-\", end=\"\")\n        print(lastMessage, end=\"\")\n        print(Style.RESET_ALL)\n        getMessages.foward_message = lastMessage\n        if \"/image\" in lastMessage:\n            bot_msg = await chatbot.Bot(last_Message=lastMessage)\n            await io.send_message(page, bot_msg)\n            await io.send_file(page)\n        elif lastMessage[0] == \"/\":\n            bot_msg = await chatbot.Bot(last_Message=lastMessage)\n            await io.send_message(page, bot_msg)\n    lastOutgoingMessage = lastMessage\n"
  },
  {
    "path": "wplay/text_to_speech.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\n\nfrom wplay.utils.helpers import audio_file_folder_path\nfrom wplay.utils.Logger import Logger\n\nfrom gtts import gTTS\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\nasync def text_to_speech(target):\n\n    try:\n        __logger.info(\"Converting text to speech audio file\")\n        # The text that you want to convert to audio\n        text = input(\"\\n\\nWrite the text you want to convert to audio file: \")\n\n        list_laguages = ['bn: Bengali', 'de: German', 'en: English', 'es: Spanish', 'fr: French', 'gu: Gujarati', 'hi: Hindi',\n        'it: Italian', 'ja: Japanese', 'kn: Kannada', 'ko: Korean', 'ml: Malayalam', 'mr: Marathi', 'pt-br: Portuguese (Brazil)', 'ru: Russian', 'ta: Tamil', 'te: Telugu', 'ur: Urdu']\n\n        print('Choose a code for language of your choice from the following list\\n')\n        print(list_laguages)\n\n        # Language in which you want to convert\n        language = input(\"\\n\\nEnter the language you want to convert to audio file: \")\n\n        # Passing the text and language to the engine,\n        myobj = gTTS(text=text, lang=language, slow=False)\n        # Saving the converted audio in a mp3 file named\n        myobj.save(audio_file_folder_path / \"{}.mp3\".format(target))\n\n    except Exception as e:\n        print(e)\n\n    finally:\n        print('\\nAudio file saved in: {}/ {}.mp3'.format(audio_file_folder_path, target))\n"
  },
  {
    "path": "wplay/utils/Logger.py",
    "content": "# region IMPORTS\nimport logging\nfrom pathlib import Path\nfrom wplay.utils.helpers import log_file_path, logs_path, test_log_file_path\n# endregion\n\n\nclass Logger:\n    if not (log_file_path).exists():\n        logs_path.mkdir(parents=True, exist_ok=True)\n        open(log_file_path, 'w').close()\n\n    if not (test_log_file_path).exists():\n        logs_path.mkdir(parents=True, exist_ok=True)\n        open(test_log_file_path, 'w').close()\n\n    def __init__(self, script_name: str, level: int = logging.WARNING):\n        self.logger = logging.getLogger(script_name)\n        self.level = level\n        self.logger.setLevel(self.level)\n\n        if not self.logger.handlers:\n            # Create handlers\n            file_handler = logging.FileHandler(log_file_path)\n            file_handler = logging.FileHandler(test_log_file_path)\n            console = logging.StreamHandler()\n            file_handler.setLevel(self.level)\n            console.setLevel(self.level)\n\n            # create formatter and add it to the handlers\n            formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')\n            console.setFormatter(formatter)\n            file_handler.setFormatter(formatter)\n\n            # add the handlers to logger\n            self.logger.addHandler(console)\n            self.logger.addHandler(file_handler)\n\n    def debug(self, msg: str):\n        self.logger.debug(msg)\n\n    def error(self, msg: str):\n        self.logger.error(msg)\n\n    def info(self, msg: str):\n        self.logger.info(msg)\n"
  },
  {
    "path": "wplay/utils/MessageStack.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\nfrom typing import List, Iterator\nimport json\n\nfrom wplay.utils import helpers\nfrom wplay.utils.Logger import Logger\n# endregion\n\n\nclass MessageStack():\n    def __init__(self):\n        self.logger = Logger(Path(__file__).name)\n        self.__create_json_file(helpers.messages_json_path)\n        self.__ensure_valid_json(helpers.messages_json_path)\n        self.__create_json_file(helpers.open_messages_json_path)\n        self.__ensure_valid_json(helpers.open_messages_json_path)\n\n    def __create_json_file(self, file_path: Path):\n        if not file_path.is_file():\n            open(file_path, 'w').close()\n            self.logger.info(f'{file_path.name} created.')\n\n    def __write_json(self, data: dict, file_path: Path):\n        with open(file_path, \"w\") as json_file:\n            json.dump(data, json_file, indent=4)\n\n    def __ensure_valid_json(self, file_path: Path):\n        valid_data = {\"messages\": list()}\n        try:\n            with open(file_path) as json_file:\n                data = json.load(json_file)\n                if 'messages' not in data:\n                    self.__write_json(valid_data, file_path)\n        except json.JSONDecodeError:\n            # Empty or Invalid Json\n            self.__write_json(valid_data, file_path)\n\n    def append_message(self, message: dict, file_path: Path):\n        \"\"\"\n        Append messages into json file.\n\n        Arguments:\n            message -- dict with a message\n            file_path {Path} -- open_messages_json_path or messages_json_path from helpers\n        \"\"\"\n        self.__ensure_valid_json(file_path)\n\n        with open(file_path) as json_file:\n            json_data = json.load(json_file)\n        json_data['messages'].append(message)\n\n        self.__write_json(json_data, file_path)\n        self.logger.info(f'Message appended to {file_path.name}')\n\n    def get_message(\n            self,\n            from_file_path: Path = helpers.messages_json_path) -> Iterator[dict]:\n        \"\"\"\n        Yield a message from a file.\n\n        Arguments:\n            from_file_path {Path} -- open_messages_path or messages_path from helpers\n\n        Exception:\n            raise StopIteration, json.JSONDecodeError, KeyError if file is empty, or the iteration stopped or the key isn't finded.\n\n        Yields:\n            [dict] -- Yield a dict with all message data\n        \"\"\"\n        with open(from_file_path) as json_file:\n            data = json.load(json_file)\n            for message in data['messages']:\n                yield message\n\n    def get_all_messages(\n            self,\n            from_file_path: Path = helpers.messages_json_path) -> List[dict]:\n        self.__ensure_valid_json(from_file_path)\n        with open(from_file_path) as json_file:\n            data = json.load(json_file)\n            return data['messages']\n\n    def move_message(self, from_file_path: Path, to_file_path: Path, uuid: str):\n        with open(from_file_path) as json_file:\n            data = json.load(json_file)\n            for message in data['messages']:\n                if uuid in message['uuid']:\n                    self.append_message(message, to_file_path)\n                    self.remove_message(uuid, from_file_path)\n\n    def move_all_messages(self, from_file_path: Path, to_file_path: Path):\n        self.__ensure_valid_json(from_file_path)\n        with open(from_file_path) as json_file:\n            data = json.load(json_file)\n            for message in data['messages']:\n                self.append_message(message, to_file_path)\n                self.remove_message(message['uuid'], from_file_path)\n\n    def remove_message(self, uuid: str, file_path: Path):\n        with open(file_path) as json_file:\n            data = json.load(json_file)\n            for i, message in enumerate(data['messages']):\n                if uuid in message['uuid']:\n                    del data['messages'][i]\n                    self.__write_json(data, file_path)\n                    self.logger.info(f\"Message Deleted Successfully from {file_path.name}\")\n\n    def remove_all_messages(self, file_path: Path):\n        with open(file_path) as json_file:\n            data = json.load(json_file)\n            for i, message in enumerate(data['messages']):\n                del data['messages'][i]\n                self.__write_json(data, file_path)\n            self.logger.info(f\"Deleted Successfully all messages from {file_path.name}\")\n"
  },
  {
    "path": "wplay/utils/SessionManager.py",
    "content": "# region Imports\nimport os\nimport stat\nimport shutil\nfrom pathlib import Path\n\nfrom whaaaaat import Separator, prompt\nfrom transitions import Machine, State\n\nfrom wplay.utils.helpers import user_data_folder_path\nfrom wplay.utils.helpers import menu_style\n# endregion\n\n\nstates = [\n    State(name='start'),\n    State(name='get_user_data_filenames', on_enter='get_user_data_filenames'),\n    State(name='prepare_questions', on_enter='prepare_questions'),\n    State(name='get_answer_menu', on_enter='get_answer_menu'),\n    State(name='verify_answers', on_enter='verify_answers')]\n\ntransitions = [\n    {'trigger': 'start', 'source': '*', 'dest': 'start'},\n    {'trigger': 'get_user_data_filenames', 'source': 'start', 'dest': 'get_user_data_filenames'},\n    {'trigger': 'prepare_questions', 'source': 'get_user_data_filenames', 'dest': 'prepare_questions'},\n    {'trigger': 'get_answer_menu', 'source': 'prepare_questions', 'dest': 'get_answer_menu'},\n    {'trigger': 'verify_answers', 'source': 'get_answer_menu', 'dest': 'verify_answers'}]\n\n\nclass SessionManager(object):\n    def __init__(self):\n        self.data_filenames = None  # type : list\n        self.questions_menu = None  # type : list\n        self.question_overwrite = None  # type : list\n        self.answers_menu = None  # type : dict\n        self.username = None  # type : str\n        self.save_session = False  # type : bool\n        self.user_options = {\n            'restore': 'Restore a session',\n            'save': 'Create a new session',\n            'continue': 'Continue without saving',\n            'delete': 'Delete a session',\n            'exit': 'Exit'\n        }    # type : dict\n\n    def reset_fields(self):\n        self.data_filenames = None  # type : list\n        self.questions_menu = None  # type : list\n        self.question_overwrite = None  # type : list\n        self.answers_menu = None  # type : dict\n        self.username = None  # type : str\n        self.save_session = False  # type : bool\n\n    def get_user_data_filenames(self):\n        self.data_filenames = [file.stem for file in user_data_folder_path.glob('*')]\n\n    def prepare_questions(self):\n        self.questions_menu = [\n            {\n                'type': 'rawlist',\n                'name': 'user_options',\n                'message': '***Session Manager***:',\n                'choices': [\n                    Separator(),\n                    self.user_options['restore'],\n                    self.user_options['save'],\n                    self.user_options['continue'],\n                    Separator(),\n                    self.user_options['delete'],\n                    self.user_options['exit'],\n                    Separator()\n                ]\n            },\n            {\n                'type': 'rawlist',\n                'name': 'restore',\n                'message': 'Select a session to try to restore:',\n                'choices': [*[session for session in self.data_filenames], '<---Go-back---'],\n                'when': lambda answers: answers['user_options'] == self.user_options['restore']\n            },\n            {\n                'type': 'input',\n                'name': 'save',\n                'message': 'Write your first name or username to save:',\n                'when': lambda answers: answers['user_options'] == self.user_options['save']\n            },\n            {\n                'type': 'checkbox',\n                'name': 'delete',\n                'message': 'Mark the sessions you want to delete:',\n                'choices': list(map(lambda e: {'name': e}, self.data_filenames)),\n                'when': lambda answers: answers['user_options'] == self.user_options['delete']\n            }\n        ]\n\n        self.question_overwrite = [\n            {\n                'type': 'confirm',\n                'name': 'overwrite_data',\n                'message': 'There is already a session with that name, overwrite it?',\n                'default': True\n            }\n        ]\n\n    def get_answer_menu(self):\n        self.answers_menu = prompt(self.questions_menu, style=menu_style)\n\n    def verify_answers(self):\n        # Handle when person choose 'Restore a session'\n        if self.answers_menu['user_options'] == self.user_options['restore']:\n            if self.answers_menu['restore'] == '<---Go-back---':\n                return False\n            else:\n                self.username = self.answers_menu['restore']\n                self.save_session = True\n                return True\n\n        # Handle when person choose 'Create a new session'\n        elif self.answers_menu['user_options'] == self.user_options['save']:\n            self.username = self.answers_menu['save']\n            self.save_session = True\n            return self.__verify_if_session_file_exists()\n\n        # Handle when person choose 'Continue without saving'\n        elif self.answers_menu['user_options'] == self.user_options['continue']:\n            self.username, self.save_session = None, False\n            return True\n\n        # Handle when person choose 'Delete a session'\n        elif self.answers_menu['user_options'] == self.user_options['delete']:\n            if len(self.answers_menu['delete']) > 0:\n                [self.__delete_session_data(user_data_folder_path / username) for username in self.answers_menu['delete']]\n            return False\n\n        # Handle when person choose 'Exit'\n        elif self.answers_menu['user_options'] == self.user_options['exit']:\n            exit()\n\n    def __verify_if_session_file_exists(self) -> bool:\n        if self.username in self.data_filenames:\n            answer_overwrite = prompt(self.question_overwrite, style=menu_style)\n            if answer_overwrite['overwrite_data']:\n                self.__delete_session_data(user_data_folder_path / self.username)\n            return answer_overwrite['overwrite_data']\n        return True\n\n    def __delete_session_data(self, path):\n        def handleError(func, path, exc_info):\n            print('Handling Error for file ', path)\n            if not os.access(path, os.W_OK):\n                print('Trying to change permission!')\n                os.chmod(path, stat.S_IWUSR)\n                shutil.rmtree(path, ignore_errors=True)\n        shutil.rmtree(path, onerror=handleError)\n\n    @staticmethod\n    def session_manager():\n        done = False\n        obj = SessionManager()\n        while(not done):\n            if obj.questions_menu is not None:\n                obj.reset_fields()\n            machine = Machine(obj, states, transitions=transitions, initial='start')\n            obj.get_user_data_filenames()\n            obj.prepare_questions()\n            obj.get_answer_menu()\n            done = obj.verify_answers()\n\n        return obj.username, obj.save_session\n"
  },
  {
    "path": "wplay/utils/TODO",
    "content": "#TODO all code: return browser.close and use it instead use exit\n\n#TODO target_search: Add a whaaaaaat menu\n#FIXME target_search: False positive group -> Groups name use the same div as contact status,\n        so we need to verify if target name is in title, not in status. But, \n        sometimes the status contain the target name and shows up as \n        false-positive group. \n        WHERE WE CAN FIX? __checking_group_list\n\n\n#TODO io: Wait for the last message to be sent before closing the browser"
  },
  {
    "path": "wplay/utils/__init__.py",
    "content": ""
  },
  {
    "path": "wplay/utils/browser_config.py",
    "content": "# region TUTORIAL\n'''\nGo to region 'FOR SCRIPTING' and use the methods in your script!\n\nEXAMPLE OF USAGE:\nfrom wplay.pyppeteerUtils import pyppeteerConfig as pypConfig\nfrom wplay.pyppeteerUtils import pyppeteerSearch as pypSearch\n\nasync def my_script(target):\n    pages, browser = wait pyp.configure_browser_and_load_whatsapp(\n    pypConfig.websites['whatsapp']\n    )\n    await pypSearch.search_for_target_and_get_ready_for_conversation(\n    pages[0],\n    target\n    )\n\n    message = pypSearch.ask_user_for_message_breakline_mode()\n    await pypSearch.send_message(pages[0], message)\n\n    message2 = pypSearch.ask_user_for_message()\n    await pypSearch.send_message(pages[0], message2)\n\n'''\n# endregion\n\n\n# region IMPORTS\nfrom typing import Any, List\nfrom pathlib import Path\n\nimport websockets.client\nfrom pyppeteer import launch, connection, launcher\nfrom pyppeteer.browser import Browser\nfrom pyppeteer.page import Page\n\nfrom wplay.utils.SessionManager import SessionManager\nfrom wplay.utils.helpers import websites, user_data_folder_path\nfrom wplay.utils.Logger import Logger\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\n# region FOR SCRIPTING\nasync def configure_browser_and_load_whatsapp() -> (Page, Browser):\n    \"\"\"\n    Configure browser, configure the first page and open whatsapp website.\n\n    Returns:\n        Page -- return the first page, with whatsapp open\n        Browser -- return the browser object\n    \"\"\"\n    __patch_pyppeteer()\n    username, save_session = SessionManager.session_manager()\n    browser = await __config_browser(username, save_session)\n    pages = await get_pages(browser)\n    first_page = pages[0]\n    await config_page(first_page)\n    await load_website(first_page, websites['whatsapp'])\n    return first_page, browser\n\n\nasync def get_pages(browser: Browser) -> List[Page]:\n    __logger.debug('Getting open pages')\n    return await browser.pages()\n\n\nasync def open_new_page(browser: Browser):\n    \"\"\"\n    Open a new tab.\n    \"\"\"\n    __logger.debug('Opening new page(tab)')\n    await browser.newPage()\n\n\nasync def config_page(page: Page):\n    __logger.debug('Configuring page')\n    await __set_user_agent(page)\n    # await __set_view_port(page)\n\n\nasync def load_website(page: Page, website: str):\n    __logger.debug(f'Loading website: {website}')\n    await page.bringToFront()\n    await page.goto(website, waitUntil='networkidle2', timeout=0)\n\n\ndef exit_if_wrong_url(page: Page, browser: Browser, url_to_check: str):\n    if not page.url == url_to_check:\n        __logger.error('Exit due to Wrong URL!')\n        browser.close()\n        exit()\n# endregion\n\n\n# region PYPPETEER PATCH\n# https://github.com/miyakogi/pyppeteer/pull/160\n# HACK: We need this until this PR is accepted. Solves the bug bellow.\n# BUG:(Pyppeteer) The communication with Chromium are disconnected after 20s.\ndef __patch_pyppeteer():\n    __logger.debug(\"Patching Pyppeteer.\")\n\n    class PatchedConnection(connection.Connection):\n        def __init__(self, *args: Any, **kwargs: Any) -> None:\n            super().__init__(*args, **kwargs)\n            self._ws = websockets.client.connect(\n                self._url,\n                loop=self._loop,\n                max_size=None,\n                ping_interval=None,\n                ping_timeout=None,\n            )\n\n    connection.Connection = PatchedConnection\n    launcher.Connection = PatchedConnection\n# endregion\n\n\n# region PYPPETEER PRIVATE FUNCTIONS\nasync def __config_browser(username: str = None, save_session: bool = False):\n    __logger.debug('Configuring Browser.')\n    if username is not None and username.strip() != '' and save_session:\n        return await launch(headless=False, autoClose=False, userDataDir=user_data_folder_path / username)\n    else:\n        return await launch(headless=False, autoClose=False)\n\n\nasync def __set_user_agent(page: Page):\n    await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36')\n\n\nasync def __set_view_port(page: Page):\n    await page.setViewport({'width': 1280, 'height': 800})\n# endregion\n\n\n# region CODE THAT MIGHT BE USEFUL SOMEDAY\n'''\n# FIX:\n# To load websites faster\nasync def intercept(request, page_one, page_two):\n    await page_one.setRequestInterception(True)\n    await page_two.setRequestInterception(True)\n    if any(request.resourceType == _ for _ in ('stylesheet', 'image', 'font', 'media')):\n        await request.abort()\n    else:\n        await request.continue_()\n    page.on('request', lambda req: asyncio.ensure_future(intercept(req)))\n'''\n# endregion\n"
  },
  {
    "path": "wplay/utils/helpers.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\n\nimport signal\nimport psutil\nfrom whaaaaat import style_from_dict, Token\n# endregion\n\n# region Whatsapp WEBSITES\nwebsites = {'whatsapp': 'https://web.whatsapp.com/', 'wpp_unknown': 'https://web.whatsapp.com/send?phone='}\n# endregion\n\n# region SELECTORS\nwhatsapp_selectors_dict = {\n    'login_area': '#app > div > div > div.landing-header',\n\n    'new_chat_button': '#side > header div[role=\"button\"] span[data-icon=\"chat\"]',\n    'search_contact_input_new_chat': '#app > div > div > div > div > span > div > span > div > div > div > label > div > div',\n    'contact_list_elements_filtered_new_chat': '#app > div > div > div > div > span > div > span > div > div > div > div > div > div > div > div > div > div > div > span > span[title][dir]',\n    'group_list_elements_filtered_new_chat': '#app > div > div > div > div > span > div > span > div > div > div > div > div > div > div > div > div > div > div > div > span[title][dir]',\n\n    'search_contact_input': '#side > div > div > label > div > div',\n    'chat_list_elements_filtered': '#pane-side > div > div > div > div > div > div > div > div > div > span > span[title][dir]',\n\n    'target_focused_title': '#main > header div > div > span[title]',\n    'message_area': '#main > footer div.selectable-text[contenteditable]',\n    'last_seen': '#main > header > div > div > span[title]',\n    'target_chat_header': '#main > header',\n    'contact_info_page_elements': '#app > div > div > div:nth-child(2) > div:last-of-type > span > div > span > div > div > div:first-child',\n    'contact_info_page_group_element_heading': '#app > div > div > div:nth-child(2) > div:last-of-type > '\n                                    'span > div > span > div > div:nth-child(5)>div>div>div>div:first-child>span',\n    'contact_info_page_group_elements': '#app > div > div > div:nth-child(2) > div:last-of-type > '\n                                    'span > div > span > div > div:nth-child(5)>div:nth-child(2)>div>div',\n    'contact_info_page_close_button': '#app > div > div > div > div > span > div > span > div > header > div > div > button',\n    'chat_or_message_search': '#side > div:nth-child(3) > div > label > div > div:last-child',\n    'chats_groups_messages_elements': '#side > div:last-child > div > div > div > div',\n    'contact_element': 'span > span > span[class^=\"matched-text\"]',\n    'group_element': 'div:last-child > div:first-child > div:first-child > div > span > span[class^=\"matched-text\"]',\n    'attach_file': '#main > header > div > div > div:nth-child(2) > div',\n    'choose_file': '#main > header > div > div > div > span > div > div > ul > li:nth-child(3) > button',\n    'send_file': '#app > div > div > div > div > span > div > span > div > div > div > span > div > div > span',\n    'profile_photo_element': '#side > header > div > div > img',\n    'about_edit_button_element': '#app > div > div > div > div > span > div > div > div > div:nth-child(4) > div > div > span > div > span',\n    'about_text_area': '#app > div > div > div > div > span > div > div > div > div:nth-child(4) > div > div > div > div',\n    'contact_info_page_target_group_name_element': 'div:nth-child(2)>div>div> div:last-of-type',\n    'contact_info_page_target_group_creation_info_element': ':scope > div:last-child > span',\n    'contact_info_page_target_group_description_element': ':scope > div:last-child span:first-of-type',\n    'contact_info_page_target_group_member_elements': ':scope > div:nth-child(4) > div > div',\n    'invalid_number_ok_button': '#app > div > span> div > span > div > div > div > div > div > div > div',\n    'target_name_selector': \"#main > header > div > div > div > span\",\n    'media_text': \"#app > div > div > div > div > span > div > span > div > div > div > div > div > div > div > div > span\",\n    'media_images': \"#app > div > div > div > div > span > div > span > div > div > span > div > div > div > div > div > div\",\n    'left_arrow_button': \"#app > div > span > div > div > div > div > div > span\",\n    'media_url_img': \"#app > div > span:nth-child(3) > div > div > div > div > div > div > div > div > img\",\n    'media_url_vid': \"#app > div > span:nth-child(3) > div > div > div > div > div > div > div > div > video\",\n}\n# endregion\n\n# region PATHS\ndata_folder_path = Path.home() / 'wplay'\nlogs_path = data_folder_path / 'logs'\nlog_file_path = logs_path / 'wplay.log'\ntest_log_file_path = logs_path / 'testwplay.log'\nuser_data_folder_path = data_folder_path / '.userData'\nprofile_photos_path = data_folder_path / 'media' / 'profilePhotos'\ntracking_folder_path = data_folder_path / 'trackingData'\nmessages_json_folder_path = data_folder_path / 'messagesJSON' / 'system'\nmessages_json_path = data_folder_path / 'messagesJSON' / 'messages.json'\nopen_messages_json_path = data_folder_path / 'messagesJSON' / 'system' / 'openMessages.json'\nmedia_path = data_folder_path / 'media' / 'media'\nsave_chat_folder_path = data_folder_path / 'savedChats'\naudio_file_folder_path = data_folder_path / 'audioFiles'\nchatbot_image_folder_path = data_folder_path / 'ChatbotImage'\n# endregion\n\n# region MENU STYLES\nmenu_style = style_from_dict({\n    Token.Separator: '#6C6C6C',\n    Token.QuestionMark: '#FF9D00 bold',\n    Token.Selected: '#5F819D',\n    Token.Pointer: '#FF9D00 bold',\n    Token.Instruction: '',  # default\n    Token.Answer: '#5F819D bold',\n    Token.Question: '',\n})\n# endregion\n\n\n# region FUNCTIONS\ndef create_dirs():\n    logs_path.mkdir(parents=True, exist_ok=True)\n    user_data_folder_path.mkdir(parents=True, exist_ok=True)\n    profile_photos_path.mkdir(parents=True, exist_ok=True)\n    tracking_folder_path.mkdir(parents=True, exist_ok=True)\n    messages_json_folder_path.mkdir(parents=True, exist_ok=True)\n    media_path.mkdir(parents=True, exist_ok=True)\n    save_chat_folder_path.mkdir(parents = True, exist_ok = True)\n    audio_file_folder_path.mkdir(parents = True, exist_ok = True)\n    tracking_folder_path.mkdir(parents = True, exist_ok = True)\n    messages_json_folder_path.mkdir(parents = True, exist_ok = True)\n    chatbot_image_folder_path.mkdir(parents= True, exist_ok=True)\n\n\ndef kill_child_processes(parent_pid, sig=signal.SIGTERM):\n    try:\n        parent = psutil.Process(parent_pid)\n    except psutil.NoSuchProcess:\n        return\n    children = parent.children(recursive=True)\n    print('Process Killed!')\n    for process in children:\n        process.send_signal(sig)\n# endregion\n"
  },
  {
    "path": "wplay/utils/io.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\nfrom typing import List, Union\n\nfrom pyppeteer.page import Page\n\nfrom wplay.utils.helpers import whatsapp_selectors_dict\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import logs_path\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\n# region FOR SCRIPTING\ndef ask_user_for_message() -> str:\n    return str(input(\"Write your message: \"))\n\n\ndef ask_user_for_message_breakline_mode() -> List[str]:\n    message = list()\n    i = 0\n    print(\"Write your message (Enter key to breakline)('.' alone to send):\")\n    while True:\n        message.append(str(input()))\n        if message[i] == '.':\n            message.pop(i)\n            break\n        elif message[i] == '...' or message[i] == '#_FILE' or message[i] == '#_TTS' or message[i] == '#_FWD':\n            break\n        i += 1\n    return message\n\n\nasync def send_message(page: Page, message: Union[List[str], str]):\n    __logger.debug(\"Sending message\")\n    for i in range(len(message)):\n        await page.type(whatsapp_selectors_dict['message_area'], message[i])\n        if isinstance(message, list):\n            await page.keyboard.down('Shift')\n            await page.keyboard.press('Enter')\n            await page.keyboard.up('Shift')\n    await page.keyboard.press('Enter')\n\n\nasync def send_file(page):\n    __logger.info(\"Sending File\")\n    await page.click(whatsapp_selectors_dict['attach_file'])\n    await page.click(whatsapp_selectors_dict['choose_file'])\n    await page.waitForSelector(whatsapp_selectors_dict['send_file'], timeout=30000)\n    await page.click(whatsapp_selectors_dict['send_file'])\n# endregion\n"
  },
  {
    "path": "wplay/utils/target_data.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\n\nfrom pyppeteer.page import Page\n\nfrom wplay.utils.helpers import whatsapp_selectors_dict\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import logs_path\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\n# region FOR SCRIPTING\nasync def get_last_seen_from_focused_target(page: Page):\n    __logger.info(\"Getting target's status information\")\n    # await page.waitForSelector(whatsapp_selectors_dict['status'], visible = True)\n    try:\n        status: str = await page.evaluate(f'document.querySelector(\"{whatsapp_selectors_dict[\"last_seen\"]}\").getAttribute(\"title\")')\n        return status\n    except:\n        return '#status not found'\n# endregion\n"
  },
  {
    "path": "wplay/utils/target_search.py",
    "content": "# region TUTORIAL\n'''\nGo to region 'FOR SCRIPTING' and use the methods in your script!\n\nEXAMPLE OF USAGE:\nfrom wplay.pyppeteerUtils import pyppeteerConfig as pypConfig\nfrom wplay.pyppeteerUtils import pyppeteerSearch as pypSearch\n\nasync def my_script(target):\n    pages, browser = wait pyp.configure_browser_and_load_whatsapp(pypConfig.websites['whatsapp'])\n    await pypSearch.search_for_target_and_get_ready_for_conversation(pages[0], target)\n\n    message = pypSearch.ask_user_for_message_breakline_mode()\n    await pypSearch.send_message(pages[0], message)\n\n    message2 = pypSearch.ask_user_for_message()\n    await pypSearch.send_message(pages[0], message2)\n'''\n# endregion\n\n\n# region IMPORTS\nimport asyncio\nimport time\nfrom pathlib import Path\n\nfrom pyppeteer.errors import ElementHandleError\nfrom pyppeteer.browser import Browser\nfrom pyppeteer.page import Page\n\nfrom wplay.utils.helpers import whatsapp_selectors_dict, websites\nfrom wplay.utils.browser_config import load_website\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import logs_path\nfrom wplay import target_info\n# endregion\n\n\n# region LOGGER\n__logger = Logger(Path(__file__).name)\n# endregion\n\n\n# region FOR SCRIPTING\nasync def search_and_select_target_all_ways(page: Page, target: str, hide_groups: bool = False):\n    \"\"\"\n    Function try to search with 'search_and_select_target' function,\n    if any error occurs we try to search with 'search_and_select_target_without_new_chat_button' function.\n\n    Arguments:\n        page {Page} -- Pyppeteer page object\n        target {str} -- string with target name or number\n        hide_groups {bool} -- hide or not groups from search result (default: {False})\n\n    Returns:\n        target_focused_title {str} -- Return the target focused title\n    \"\"\"\n    try:\n        if not await search_target_by_number(page, target):\n            await search_and_select_target(page, target)\n    except Exception as e:\n        __logger.error(f\"Error searching target: {str(e)}. Trying the second way to search.\")\n        await page.reload()\n        await search_and_select_target_without_new_chat_button(page, target)\n\n\nasync def search_target_by_number(page: Page, target: str):\n    if await __try_load_contact_by_number(page, target):\n        # target_focused_title = await __get_focused_target_title(page, target)\n        # await __display_complete_target_info(page,choosed_target,contact_tuple)\n        await __wait_for_message_area(page)\n        return True\n    else:\n        return False\n\n\nasync def search_and_select_target(page: Page, target: str, hide_groups: bool = False):\n    \"\"\"\n    Function search for targets using the whatsapp new chat button.\n    When this function print the list of target found it doesn't print the phone number,\n    the phone is printed only after you choose the target.\n\n    Arguments:\n        page {Page} -- Pyppeteer page object\n        target {str} -- string with target name or number\n        hide_groups {bool} -- hide or not groups from search result (default: {False})\n\n    Returns:\n        target_focused_title {str} -- Return the target focused title\n    \"\"\"\n\n    await __open_new_chat(page)\n    await __type_in_new_chat_search_bar(page, target)\n    contact_list_elements_unchecked = await __get_contacts_elements_filtered(page, target)\n    group_list_elements_unchecked = await __get_groups_elements_filtered(page, target, hide_groups)\n    contact_titles_unchecked = await __get_contacts_titles_from_elements_unchecked(page, contact_list_elements_unchecked)\n    group_titles_unchecked = await __get_groups_titles_from_elements_unchecked(page, group_list_elements_unchecked)\n    contact_list_unchecked = __zip_contact_titles_and_elements_unchecked(contact_titles_unchecked, contact_list_elements_unchecked)\n    group_list_unchecked = __zip_group_titles_and_elements_unchecked(group_titles_unchecked, group_list_elements_unchecked)\n    contact_tuple = __check_contact_list(target, contact_list_unchecked)\n    group_tuple = __check_group_list(target, group_list_unchecked)\n    target_tuple = __get_target_tuple(contact_tuple, group_tuple)\n    __print_target_tuple(target_tuple)\n    target_index_choosed = __ask_user_to_choose_the_filtered_target(target_tuple)\n    choosed_target = __get_choosed_target(target_tuple, target_index_choosed)\n    await __navigate_to_target(page, choosed_target)\n    target_focused_title = await __get_focused_target_title(page, target)\n    await __display_complete_target_info(page, choosed_target, contact_tuple)\n    __check_target_focused_title(target, target_focused_title)\n    await __wait_for_message_area(page)\n    return target_focused_title\n\n\nasync def search_and_select_target_without_new_chat_button(page: Page, target: str, hide_groups: bool = False):\n    \"\"\"\n    Function search for targets search bar in the whatsapp, without using the new chat button.\n    Here we don't look for the contact by the number, we look for the number as if it were a string,\n    just to be an alternative to the method used in the other function.\n    When this function print the list of target found it print the phone number of every target,\n    the phone is also printed after you choose the target.\n\n    Arguments:\n        page {Page} -- Pyppeteer page object\n        target {str} -- string with target name or number\n        hide_groups {bool} -- hide or not groups from search result (default: {False})\n\n    Returns:\n        target_focused_title {str} -- Return the target focused title\n    \"\"\"\n    await __type_in_chat_or_message_search(page, target)\n    chats_messages_groups_elements_list = await __get_chats_messages_groups_elements(page)\n    contact_name_index_tuple_list = await __get_contacts_matched_with_query(chats_messages_groups_elements_list)\n    group_name_index_tuple_list = await __get_groups_matched_with_query(chats_messages_groups_elements_list, hide_groups)\n    await __get_number_of_filtered_contacts(page, contact_name_index_tuple_list, chats_messages_groups_elements_list)\n    target_tuple = __get_target_tuple(contact_name_index_tuple_list, group_name_index_tuple_list)\n    __print_target_tuple(target_tuple)\n    target_index_chosen = __ask_user_to_choose_the_filtered_target(target_tuple)\n\n    # chosen_target will be a tuple (a,b) such that a is the name of the target and b is the\n    # index of that element in chats_messages_groups_elements_list\n\n    chosen_target = __get_choosed_target(target_tuple, target_index_chosen)\n    await __open_selected_chat(chosen_target[1], chats_messages_groups_elements_list)\n    target_name = chosen_target[0]\n    await __display_complete_target_info(page, chosen_target, contact_name_index_tuple_list)\n    await __wait_for_message_area(page)\n    return target_name\n# endregion\n\n\n# region SEARCH AND SELECT TARGET\nasync def __accept_dialog(dialog):\n    try:\n        await dialog.accept()\n    except:\n        pass\n\n\nasync def __try_load_contact_by_number(page: Page, target: str) -> bool:\n    try:\n        if int(target):\n            __logger.debug(\"Loading contact by number.\")\n\n            page.on(\n                'dialog',\n                lambda dialog: asyncio.ensure_future(__accept_dialog(dialog))\n            )\n            await load_website(page, f\"{websites['wpp_unknown']}{target}\")\n            time.sleep(2)\n            if (await page.evaluate(f'document.querySelector(\"{whatsapp_selectors_dict[\"invalid_number_ok_button\"]}\") != null')):\n                await page.click(whatsapp_selectors_dict[\"invalid_number_ok_button\"])\n                __logger.debug(f\"Invalid number: {target}\")\n                print(f\"Invalid Number: {target}\")\n                return False\n            return True\n    except Exception as e:\n        __logger.error(f\"Error loading contact by number: {str(e)}\")\n        return False\n    return False\n\n\nasync def __get_number_of_filtered_contacts(page: Page, contact_name_index_tuple_list: list, chats_messages_groups_elements_list: list):\n    try:\n        for index, contact_name_index_tuple in enumerate(contact_name_index_tuple_list):\n            await chats_messages_groups_elements_list[contact_name_index_tuple[1]].click()\n            await __open_target_chat_info_page(page)\n            contact_page_elements = await __get_contact_page_elements(page)\n            complete_target_info = {}\n            await __get_contact_about_and_phone(contact_page_elements[3], complete_target_info)\n            contact_name_index_tuple_list[index] = (contact_name_index_tuple[0] + \" : \" + complete_target_info['Mobile'], contact_name_index_tuple[1])\n            await __close_contact_info_page(page)\n    except Exception as e:\n        print(e)\n\n\nasync def __display_complete_target_info(page, target_tuple, contact_tuple):\n    complete_target_info = {}\n    try:\n        if any(target_tuple[0] in i for i in contact_tuple):\n            complete_target_info = await __get_complete_info_on_target(page)\n            __print_complete_target_info(complete_target_info)\n            await __close_contact_info_page(page)\n        else:\n            complete_target_info = await __get_complete_info_on_group(page)\n            __print_complete_target_info(complete_target_info)\n            await __close_contact_info_page(page)\n    except Exception as e:\n        print(e)\n\n\nasync def __type_in_chat_or_message_search(page, target):\n    try:\n        print(f'Looking for: {target}')\n        await page.waitForSelector(\n            whatsapp_selectors_dict['chat_or_message_search'],\n            visible=True,\n            timeout=0\n        )\n        await page.waitFor(500)\n        await page.type(whatsapp_selectors_dict['chat_or_message_search'], target)\n        await page.waitFor(3000)\n    except Exception as e:\n        print(e)\n\n\nasync def __get_chats_messages_groups_elements(page: Page):\n    chats_messages_groups_elements_list = []  # type : list[int]\n    try:\n        chats_messages_groups_elements_list = await page.querySelectorAll(whatsapp_selectors_dict['chats_groups_messages_elements'])\n        return chats_messages_groups_elements_list\n    except Exception as e:\n        print(e)\n        exit()\n\n\nasync def __get_contacts_matched_with_query(chats_groups_messages_elements_list: list):\n    contacts_to_choose_from = []  # type : list[str , int]\n    get_contact_node_title_function = 'node => node.parentNode.getAttribute(\"title\")'\n    for idx, element in enumerate(chats_groups_messages_elements_list):\n        try:\n            contact_name = await element.querySelectorEval(whatsapp_selectors_dict['contact_element'], get_contact_node_title_function)\n            if contact_name is not None:\n                contacts_to_choose_from.append((contact_name, idx))\n        except ElementHandleError:\n            # if it is not a contact element, move to the next one\n            continue\n        except Exception as e:\n            print(e)\n\n    return contacts_to_choose_from\n\n\nasync def __get_groups_matched_with_query(chats_groups_messages_elements_list: list, hide_groups: bool):\n    groups_to_choose_from = []\n\n    if hide_groups:\n        return groups_to_choose_from\n\n    get_group_node_title_function = 'node => node.parentNode.getAttribute(\"title\")'\n    for idx, element in enumerate(chats_groups_messages_elements_list):\n        try:\n            group_name = await element.querySelectorEval(whatsapp_selectors_dict['group_element'],\n                                                         get_group_node_title_function)\n            groups_to_choose_from.append((group_name,idx))\n        except ElementHandleError:\n            # if it is not a contact element, move to the next one\n            continue\n        except Exception as e:\n            print(e)\n\n    return groups_to_choose_from\n\n\nasync def __open_selected_chat(target_index: int, chats_messages_groups_elements_list: list):\n    try:\n        await chats_messages_groups_elements_list[target_index].click()\n    except Exception as e:\n        print(f\"This target doesn't exist! Error: {str(e)}\")\n        exit()\n\n\nasync def __get_complete_info_on_target(page: Page):\n    contact_page_elements = []\n    try:\n        await __open_target_chat_info_page(page)\n        contact_page_elements = await __get_contact_page_elements(page)\n        complete_target_info = {}\n        await __get_contact_name_info(contact_page_elements[0], complete_target_info)\n        await __get_contact_about_and_phone(contact_page_elements[3], complete_target_info)\n        await __get_contact_groups_common_with_target(complete_target_info, page)\n        return complete_target_info\n    except Exception as e:\n        print(e)\n\n\nasync def __get_complete_info_on_group(page):\n    try:\n        await __open_target_chat_info_page(page)\n        contact_page_elements = await __get_contact_page_elements(page)\n        complete_target_group_info = {}\n        await __get_target_group_name(contact_page_elements[0],complete_target_group_info)\n        await __get_target_group_creation_info(contact_page_elements[0],complete_target_group_info)\n        await __get_target_group_description(contact_page_elements[1],complete_target_group_info)\n        await __get_target_group_members(contact_page_elements[4],complete_target_group_info)\n        return complete_target_group_info\n    except Exception as e:\n        print(e)\n\n\nasync def __get_target_group_name(element, complete_target_info):\n    try:\n        get_inner_text_function = 'e => e.innerText'\n        complete_target_info['Name'] = await element.querySelectorEval(whatsapp_selectors_dict['contact_info_page_target_group_name_element'],get_inner_text_function)\n    except Exception as e:\n        print(e)\n\n\nasync def __get_target_group_creation_info(element, complete_target_info):\n    try:\n        get_inner_text_function = 'e => e.innerText'\n        complete_target_info['Creation Info'] = await element.querySelectorEval\\\n            (whatsapp_selectors_dict['contact_info_page_target_group_creation_info_element'],get_inner_text_function)\n    except Exception as e:\n        print(e)\n\n\nasync def __get_target_group_description(element,complete_target_info):\n    try:\n        get_inner_text_function = 'e => e.innerText'\n        complete_target_info['Description'] = await element.querySelectorEval \\\n            (whatsapp_selectors_dict['contact_info_page_target_group_description_element'], get_inner_text_function)\n    except Exception as e:\n        print(e)\n\n\nasync def __get_target_group_members(element, complete_target_info):\n    try:\n        children_elements = await element.querySelectorAll(':scope > div')\n        if len(children_elements) <= 2:\n            target_group_members_selector = ':scope > div:last-child > div > div'\n        elif len(children_elements) == 3:\n            target_group_members_selector = ':scope > div:nth-child(2) > div > div'\n        else:\n            target_group_members_selector = whatsapp_selectors_dict['contact_info_page_target_group_member_elements']\n\n        target_group_member_elements = await element.querySelectorAll(target_group_members_selector)\n\n        group_member_name_selector = ':scope span[title]'\n        get_element_title_function = 'e => e.getAttribute(\"title\")'\n        complete_target_info['Members'] = [await ele.querySelectorEval(group_member_name_selector,get_element_title_function)\n                                         for ele in target_group_member_elements]\n    except Exception as e:\n        print(e)\n\n\nasync def __open_target_chat_info_page(page):\n    try:\n        await page.waitForSelector(\n            whatsapp_selectors_dict['target_chat_header'],\n            visible=True,\n            timeout=3000\n        )\n        await page.click(whatsapp_selectors_dict['target_chat_header'])\n    except Exception as e:\n        print(e)\n\n\nasync def __get_contact_page_elements(page: Page):\n    contact_page_elements = []\n    try:\n        await page.waitForSelector(\n            whatsapp_selectors_dict['contact_info_page_elements'],\n            visible=True,\n            timeout=8000\n        )\n        contact_page_elements = await page.querySelectorAll(whatsapp_selectors_dict['contact_info_page_elements'])\n        return contact_page_elements\n    except Exception as e:\n        print(e)\n\n\nasync def __get_contact_name_info(contact_name_element,complete_target_info):\n    try:\n        complete_target_info['Name'] = await contact_name_element.querySelectorEval('span > span', 'element => element.innerText')\n        complete_target_info['Last_seen'] = await contact_name_element.querySelectorEval('div > span:last-of-type > div > span', 'element => element.getAttribute(\"title\")')\n    except:\n        print(f'last seen not available')\n\n\nasync def __get_contact_about_and_phone(contact_name_element, complete_target_info):\n    try:\n        complete_target_info['About'] = await contact_name_element.querySelectorEval('div:nth-child(2) > div > div > span > span', 'element => element.getAttribute(\"title\")')\n        complete_target_info['Mobile'] = await contact_name_element.querySelectorEval('div:last-of-type > div > div > span > span', 'element => element.innerText')\n        target_info.target_contact_number(complete_target_info['Mobile'])\n    except Exception as e:\n        print(e)\n\n\nasync def __get_contact_groups_common_with_target(complete_target_info,page):\n    try:\n        await page.waitForSelector(\n            whatsapp_selectors_dict['contact_info_page_group_element_heading'],\n            visible=True,\n            timeout=3000\n        )\n\n        if (await page.evaluate(f'document.querySelector(\"{whatsapp_selectors_dict[\"contact_info_page_group_element_heading\"]}\").innerText'))\\\n               == \"Groups in common\":\n            group_elements = await page.querySelectorAll(whatsapp_selectors_dict['contact_info_page_group_elements'])\n            complete_target_info['Groups'] = [await ele.querySelectorEval('div>div>div:nth-child(2)>div:first-child>div>div>span', 'e => e.getAttribute(\"title\")') for ele in group_elements]\n        else:\n            complete_target_info['Groups'] = []\n    except:\n        complete_target_info['Groups'] = []\n        print(f'No groups in common')\n\n\nasync def __close_contact_info_page(page: Page):\n    try:\n        await page.waitForSelector(\n            whatsapp_selectors_dict['contact_info_page_close_button'],\n            visible=True,\n            timeout=5000\n        )\n        await page.click(whatsapp_selectors_dict['contact_info_page_close_button'])\n    except Exception as e:\n        print(e)\n\n\ndef __print_complete_target_info(complete_target_info):\n    for key in complete_target_info.keys():\n        if key == \"Groups\" or key == \"Members\":\n            print(key + \":\")\n            print(*complete_target_info[key], sep=\",\")\n        else:\n            print(f'{key}: {complete_target_info[key]} ')\n\n\nasync def __open_new_chat(page: Page):\n    await page.waitForSelector(\n        whatsapp_selectors_dict['new_chat_button'],\n        visible=True,\n        timeout=0\n    )\n    await page.waitFor(500)\n    await page.click(whatsapp_selectors_dict['new_chat_button'])\n\n\nasync def __type_in_new_chat_search_bar(page: Page, target: str):\n    print(f'Looking for: {target}')\n    __logger.info('Searching Target')\n    await page.waitForSelector(\n        whatsapp_selectors_dict['search_contact_input_new_chat'],\n        visible=True\n    )\n    await page.type(whatsapp_selectors_dict['search_contact_input_new_chat'], target)\n    await page.waitFor(3000)\n\n\nasync def __get_contacts_elements_filtered(page: Page, target: str):\n    contact_list_elements_unchecked = list()\n    try:\n        await page.waitForSelector(\n            whatsapp_selectors_dict['contact_list_elements_filtered_new_chat'],\n            visible=True,\n            timeout=3000\n        )\n\n        contact_list_elements_unchecked = await page.querySelectorAll(whatsapp_selectors_dict['contact_list_elements_filtered_new_chat'])\n    except:\n        print(f'No contact named by \"{target}\"!')\n        __logger.info('Target not found')\n    return contact_list_elements_unchecked\n\n\nasync def __get_groups_elements_filtered(page: Page, target: str, hide_groups: bool = False):\n    group_list_elements_unchecked = list()\n\n    if hide_groups:\n        return group_list_elements_unchecked\n\n    try:\n        await page.waitForSelector(\n            whatsapp_selectors_dict['group_list_elements_filtered_new_chat'],\n            visible=True,\n            timeout=3000\n        )\n\n        group_list_elements_unchecked = await page.querySelectorAll(whatsapp_selectors_dict['group_list_elements_filtered_new_chat'])\n    except:\n        print(f'No group named by \"{target}\"!')\n        __logger.info('Target not found in groups')\n    return group_list_elements_unchecked\n\n\nasync def __get_contacts_titles_from_elements_unchecked(page: Page, contact_list_elements_unchecked: list):\n    contact_titles_unchecked = []\n    for i in range(len(contact_list_elements_unchecked)):\n        contact_titles_unchecked.append(await page.evaluate(f'document.querySelectorAll(\"{whatsapp_selectors_dict[\"contact_list_elements_filtered_new_chat\"]}\")[{i}].getAttribute(\"title\")'))\n    return contact_titles_unchecked\n\n\nasync def __get_groups_titles_from_elements_unchecked(page: Page, group_list_elements_unchecked: list):\n    group_titles_unchecked = []\n    for i in range(len(group_list_elements_unchecked)):\n        group_titles_unchecked.append(await page.evaluate(f'document.querySelectorAll(\"{whatsapp_selectors_dict[\"group_list_elements_filtered_new_chat\"]}\")[{i}].getAttribute(\"title\")'))\n    return group_titles_unchecked\n\n\n# contact_list_unchecked is a zip (list of tuples) of contact_titles and\n# contact elements, unchecked.\ndef __zip_contact_titles_and_elements_unchecked(contact_titles_unchecked, contact_list_elements_unchecked):\n    contact_list_unchecked = list(zip(contact_titles_unchecked, contact_list_elements_unchecked))\n    return contact_list_unchecked\n\n\ndef __zip_group_titles_and_elements_unchecked(group_titles_unchecked, group_list_elements_unchecked):\n    group_list_unchecked = list(zip(group_titles_unchecked, group_list_elements_unchecked))\n    return group_list_unchecked\n\n\n# __checking_contact_list verify if target is in title, if not we pop from list\ndef __check_contact_list(target: str, contact_list_unchecked):\n    i = 0\n    while i < len(contact_list_unchecked):\n        if len(contact_list_unchecked) <= 0:\n            break\n\n        # we can add more verifications if we are getting false-positive contacts\n        if contact_list_unchecked[i][0].lower().find(target.lower()) == -1:\n            try:\n                contact_list_unchecked.pop(i)\n            except Exception as e:\n                print(f'Error: {str(e)}')\n            i -= 1\n        i += 1\n\n    contact_tuple = tuple(contact_list_unchecked)\n    return contact_tuple\n\n\ndef __check_group_list(target: str, group_list_unchecked):\n    i = 0\n    while i < len(group_list_unchecked):\n        if len(group_list_unchecked) <= 0:\n            break\n\n        # we can add more verifications if we are getting false-positive groups\n        if group_list_unchecked[i][0].lower().find(target.lower()) == -1:\n            try:\n                group_list_unchecked.pop(i)\n            except Exception as e:\n                print(f'Error: {str(e)}')\n            i -= 1\n        i += 1\n\n    group_tuple = tuple(group_list_unchecked)\n    return group_tuple\n\n\n# target_list is like that: (((0, 'a'), (1, 'b')), ((3, 'c'), (4, 'd'))),\n# but instead numbers and letters we have titles and elements\n# the first index is the contacts and the second is the groups\ndef __get_target_tuple(contact_tuple, group_tuple):\n    target_tuple = (contact_tuple, group_tuple)\n    # check to see if the target exits in the user's address book\n    if len(target_tuple[0]) == 0 and len(target_tuple[1]) == 0:\n        print('The target does not exist, please enter a valid target name')\n        __logger.error('Invalid target name entered')\n        exit()\n    return target_tuple\n\n\ndef __print_target_tuple(target_tuple):\n    lenght_of_contacts_tuple = len(target_tuple[0])\n    lenght_of_groups_tuple = len(target_tuple[1])\n\n    for i in range(lenght_of_contacts_tuple):\n        if lenght_of_contacts_tuple <= 0:\n            break\n        if i == 0:\n            print(\"Contacts found:\")\n            __logger.info('List of Targets')\n        print(f'{i}: {target_tuple[0][i][0]}')\n\n    for i in range(lenght_of_contacts_tuple, lenght_of_groups_tuple + lenght_of_contacts_tuple):\n        if lenght_of_groups_tuple <= 0:\n            break\n        if i == lenght_of_contacts_tuple:\n            print(\"Groups found:\")\n            __logger.info('List of Target in groups')\n        print(f'{i}: {target_tuple[1][i-lenght_of_contacts_tuple][0]}')\n\n\ndef __ask_user_to_choose_the_filtered_target(target_tuple):\n    if len(target_tuple[0] + target_tuple[1]) > 0:\n        __logger.info('Input Target Number')\n        target_index_choosed = int(\n            input('Enter the number of the target you wish to choose: '))\n    return target_index_choosed\n\n\ndef __get_choosed_target(target_tuple, target_index_choosed):\n    lenght_of_contacts_tuple = len(target_tuple[0])\n    if target_index_choosed is None:\n        exit()\n\n    try:\n        if target_index_choosed < lenght_of_contacts_tuple:\n            choosed_target = target_tuple[0][target_index_choosed]\n        elif target_index_choosed >= lenght_of_contacts_tuple:\n            choosed_target = target_tuple[1][target_index_choosed - lenght_of_contacts_tuple]\n        else:\n            print(\"This target doesn't exist!\")\n            __logger.error('Invalid Target')\n            exit()\n    except Exception as e:\n        print(f\"This target doesn't exist! Error: {str(e)}\")\n        __logger.error('Invalid Target')\n        exit()\n    return choosed_target\n\n\nasync def __navigate_to_target(page: Page, choosed_target):\n    try:\n        await choosed_target[1].click()\n    except Exception as e:\n        print(f\"This target doesn't exist! Error: {str(e)}\")\n        __logger.error('Invalid Target')\n        exit()\n\n\nasync def __get_focused_target_title(page: Page, target):\n    try:\n        await page.waitForSelector(whatsapp_selectors_dict['target_focused_title'])\n        target_focused_title = await page.evaluate(f'document.querySelector(\"{whatsapp_selectors_dict[\"target_focused_title\"]}\").getAttribute(\"title\")')\n    except Exception as e:\n        print(f'No target selected! Error: {str(e)}')\n        __logger.error('Target not selected from list')\n        exit()\n    return target_focused_title\n\n\ndef __print_selected_target_title(target_focused_title):\n    print(f\"You've selected the target named by: {target_focused_title}\")\n    __logger.info('Selected Target')\n\n\ndef __check_target_focused_title(target, target_focused_title):\n    \"\"\"if int(target):\n        def only_numerics(seq):\n            seq_type= type(seq)\n            return seq_type().join(filter(seq_type.isdigit, seq))\n\n        target = only_numerics(target)\n        target_focused_title = only_numerics(target_focused_title)\n\n        if target_focused_title.strip().find(target) == -1:\n            print(f\"Maybe you're focused in the wrong target, {target_focused_title}\")\n            must_continue = str(input(\"Do you want to continue (yes/no)? \"))\n            accepted_yes = {'yes', 'y'}\n            if not must_continue.lower() in accepted_yes:\n                exit()\n    \"\"\"\n    if target_focused_title.lower().find(target.lower()) == -1:\n        print(f\"You're focused in the wrong target, {target_focused_title}\")\n        must_continue = str(input(\"Do you want to continue (yes/no)? \"))\n        accepted_yes = {'yes', 'y'}\n        if not must_continue.lower() in accepted_yes:\n            exit()\n\n\nasync def __wait_for_message_area(page: Page):\n    try:\n        await page.waitForSelector(whatsapp_selectors_dict['message_area'])\n    except Exception as e:\n        print(f\"You don't belong this group anymore! Error: {str(e)}\")\n# endregion\n"
  },
  {
    "path": "wplay/utils/target_select.py",
    "content": "# region IMPORTS\nfrom pathlib import Path\n\nfrom pyppeteer.page import Page\n\nfrom wplay.utils import target_search\nfrom wplay.utils.Logger import Logger\nfrom wplay.utils.helpers import whatsapp_selectors_dict\n# endregion\n\n\n# region FOR SCRIPTING\nasync def manual_select_target(page: Page, hide_groups: bool = False):\n    __print_manual_selection_info()\n    await __open_new_chat(page)\n    target_focused_title = await __get_focused_target_title(page)\n    await __wait_for_message_area(page)\n    __print_selected_target_title(target_focused_title)\n    complete_target_info =  await target_search.__get_complete_info_on_target(page)\n    target_search.__print_complete_target_info(complete_target_info)\n    await __close_contact_info_page(page)\n    return target_focused_title\n# endregion\n\n\n# region SELECT TARGET\ndef __print_manual_selection_info():\n    print(f\"You've to go to whatsapp web and select target manually\")\n\n\ndef __print_selected_target_title(target_focused_title: str):\n    print(f\"You've selected the target named by: {target_focused_title}\")\n\n\nasync def __close_contact_info_page(page: Page):\n    try:\n        await page.waitForSelector(\n            whatsapp_selectors_dict['contact_info_page_close_button'],\n            visible=True,\n            timeout=5000\n        )\n        await page.click(whatsapp_selectors_dict['contact_info_page_close_button'])\n    except Exception as e:\n        print(e)\n\n\nasync def __open_new_chat(page: Page):\n    await page.waitForSelector(\n        whatsapp_selectors_dict['new_chat_button'],\n        visible=True,\n        timeout=0\n    )\n\n\nasync def __get_focused_target_title(page: Page):\n    try:\n        await page.waitForSelector(whatsapp_selectors_dict['target_focused_title'], visible=True, timeout=0)\n        target_focused_title = await page.evaluate(f'document.querySelector(\"{whatsapp_selectors_dict[\"target_focused_title\"]}\").getAttribute(\"title\")')\n    except Exception as e:\n        print(f'No target selected! Error: {str(e)}')\n        exit()\n    return target_focused_title\n\n\nasync def __wait_for_message_area(page: Page):\n    try:\n        await page.waitForSelector(whatsapp_selectors_dict['message_area'], timeout=0)\n    except Exception as e:\n        print(f\"You don't belong this group anymore! Error: {str(e)}\")\n# endregion\n"
  },
  {
    "path": "wplay/utils/verify_internet.py",
    "content": "import http.client as httplib\n\n\ndef internet_avalaible():\n    \"\"\"\n    Checks internet connection.\n    \"\"\"\n    conn = httplib.HTTPConnection(\"www.google.com\", timeout=5)\n    try:\n        conn.request(\"HEAD\", \"/\")\n        conn.close()\n        return True\n    except:\n        conn.close()\n        return False\n"
  }
]