├── .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 | [](https://github.com/shauryauppal/PyWhatsapp) [](https://github.com/shauryauppal/PyWhatsapp/blob/master/LICENSE) [](https://github.com/shauryauppal/PyWhatsapp/stargazers) [](https://hits.seeyoufarm.com) [](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 |
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://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 | [](https://star-history.com/#shauryauppal/PyWhatsapp&Date)
221 |
222 | #### If this helped you in any way gift me a cup of coffee :coffee:
223 | [](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 |
--------------------------------------------------------------------------------