├── .github └── workflows │ └── codesee-arch-diagram.yml ├── .gitignore ├── Documents └── opportunity.pdf ├── Input_Type.PNG ├── Install AutoIt for Sending Attachments ├── SciTE4AutoIt3.exe └── autoit-v3-setup.exe ├── LICENSE ├── Media ├── Demo.gif ├── goodmorning.jpg ├── goodnight.jpg └── howareyou.jpg ├── PyWhatsapp.py ├── README.md ├── _config.yml ├── contacts.txt ├── driver ├── chromedriver └── chromedriver.exe └── requirements.txt /.github/workflows/codesee-arch-diagram.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request_target: 6 | types: [opened, synchronize, reopened] 7 | 8 | name: CodeSee Map 9 | 10 | jobs: 11 | test_map_action: 12 | runs-on: ubuntu-latest 13 | continue-on-error: true 14 | name: Run CodeSee Map Analysis 15 | steps: 16 | - name: checkout 17 | id: checkout 18 | uses: actions/checkout@v2 19 | with: 20 | repository: ${{ github.event.pull_request.head.repo.full_name }} 21 | ref: ${{ github.event.pull_request.head.ref }} 22 | fetch-depth: 0 23 | 24 | # codesee-detect-languages has an output with id languages. 25 | - name: Detect Languages 26 | id: detect-languages 27 | uses: Codesee-io/codesee-detect-languages-action@latest 28 | 29 | - name: Configure JDK 16 30 | uses: actions/setup-java@v2 31 | if: ${{ fromJSON(steps.detect-languages.outputs.languages).java }} 32 | with: 33 | java-version: '16' 34 | distribution: 'zulu' 35 | 36 | # CodeSee Maps Go support uses a static binary so there's no setup step required. 37 | 38 | - name: Configure Node.js 14 39 | uses: actions/setup-node@v2 40 | if: ${{ fromJSON(steps.detect-languages.outputs.languages).javascript }} 41 | with: 42 | node-version: '14' 43 | 44 | - name: Configure Python 3.x 45 | uses: actions/setup-python@v2 46 | if: ${{ fromJSON(steps.detect-languages.outputs.languages).python }} 47 | with: 48 | python-version: '3.x' 49 | architecture: 'x64' 50 | 51 | - name: Configure Ruby '3.x' 52 | uses: ruby/setup-ruby@v1 53 | if: ${{ fromJSON(steps.detect-languages.outputs.languages).ruby }} 54 | with: 55 | ruby-version: '3.0' 56 | 57 | # CodeSee Maps Rust support uses a static binary so there's no setup step required. 58 | 59 | - name: Generate Map 60 | id: generate-map 61 | uses: Codesee-io/codesee-map-action@latest 62 | with: 63 | step: map 64 | github_ref: ${{ github.ref }} 65 | languages: ${{ steps.detect-languages.outputs.languages }} 66 | 67 | - name: Upload Map 68 | id: upload-map 69 | uses: Codesee-io/codesee-map-action@latest 70 | with: 71 | step: mapUpload 72 | api_token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }} 73 | github_ref: ${{ github.ref }} 74 | 75 | - name: Insights 76 | id: insights 77 | uses: Codesee-io/codesee-map-action@latest 78 | with: 79 | step: insights 80 | api_token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }} 81 | github_ref: ${{ github.ref }} 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | User_Data/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | -------------------------------------------------------------------------------- /Documents/opportunity.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/Documents/opportunity.pdf -------------------------------------------------------------------------------- /Input_Type.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/Input_Type.PNG -------------------------------------------------------------------------------- /Install AutoIt for Sending Attachments/SciTE4AutoIt3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/Install AutoIt for Sending Attachments/SciTE4AutoIt3.exe -------------------------------------------------------------------------------- /Install AutoIt for Sending Attachments/autoit-v3-setup.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/Install AutoIt for Sending Attachments/autoit-v3-setup.exe -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Media/Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/Media/Demo.gif -------------------------------------------------------------------------------- /Media/goodmorning.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/Media/goodmorning.jpg -------------------------------------------------------------------------------- /Media/goodnight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/Media/goodnight.jpg -------------------------------------------------------------------------------- /Media/howareyou.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/Media/howareyou.jpg -------------------------------------------------------------------------------- /PyWhatsapp.py: -------------------------------------------------------------------------------- 1 | import schedule 2 | # Importing traceback to catch xml button not found errors in the future 3 | import traceback 4 | from selenium import webdriver 5 | from selenium.webdriver.support.ui import WebDriverWait 6 | from selenium.webdriver.support import expected_conditions as EC 7 | from selenium.webdriver.common.keys import Keys 8 | from selenium.webdriver.common.by import By 9 | from selenium.common.exceptions import NoSuchElementException 10 | from selenium.webdriver.common.action_chains import ActionChains 11 | from selenium.webdriver.chrome.options import Options 12 | 13 | try: 14 | import autoit 15 | except ModuleNotFoundError: 16 | pass 17 | import time 18 | import datetime 19 | import os 20 | import argparse 21 | import platform 22 | 23 | if platform.system() == 'Darwin': 24 | # MACOS Path 25 | chrome_default_path = os.getcwd() + '/driver/chromedriver' 26 | else: 27 | # Windows Path 28 | chrome_default_path = os.getcwd() + '/driver/chromedriver.exe' 29 | 30 | parser = argparse.ArgumentParser(description='PyWhatsapp Guide') 31 | parser.add_argument('--chrome_driver_path', action='store', type=str, default=chrome_default_path, 32 | help='chromedriver executable path (MAC and Windows path would be different)') 33 | parser.add_argument('--message', action='store', type=str, default='', help='Enter the msg you want to send') 34 | parser.add_argument('--remove_cache', action='store', type=str, default='False', 35 | help='Remove Cache | Scan QR again or Not') 36 | parser.add_argument('--import_contact', action='store', type=str, default='False', 37 | help='Import contacts.txt or not (True/False)') 38 | parser.add_argument('--enable_headless', action='store', type=str, default='False', 39 | help='Enable Headless Driver (True/False)') 40 | args = parser.parse_args() 41 | 42 | if args.remove_cache == 'True': 43 | os.system('rm -rf User_Data/*') 44 | browser = None 45 | Contact = None 46 | message = None if args.message == '' else args.message 47 | Link = "https://web.whatsapp.com/" 48 | wait = None 49 | choice = None 50 | docChoice = None 51 | doc_filename = None 52 | unsaved_Contacts = None 53 | 54 | 55 | def input_contacts(): 56 | global Contact, unsaved_Contacts 57 | # List of Contacts 58 | Contact = [] 59 | unsaved_Contacts = [] 60 | while True: 61 | # Enter your choice 1 or 2 62 | print("PLEASE CHOOSE ONE OF THE OPTIONS:\n") 63 | print("1.Message to Saved Contact number") 64 | print("2.Message to Unsaved Contact number\n") 65 | x = int(input("Enter your choice(1 or 2):\n")) 66 | print() 67 | if x == 1: 68 | n = int(input('Enter number of Contacts to add(count)->')) 69 | print() 70 | for i in range(0, n): 71 | inp = str(input("Enter contact name(text)->")) 72 | inp = '"' + inp + '"' 73 | # print (inp) 74 | Contact.append(inp) 75 | elif x == 2: 76 | n = int(input('Enter number of unsaved Contacts to add(count)->')) 77 | print() 78 | for i in range(0, n): 79 | # Example use: 919899123456, Don't use: +919899123456 80 | # Reference : https://faq.whatsapp.com/en/android/26000030/ 81 | inp = str(input( 82 | "Enter unsaved contact number with country code(interger):\n\nValid input: 91943xxxxx12\nInvalid input: +91943xxxxx12\n\n")) 83 | # print (inp) 84 | unsaved_Contacts.append(inp) 85 | 86 | choi = input("Do you want to add more contacts(y/n)->") 87 | if choi == "n": 88 | break 89 | 90 | if len(Contact) != 0: 91 | print("\nSaved contacts entered list->", Contact) 92 | if len(unsaved_Contacts) != 0: 93 | print("Unsaved numbers entered list->", unsaved_Contacts) 94 | input("\nPress ENTER to continue...") 95 | 96 | 97 | def input_message(): 98 | global message 99 | # Enter your Good Morning Msg 100 | print( 101 | "Enter the message and use the symbol '~' to end the message:\nFor example: Hi, this is a test message~\n\nYour message: ") 102 | message = [] 103 | done = False 104 | 105 | while not done: 106 | temp = input() 107 | if len(temp) != 0 and temp[-1] == "~": 108 | done = True 109 | message.append(temp[:-1]) 110 | else: 111 | message.append(temp) 112 | message = "\n".join(message) 113 | print(message) 114 | 115 | 116 | def whatsapp_login(chrome_path, headless): 117 | global wait, browser, Link 118 | chrome_options = Options() 119 | chrome_options.add_argument('--user-data-dir=./User_Data') 120 | if headless == 'True': 121 | chrome_options.add_argument('--headless') 122 | browser = webdriver.Chrome(executable_path=chrome_path, options=chrome_options) 123 | wait = WebDriverWait(browser, 600) 124 | browser.get(Link) 125 | browser.maximize_window() 126 | print("QR scanned") 127 | 128 | 129 | def send_message(target): 130 | global message, wait, browser 131 | try: 132 | x_arg = '//span[contains(@title,' + target + ')]' 133 | ct = 0 134 | while ct != 5: 135 | try: 136 | group_title = wait.until(EC.presence_of_element_located((By.XPATH, x_arg))) 137 | group_title.click() 138 | break 139 | except Exception as e: 140 | print("Retry Send Message Exception", e) 141 | ct += 1 142 | time.sleep(3) 143 | input_box = browser.find_element_by_xpath('//*[@id="main"]/footer/div[1]/div[2]/div/div[2]') 144 | for ch in message: 145 | if ch == "\n": 146 | ActionChains(browser).key_down(Keys.SHIFT).key_down(Keys.ENTER).key_up(Keys.ENTER).key_up( 147 | Keys.SHIFT).key_up(Keys.BACKSPACE).perform() 148 | else: 149 | input_box.send_keys(ch) 150 | input_box.send_keys(Keys.ENTER) 151 | print("Message sent successfully") 152 | time.sleep(1) 153 | except NoSuchElementException as e: 154 | print("send message exception: ", e) 155 | return 156 | 157 | 158 | def send_unsaved_contact_message(): 159 | global message 160 | try: 161 | time.sleep(10) 162 | browser.implicitly_wait(10) 163 | input_box = browser.find_element_by_xpath('//*[@id="main"]/footer/div[1]/div[2]/div/div[2]') 164 | for ch in message: 165 | if ch == "\n": 166 | ActionChains(browser).key_down(Keys.SHIFT).key_down(Keys.ENTER).key_up(Keys.ENTER).key_up( 167 | Keys.SHIFT).key_up(Keys.BACKSPACE).perform() 168 | else: 169 | input_box.send_keys(ch) 170 | input_box.send_keys(Keys.ENTER) 171 | print("Message sent successfully") 172 | except Exception as e: 173 | print("Failed to send message exception: ", e) 174 | return 175 | 176 | 177 | def send_attachment(): 178 | # Attachment Drop Down Menu 179 | try: 180 | clipButton = browser.find_element_by_xpath('//*[@id="main"]/footer/div[1]/div[1]/div[2]/div/div/span') 181 | clipButton.click() 182 | except: 183 | traceback.print_exc() 184 | time.sleep(1) 185 | 186 | # To send Videos and Images. 187 | try: 188 | mediaButton = browser.find_element_by_xpath('//*[@id="main"]/footer/div[1]/div[1]/div[2]/div/span/div/div/ul/li[1]/button') 189 | mediaButton.click() 190 | except: 191 | traceback.print_exc() 192 | time.sleep(3) 193 | hour = datetime.datetime.now().hour 194 | # After 5am and before 11am scheduled this. 195 | if (hour >= 5 and hour <= 11): 196 | image_path = os.getcwd() + "\\Media\\" + 'goodmorning.jpg' 197 | # After 9pm and before 11pm schedule this 198 | elif (hour >= 21 and hour <= 23): 199 | image_path = os.getcwd() + "\\Media\\" + 'goodnight.jpg' 200 | else: # At any other time schedule this. 201 | image_path = os.getcwd() + "\\Media\\" + 'howareyou.jpg' 202 | # print(image_path) 203 | 204 | autoit.control_focus("Open", "Edit1") 205 | autoit.control_set_text("Open", "Edit1", image_path) 206 | autoit.control_click("Open", "Button1") 207 | 208 | time.sleep(3) 209 | # Send button 210 | try: 211 | whatsapp_send_button = browser.find_element_by_xpath('//*[@id="app"]/div/div/div[2]/div[2]/span/div/span/div/div/div[2]/span/div/div') 212 | whatsapp_send_button.click() 213 | except: 214 | traceback.print_exc() 215 | 216 | print("File sent") 217 | 218 | 219 | def send_files(): 220 | global doc_filename 221 | # Attachment Drop Down Menu 222 | clipButton = browser.find_element_by_xpath('//*[@id="main"]/footer/div[1]/div[1]/div[2]/div/div/span') 223 | clipButton.click() 224 | 225 | time.sleep(1) 226 | # To send a Document(PDF, Word file, PPT) 227 | # This makes sure that gifs, images can be imported through documents folder and they display 228 | # properly in whatsapp web. 229 | if doc_filename.split('.')[1]=='pdf'or doc_filename.split('.')[1]=='docx'or doc_filename.split('.')[1]=='pptx' : 230 | try: 231 | docButton = browser.find_element_by_xpath('//*[@id="main"]/footer/div[1]/div[1]/div[2]/div/span/div/div/ul/li[3]/button') 232 | 233 | docButton.click() 234 | except: 235 | # Check for traceback errors with XML imports 236 | traceback.print_exc() 237 | else: 238 | try: 239 | # IMG attatchment button 240 | docButton = browser.find_element_by_xpath('//*[@id="main"]/footer/div[1]/div[1]/div[2]/div/span/div/div/ul/li[1]/button') 241 | docButton.click() 242 | except: 243 | # Check for traceback errors with XML imports 244 | traceback.print_exc() 245 | time.sleep(1) 246 | docPath = os.getcwd() + "\\Documents\\" + doc_filename 247 | try: 248 | autoit.control_focus("Open", "Edit1") 249 | except : 250 | traceback.print_exc() 251 | autoit.control_set_text("Open", "Edit1", (docPath)) 252 | autoit.control_click("Open", "Button1") 253 | time.sleep(3) 254 | # Changed whatsapp send button xml link. 255 | whatsapp_send_button = browser.find_element_by_xpath('//*[@id="app"]/div/div/div[2]/div[2]/span/div/span/div/div/div[2]/span/div/div') 256 | 257 | whatsapp_send_button.click() 258 | print('File sent') 259 | 260 | def import_contacts(): 261 | global Contact, unsaved_Contacts 262 | Contact = [] 263 | unsaved_Contacts = [] 264 | fp = open("contacts.txt", "r") 265 | while True: 266 | line = fp.readline() 267 | con = ' '.join(line.split()) 268 | if con and con.isdigit(): 269 | unsaved_Contacts.append(int(con)) 270 | elif con: 271 | Contact.append(con) 272 | if not line: 273 | break 274 | 275 | 276 | def sender(): 277 | global Contact, choice, docChoice, unsaved_Contacts 278 | print(Contact, unsaved_Contacts) 279 | for i in Contact: 280 | try: 281 | send_message(i) 282 | print("Message sent to ", i) 283 | except Exception as e: 284 | print("Msg to {} send Exception {}".format(i, e)) 285 | if (choice == "yes"): 286 | try: 287 | send_attachment() 288 | except: 289 | print('Attachment not sent.') 290 | if (docChoice == "yes"): 291 | try: 292 | send_files() 293 | except: 294 | print('Files not sent') 295 | time.sleep(5) 296 | if len(unsaved_Contacts) > 0: 297 | for i in unsaved_Contacts: 298 | link = "https://web.whatsapp.com/send?phone={}&text&source&data&app_absent".format(i) 299 | # driver = webdriver.Chrome() 300 | browser.get(link) 301 | print("Sending message to", i) 302 | send_unsaved_contact_message() 303 | if (choice == "yes"): 304 | try: 305 | send_attachment() 306 | except: 307 | print() 308 | if (docChoice == "yes"): 309 | try: 310 | send_files() 311 | except: 312 | print() 313 | time.sleep(7) 314 | 315 | 316 | # For GoodMorning Image and Message 317 | schedule.every().day.at("07:00").do(sender) 318 | # For How are you message 319 | schedule.every().day.at("13:35").do(sender) 320 | # For GoodNight Image and Message 321 | schedule.every().day.at("22:00").do(sender) 322 | 323 | # Example Schedule for a particular day of week Monday 324 | schedule.every().monday.at("08:00").do(sender) 325 | 326 | 327 | # To schedule your msgs 328 | def scheduler(): 329 | while True: 330 | schedule.run_pending() 331 | time.sleep(1) 332 | 333 | if __name__ == "__main__": 334 | print("Web Page Open") 335 | 336 | if os.path.exists("contacts.txt") and args.import_contact == 'True': 337 | # read contacts from text file 338 | import_contacts() 339 | if not Contact and not unsaved_Contacts: 340 | # Append more contact as input to send messages 341 | input_contacts() 342 | if message == None: 343 | # Enter the message you want to send 344 | input_message() 345 | 346 | # If you want to schedule messages for 347 | # a particular timing choose yes 348 | # If no choosed instant message would be sent 349 | isSchedule = input('Do you want to schedule your Message(yes/no):') 350 | if (isSchedule == "yes"): 351 | jobtime = input('input time in 24 hour (HH:MM) format - ') 352 | 353 | # Send Attachment Media only Images/Video 354 | choice = input("Would you like to send attachment(yes/no): ") 355 | 356 | docChoice = input("Would you file to send a Document file(yes/no): ") 357 | if (docChoice == "yes"): 358 | # Note the document file should be present in the Document Folder 359 | doc_filename = input("Enter the Document file name you want to send: ") 360 | 361 | # Let us login and Scan 362 | print("SCAN YOUR QR CODE FOR WHATSAPP WEB") 363 | whatsapp_login(args.chrome_driver_path, args.enable_headless) 364 | 365 | # Send message to all Contact List 366 | # This sender is just for testing purpose to check script working or not. 367 | # Scheduling works below. 368 | if isSchedule == "yes": 369 | schedule.every().day.at(jobtime).do(sender) 370 | else: 371 | sender() 372 | print("Task Completed") 373 | 374 | # Messages are scheduled to send 375 | # Default schedule to send attachment and greet the personal 376 | # For GoodMorning, GoodNight and howareyou wishes 377 | # Comment in case you don't want to send wishes or schedule 378 | scheduler() 379 | # browser.quit() 380 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyWhatsapp 2 | [![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://github.com/shauryauppal/PyWhatsapp) [![License](https://img.shields.io/github/license/shauryauppal/PyWhatsapp.svg)](https://github.com/shauryauppal/PyWhatsapp/blob/master/LICENSE) [![GitHub stars](https://img.shields.io/github/stars/shauryauppal/PyWhatsapp.svg)](https://github.com/shauryauppal/PyWhatsapp/stargazers) [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fshauryauppal%2FPyWhatsapp&count_bg=%23CA175B&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) [![LinkedIn](https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/shaurya-uppal/) 3 | 4 | ## Python Automation using Selenium & Scheduling of messages and media 5 | 6 | ## Objective: 7 | Pywhatsapp is used to Automate Whatsapp through Whatsapp web. We can 8 | add number of contacts whom we want to send messages or Media 9 | attachments ( like Video or Images). Selenium, Autoit and Schedule have 10 | been used one from Automation and other for Scheduling messages. 11 | 12 | --- 13 | 14 | ## Use Case: 15 | We can schedule Good Morning or Good night messages with a nice Picture 16 | at a particular time to our loved ones. We can set reminders. Suppose at 17 | 12 o'clock you want to wish your friend happy birthday so schedule your 18 | messages and sleep peacefully. 19 | 20 | ## Installation 21 | 22 | >$ pip install -r requirements.txt 23 | 24 | OR 25 | 26 | >$ pip install selenium 27 | > 28 | >$ pip install schedule 29 | > 30 | >$ pip install PyAutoIt 31 | 32 | ###### NOTE: If there is any issue in installation of pyautoit then clone the repo and install from repo. [LINK](https://github.com/jacexh/pyautoit) 33 | --- 34 | 35 | ### Platform: Windows 36 | ChromeDriver used: If this versions becomes outdated or gives problem 37 | download the latest version from Download Link 39 | 40 | ### Platform Mac 41 | Remove the ChromeDriverused in the repository and install Mac ChromeDrive Download Link 43 | 44 | >Set ChromeDriver path in function whatsapp_login() 45 | >Set 46 | > ChromeDriver Path in MacOS 47 | 48 | --- 49 | ### For Sending Attachments you need to Install AutoIt (Optional, if you only want to send messages) | (Only FOR WINDOWS USERS): 50 | 51 | You may install from the links given below or Install from the folder 52 | named "Install AutoIt for Sending Attachments" in the repository. 53 | 54 | Official 55 | Website Download Webpage 56 | 57 | Installation Link of AutoIt.exe 59 | 60 | AutoitScript Editor (optional to install) 62 | 63 | Installation is pretty Simple no changes in setting are required keep 64 | everything default. Few clicks on Next and you are done. 65 | 66 | --- 67 | 68 | # Promotion: Subscribe to my Newsletter - Data Science and Problem Solving 69 | Screenshot 2021-12-31 at 1 04 31 PM 70 | 71 | 72 | --- 73 | 74 | ## Feature Enhancement: 75 | QR CODE Scanning: On receiving a lot of complaints about QR Code Scanning Issue again and again. I have added a Cookie system that will save your session so that whatsapp don't think you are login for first time. By Saving Session data you will have to scan QR Code to Login only once or till the time whatsapp doesnot log you out from whatsappweb. 76 | 77 | NOTE: A folder User_Data will be created which has all your session information. Keep this Folder VERY SAFE. 78 | 79 | ## Code: 80 | ### Added ArgParser 81 | `python3 PyWhatsapp.py --help`
82 | --chrome_driver_path (required) CHROME_DRIVER_PATH chromedriver executable path (MAC and Windows path would be different)
83 | --message (optional) MESSAGE Enter the msg you want to send
84 | --remove_cache (optional) REMOVE_CACHE Remove Cache | Scan QR again or Not
85 | --import_contact (optional) IMPORT_CONTACT reads contacts from contact.txt text file
86 | --enable_headless (optional) Enable Headless Driver (True/False)
87 | 88 | For Windows: `python3 PyWhatsapp.py --chrome_driver_path 'driver/chromedriver.exe' --message 'Hi Shaurya, How Are you?'` 89 |
For MACOS: `python3 PyWhatsapp.py --chrome_driver_path 'driver/chromedriver' --message 'Hi Shaurya, How Are you?'` 90 | 91 | ### input_contacts() 92 | 93 | In this functions Contacts list can be hardcoded or you can give input 94 | accordingly.(Make changes in Contact array according to you) 95 | 96 | 97 | ``` 98 | 1.Enter Saved Contact number-> 99 | 2.Enter Unsaved Contact number-> 100 | Enter your choice(1 or 2):->1 101 | # For saved Contacts 102 | Enter number of Contacts to add(count)->1 103 | Enter contact name(text)->Shaurya 104 | # For unsaved Contacts 105 | Enter number of unsaved Contacts to add(count)->1 106 | Enter unsaved contact number with country code(interger)->919899123456 107 | ``` 108 | 109 | ### NOTE: For unsaved contacts: 110 | Do enter your country code then contact number. 111 | >Use: 919899123456 112 | > 113 | >Don't Use: +919899123456 114 | 115 | 116 | 117 | ### input_message() 118 | In this function we take input of message to send to all the Contacts 119 | list from user. 120 | 121 | Example: 122 | >Enter the msg to send-> Good morning 123 | 124 | ### Enter choice to schedule message or not. 125 | >Do you want to schedule your Message(yes/no): yes 126 | > 127 | >input time in 24 hour (HH:MM) format - 10:10 128 | 129 | NOTE: If testing program for the first time Scheduling should be `no` 130 | inorder to check it is working perfectly. 131 | 132 | ### Enter choice whether to send attachments or not. 133 | >Would you like to send attachment(yes/no): yes 134 | > 135 | >Answer the input with yes or no. 136 | 137 | ### send_attachments() 138 | NOTE: Add Photos & Videos in the Media Folder. 139 | 140 | image_path = os.getcwd() +"\\Media\\" + 'goodmorning.jpg' 141 | 142 | Example path to send goodmorning image to your listed Contacts. 143 | 144 | * "hour" variable is used to check current Hour on the clock and 145 | according image is sent to the Contact. 146 | * If time is after 5am and before 11am schedule goodmorning.jpg image. 147 | * If time is after 9pm schedule goodnight image. 148 | * If time is anyother send howareyou image. 149 | 150 | You can set your own photos at a particular time feel free to do that. 151 | 152 | ### send_files() 153 | NOTE: Add the document in the documents folder. 154 | >Would you file to send a Document file(yes/no): yes 155 | > 156 | >Enter the Document file name you want to send: opportunity 157 | 158 | * If the document file names are same then write the document name 159 | with extension like opportunity.pdf or opportunity.txt 160 | 161 | 162 | ### Schedule messages and Attachments 163 | schedule.every().Monday.at("06:00").do(sender) 164 | 165 | schedule.every().Tuesday.at("07:00").do(sender) 166 | 167 | schedule.every().Friday.at("07:30").do(sender) 168 | 169 | schedule.every().day.at("08:30").do(sender) 170 | 171 | * You make change these schedule days and time according to you. 172 | 173 | ### How to write contacts in contacts.txt file (check commited file)? 174 | ``` 175 | shaurya <- isdigit() = False so this is saved contact 176 | 177 | 919899312345 <- isdigit() = True so this is unsaved contact 178 | ``` 179 | 180 | --- 181 | 182 | 183 | ### Input Screenshot: 184 | 186 | 187 | --- 188 | 189 | ### Demo of Working (GIF) 190 | 192 | 193 | --- 194 | 195 | ## Contributions 196 | Issues 197 | and Pull 198 | requests are most welcome. 199 | 200 | --- 201 | ## License 202 | License 203 | Code and documentation are available according to the Apache License 204 | (see LICENSE). 206 | 207 | --- 208 | 209 | ## Author: 210 | ## Shaurya Uppal 211 | ## Endorse me at LinkedIn if this project was helpful. [![Linkedin](https://i.stack.imgur.com/gVE0j.png) LinkedIn](https://www.linkedin.com/in/shaurya-uppal/) 212 | 213 | shauryauppal00111@gmail.com 214 | 215 | Feel free to mail me for any queries (After you have tried finding your 216 | solution). 217 | 218 | ### Star History 219 | 220 | [![Star History Chart](https://api.star-history.com/svg?repos=shauryauppal/PyWhatsapp&type=Date)](https://star-history.com/#shauryauppal/PyWhatsapp&Date) 221 | 222 | #### If this helped you in any way gift me a cup of coffee :coffee: 223 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UXSREFS2VFSWU) 224 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight -------------------------------------------------------------------------------- /contacts.txt: -------------------------------------------------------------------------------- 1 | DummySavedContactName1 2 | DummySavedContactName2 3 | 4 | 917982212345 5 | 918851512345 6 | 917042812345 7 | -------------------------------------------------------------------------------- /driver/chromedriver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/driver/chromedriver -------------------------------------------------------------------------------- /driver/chromedriver.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shauryauppal/PyWhatsapp/f072a3aef16b9c98772c430768201273da878c65/driver/chromedriver.exe -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyAutoIt==0.4 2 | schedule==0.5.0 3 | selenium==3.13.0 4 | --------------------------------------------------------------------------------