pygwan 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pygwan-0.1.0/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
pygwan-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.1
2
+ Name: pygwan
3
+ Version: 0.1.0
4
+ Summary: Unofficial Python wrapper for the WhatsApp Cloud API by Tarmica Chiwara
5
+ Project-URL: Homepage, https://github.com/yourusername/pygwan
6
+ Author-email: Tarmica Chiwara <tarimicac@gmail.com>
7
+ License-File: LICENSE
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Pygwan
15
+
16
+ Unofficial Python wrapper for the WhatsApp Cloud API by Tarmica Chiwara.
17
+
18
+ ## Installation
19
+
20
+ To install the WhatsApp Python wrapper, use the following command:
21
+
22
+ ```bash
23
+ pip install pygwan
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ Import the `WhatsApp` class from the package and initialize an instance with your WhatsApp token and phone number ID:
29
+
30
+ ```python
31
+ from pygwan import WhatsApp
32
+
33
+ whatsapp = WhatsApp(token="your_token", phone_number_id="your_phone_number_id")
34
+ ```
35
+
36
+ ### Sending a Message
37
+
38
+ You can send a text message to a WhatsApp user using the `send_message` method:
39
+
40
+ ```python
41
+ whatsapp.send_message("Hello, this is a test message.", "recipient_phone_number")
42
+ ```
43
+
44
+ ### Replying to a Message
45
+
46
+ Reply to a message using the `reply_to_message` method:
47
+
48
+ ```python
49
+ whatsapp.reply_to_message("message_id", "recipient_phone_number", "Reply message.")
50
+ ```
51
+
52
+ ### Sending a Template
53
+
54
+ Send a template message using the `send_template` method:
55
+
56
+ ```python
57
+ components = [
58
+ # List of template components
59
+ # Example: {"type": "text", "text": "Hello, this is a template message."}
60
+ ]
61
+ whatsapp.send_template("template_name", "recipient_phone_number", components)
62
+ ```
63
+
64
+ ### Sending a Location
65
+
66
+ Send a location message using the `send_location` method:
67
+
68
+ ```python
69
+ whatsapp.send_location("-23.564", "-46.654", "Location Name", "Location Address", "recipient_phone_number")
70
+ ```
71
+
72
+ ### Sending an Image
73
+
74
+ Send an image message using the `send_image` method:
75
+
76
+ ```python
77
+ image_link = "https://example.com/image.jpg"
78
+ whatsapp.send_image(image_link, "recipient_phone_number")
79
+ ```
80
+
81
+ ## Documentation
82
+
83
+ For more detailed usage and information, please refer to the official documentation in the code lol
84
+
85
+ ## License
86
+
87
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
pygwan-0.1.0/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # Pygwan
2
+
3
+ Unofficial Python wrapper for the WhatsApp Cloud API by Tarmica Chiwara.
4
+
5
+ ## Installation
6
+
7
+ To install the WhatsApp Python wrapper, use the following command:
8
+
9
+ ```bash
10
+ pip install pygwan
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Import the `WhatsApp` class from the package and initialize an instance with your WhatsApp token and phone number ID:
16
+
17
+ ```python
18
+ from pygwan import WhatsApp
19
+
20
+ whatsapp = WhatsApp(token="your_token", phone_number_id="your_phone_number_id")
21
+ ```
22
+
23
+ ### Sending a Message
24
+
25
+ You can send a text message to a WhatsApp user using the `send_message` method:
26
+
27
+ ```python
28
+ whatsapp.send_message("Hello, this is a test message.", "recipient_phone_number")
29
+ ```
30
+
31
+ ### Replying to a Message
32
+
33
+ Reply to a message using the `reply_to_message` method:
34
+
35
+ ```python
36
+ whatsapp.reply_to_message("message_id", "recipient_phone_number", "Reply message.")
37
+ ```
38
+
39
+ ### Sending a Template
40
+
41
+ Send a template message using the `send_template` method:
42
+
43
+ ```python
44
+ components = [
45
+ # List of template components
46
+ # Example: {"type": "text", "text": "Hello, this is a template message."}
47
+ ]
48
+ whatsapp.send_template("template_name", "recipient_phone_number", components)
49
+ ```
50
+
51
+ ### Sending a Location
52
+
53
+ Send a location message using the `send_location` method:
54
+
55
+ ```python
56
+ whatsapp.send_location("-23.564", "-46.654", "Location Name", "Location Address", "recipient_phone_number")
57
+ ```
58
+
59
+ ### Sending an Image
60
+
61
+ Send an image message using the `send_image` method:
62
+
63
+ ```python
64
+ image_link = "https://example.com/image.jpg"
65
+ whatsapp.send_image(image_link, "recipient_phone_number")
66
+ ```
67
+
68
+ ## Documentation
69
+
70
+ For more detailed usage and information, please refer to the official documentation in the code lol
71
+
72
+ ## License
73
+
74
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pygwan"
7
+ version = "0.1.0"
8
+ authors = [{ name = "Tarmica Chiwara", email = "tarimicac@gmail.com" }]
9
+ description = "Unofficial Python wrapper for the WhatsApp Cloud API by Tarmica Chiwara"
10
+ readme = "README.md"
11
+ requires-python = ">=3.7"
12
+ classifiers = [
13
+ "Programming Language :: Python :: 3",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Operating System :: OS Independent",
16
+ ]
17
+
18
+ [project.urls]
19
+ Homepage = "https://github.com/yourusername/pygwan"
File without changes
@@ -0,0 +1,1063 @@
1
+ """
2
+ Unofficial python wrapper for the WhatsApp Cloud API by Tarmica Chiwara
3
+ """
4
+ from __future__ import annotations
5
+ import os
6
+ import mimetypes
7
+ import requests
8
+ import logging
9
+ import warnings
10
+ from colorama import Fore, Style
11
+ from requests_toolbelt.multipart.encoder import MultipartEncoder
12
+ from typing import Optional, Dict, Any, List, Union, Tuple, Callable
13
+
14
+
15
+ # Setup logging
16
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
17
+
18
+
19
+ class WhatsApp(object):
20
+ """ "
21
+ WhatsApp Object
22
+ """
23
+
24
+ def __init__(self, token=None, phone_number_id=None):
25
+ """
26
+ Initialize the WhatsApp Object
27
+
28
+ Args:
29
+ token[str]: Token for the WhatsApp cloud API obtained from the developer portal
30
+ phone_number_id[str]: Phone number id for the WhatsApp cloud API obtained from the developer portal
31
+ """
32
+ self.token = token
33
+ self.phone_number_id = phone_number_id
34
+ self.base_url = "https://graph.facebook.com/v17.0"
35
+ self.v15_base_url = "https://graph.facebook.com/v17.0"
36
+ self.url = f"{self.base_url}/{phone_number_id}/messages"
37
+
38
+ self.headers = {
39
+ "Content-Type": "application/json",
40
+ "Authorization": "Bearer {}".format(self.token),
41
+ }
42
+
43
+ def send_message(
44
+ self, message, recipient_id, recipient_type="individual", preview_url=True
45
+ ):
46
+ """
47
+ Sends a text message to a WhatsApp user
48
+
49
+ Args:
50
+ message[str]: Message to be sent to the user
51
+ recipient_id[str]: Phone number of the user with country code wihout +
52
+ recipient_type[str]: Type of the recipient, either individual or group
53
+ preview_url[bool]: Whether to send a preview url or not
54
+
55
+ Example:
56
+ ```python
57
+ >>> from whatsapp import WhatsApp
58
+ >>> whatsapp = WhatsApp(token, phone_number_id)
59
+ >>> whatsapp.send_message("Hello World", "5511999999999")
60
+ >>> whatsapp.send_message("Hello World", "5511999999999", preview_url=False)
61
+
62
+ """
63
+ data = {
64
+ "messaging_product": "whatsapp",
65
+ "recipient_type": recipient_type,
66
+ "to": recipient_id,
67
+ "type": "text",
68
+ "text": {"preview_url": preview_url, "body": message},
69
+ }
70
+ logging.info(f"Sending message to {recipient_id}")
71
+ r = requests.post(f"{self.url}", headers=self.headers, json=data)
72
+ if r.status_code == 200:
73
+ logging.info(f"Message sent to {recipient_id}")
74
+ return r.json()
75
+ logging.info(f"Message not sent to {recipient_id}")
76
+ logging.info(f"Status code: {r.status_code}")
77
+ logging.error(f"Response: {r.json()}")
78
+ return r.json()
79
+
80
+ def reply_to_message(
81
+ self, message_id: str, recipient_id: str, message: str, preview_url: bool = True
82
+ ):
83
+ """
84
+ Replies to a message
85
+
86
+ Args:
87
+ message_id[str]: Message id of the message to be replied to
88
+ recipient_id[str]: Phone number of the user with country code wihout +
89
+ message[str]: Message to be sent to the user
90
+ preview_url[bool]: Whether to send a preview url or not
91
+ """
92
+ data = {
93
+ "messaging_product": "whatsapp",
94
+ "recipient_type": "individual",
95
+ "to": recipient_id,
96
+ "type": "text",
97
+ "context": {"message_id": message_id},
98
+ "text": {"preview_url": preview_url, "body": message},
99
+ }
100
+
101
+ logging.info(f"Replying to {message_id}")
102
+ r = requests.post(f"{self.url}", headers=self.headers, json=data)
103
+ if r.status_code == 200:
104
+ logging.info(f"Message sent to {recipient_id}")
105
+ return r.json()
106
+ logging.info(f"Message not sent to {recipient_id}")
107
+ logging.info(f"Status code: {r.status_code}")
108
+ logging.error(f"Response: {r.json()}")
109
+ return r.json()
110
+
111
+ def send_template(self, template, recipient_id, components, lang: str = "en_US"):
112
+ """
113
+ Sends a template message to a WhatsApp user, Template messages can either be;
114
+ 1. Text template
115
+ 2. Media based template
116
+ 3. Interactive template
117
+ You can customize the template message by passing a dictionary of components.
118
+ You can find the available components in the documentation.
119
+ https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates
120
+ Args:
121
+ template[str]: Template name to be sent to the user
122
+ recipient_id[str]: Phone number of the user with country code wihout +
123
+ lang[str]: Language of the template message
124
+ components[list]: List of components to be sent to the user # CHANGE
125
+ Example:
126
+ >>> from whatsapp import WhatsApp
127
+ >>> whatsapp = WhatsApp(token, phone_number_id)
128
+ >>> whatsapp.send_template("hello_world", "5511999999999", lang="en_US"))
129
+ """
130
+ data = {
131
+ "messaging_product": "whatsapp",
132
+ "to": recipient_id,
133
+ "type": "template",
134
+ "template": {
135
+ "name": template,
136
+ "language": {"code": lang},
137
+ "components": components,
138
+ },
139
+ }
140
+ logging.info(f"Sending template to {recipient_id}")
141
+ r = requests.post(self.url, headers=self.headers, json=data)
142
+ if r.status_code == 200:
143
+ logging.info(f"Template sent to {recipient_id}")
144
+ return r.json()
145
+ logging.info(f"Template not sent to {recipient_id}")
146
+ logging.info(f"Status code: {r.status_code}")
147
+ logging.error(f"Response: {r.json()}")
148
+ return r.json()
149
+
150
+ def send_templatev2(self, template, recipient_id, components, lang: str = "en_US"):
151
+ message = f"{Fore.RED}The 'send_templatev2' method is being deprecated and will be removed in the future. Please use the 'send_template' method instead.{Style.RESET_ALL}"
152
+ warnings.warn(message, DeprecationWarning)
153
+ return send_template(template, recipient_id, components, lang=lang) # type: ignore
154
+
155
+ def send_location(self, lat, long, name, address, recipient_id):
156
+ """
157
+ Sends a location message to a WhatsApp user
158
+
159
+ Args:
160
+ lat[str]: Latitude of the location
161
+ long[str]: Longitude of the location
162
+ name[str]: Name of the location
163
+ address[str]: Address of the location
164
+ recipient_id[str]: Phone number of the user with country code wihout +
165
+
166
+ Example:
167
+ >>> from whatsapp import WhatsApp
168
+ >>> whatsapp = WhatsApp(token, phone_number_id)
169
+ >>> whatsapp.send_location("-23.564", "-46.654", "My Location", "Rua dois, 123", "5511999999999")
170
+ """
171
+ data = {
172
+ "messaging_product": "whatsapp",
173
+ "to": recipient_id,
174
+ "type": "location",
175
+ "location": {
176
+ "latitude": lat,
177
+ "longitude": long,
178
+ "name": name,
179
+ "address": address,
180
+ },
181
+ }
182
+ logging.info(f"Sending location to {recipient_id}")
183
+ r = requests.post(self.url, headers=self.headers, json=data)
184
+ if r.status_code == 200:
185
+ logging.info(f"Location sent to {recipient_id}")
186
+ return r.json()
187
+ logging.info(f"Location not sent to {recipient_id}")
188
+ logging.info(f"Status code: {r.status_code}")
189
+ logging.error(r.json())
190
+ return r.json()
191
+
192
+ def send_image(
193
+ self,
194
+ image,
195
+ recipient_id,
196
+ recipient_type="individual",
197
+ caption=None,
198
+ link=True,
199
+ ):
200
+ """
201
+ Sends an image message to a WhatsApp user
202
+
203
+ There are two ways to send an image message to a user, either by passing the image id or by passing the image link.
204
+ Image id is the id of the image uploaded to the cloud api.
205
+
206
+ Args:
207
+ image[str]: Image id or link of the image
208
+ recipient_id[str]: Phone number of the user with country code wihout +
209
+ recipient_type[str]: Type of the recipient, either individual or group
210
+ caption[str]: Caption of the image
211
+ link[bool]: Whether to send an image id or an image link, True means that the image is an id, False means that the image is a link
212
+
213
+
214
+ Example:
215
+ >>> from whatsapp import WhatsApp
216
+ >>> whatsapp = WhatsApp(token, phone_number_id)
217
+ >>> whatsapp.send_image("https://i.imgur.com/Fh7XVYY.jpeg", "5511999999999")
218
+ """
219
+ if link:
220
+ data = {
221
+ "messaging_product": "whatsapp",
222
+ "recipient_type": recipient_type,
223
+ "to": recipient_id,
224
+ "type": "image",
225
+ "image": {"link": image, "caption": caption},
226
+ }
227
+ else:
228
+ data = {
229
+ "messaging_product": "whatsapp",
230
+ "recipient_type": recipient_type,
231
+ "to": recipient_id,
232
+ "type": "image",
233
+ "image": {"id": image, "caption": caption},
234
+ }
235
+ logging.info(f"Sending image to {recipient_id}")
236
+ r = requests.post(self.url, headers=self.headers, json=data)
237
+ if r.status_code == 200:
238
+ logging.info(f"Image sent to {recipient_id}")
239
+ return r.json()
240
+ logging.info(f"Image not sent to {recipient_id}")
241
+ logging.info(f"Status code: {r.status_code}")
242
+ logging.error(r.json())
243
+ return r.json()
244
+
245
+ def send_sticker(
246
+ self, sticker: str, recipient_id: str, recipient_type="individual", link=True
247
+ ):
248
+ """
249
+ Sends a sticker message to a WhatsApp user
250
+
251
+ There are two ways to send a sticker message to a user, either by passing the image id or by passing the sticker link.
252
+ Sticker id is the id of the sticker uploaded to the cloud api.
253
+
254
+ Args:
255
+ sticker[str]: Sticker id or link of the sticker
256
+ recipient_id[str]: Phone number of the user with country code wihout +
257
+ recipient_type[str]: Type of the recipient, either individual or group
258
+ link[bool]: Whether to send an sticker id or an sticker link, True means that the sticker is an id, False means that the image is a link
259
+
260
+
261
+ Example:
262
+ >>> from whatsapp import WhatsApp
263
+ >>> whatsapp = WhatsApp(token, phone_number_id)
264
+ >>> whatsapp.send_sticker("170511049062862", "5511999999999", link=False)
265
+ """
266
+ if link:
267
+ data = {
268
+ "messaging_product": "whatsapp",
269
+ "recipient_type": recipient_type,
270
+ "to": recipient_id,
271
+ "type": "sticker",
272
+ "sticker": {"link": sticker},
273
+ }
274
+ else:
275
+ data = {
276
+ "messaging_product": "whatsapp",
277
+ "recipient_type": recipient_type,
278
+ "to": recipient_id,
279
+ "type": "sticker",
280
+ "sticker": {"id": sticker},
281
+ }
282
+ logging.info(f"Sending sticker to {recipient_id}")
283
+ r = requests.post(self.url, headers=self.headers, json=data)
284
+ if r.status_code == 200:
285
+ logging.info(f"Sticker sent to {recipient_id}")
286
+ return r.json()
287
+ logging.info(f"Sticker not sent to {recipient_id}")
288
+ logging.info(f"Status code: {r.status_code}")
289
+ logging.error(r.json())
290
+ return r.json()
291
+
292
+ def send_audio(self, audio, recipient_id, link=True):
293
+ """
294
+ Sends an audio message to a WhatsApp user
295
+ Audio messages can either be sent by passing the audio id or by passing the audio link.
296
+
297
+ Args:
298
+ audio[str]: Audio id or link of the audio
299
+ recipient_id[str]: Phone number of the user with country code wihout +
300
+ link[bool]: Whether to send an audio id or an audio link, True means that the audio is an id, False means that the audio is a link
301
+
302
+ Example:
303
+ >>> from whatsapp import WhatsApp
304
+ >>> whatsapp = WhatsApp(token, phone_number_id)
305
+ >>> whatsapp.send_audio("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3", "5511999999999")
306
+ """
307
+ if link:
308
+ data = {
309
+ "messaging_product": "whatsapp",
310
+ "to": recipient_id,
311
+ "type": "audio",
312
+ "audio": {"link": audio},
313
+ }
314
+ else:
315
+ data = {
316
+ "messaging_product": "whatsapp",
317
+ "to": recipient_id,
318
+ "type": "audio",
319
+ "audio": {"id": audio},
320
+ }
321
+ logging.info(f"Sending audio to {recipient_id}")
322
+ r = requests.post(self.url, headers=self.headers, json=data)
323
+ if r.status_code == 200:
324
+ logging.info(f"Audio sent to {recipient_id}")
325
+ return r.json()
326
+ logging.info(f"Audio not sent to {recipient_id}")
327
+ logging.info(f"Status code: {r.status_code}")
328
+ logging.error(f"Response: {r.json()}")
329
+ return r.json()
330
+
331
+ def send_video(
332
+ self, video, recipient_id, caption=None, link=True
333
+ ) -> Dict[Any, Any]:
334
+ """ "
335
+ Sends a video message to a WhatsApp user
336
+ Video messages can either be sent by passing the video id or by passing the video link.
337
+
338
+ Args:
339
+ video[str]: Video id or link of the video
340
+ recipient_id[str]: Phone number of the user with country code wihout +
341
+ caption[str]: Caption of the video
342
+ link[bool]: Whether to send a video id or a video link, True means that the video is an id, False means that the video is a link
343
+
344
+ example:
345
+ >>> from whatsapp import WhatsApp
346
+ >>> whatsapp = WhatsApp(token, phone_number_id)
347
+ >>> whatsapp.send_video("https://www.youtube.com/watch?v=dQw4w9WgXcQ", "5511999999999")
348
+ """
349
+ if link:
350
+ data = {
351
+ "messaging_product": "whatsapp",
352
+ "to": recipient_id,
353
+ "type": "video",
354
+ "video": {"link": video, "caption": caption},
355
+ }
356
+ else:
357
+ data = {
358
+ "messaging_product": "whatsapp",
359
+ "to": recipient_id,
360
+ "type": "video",
361
+ "video": {"id": video, "caption": caption},
362
+ }
363
+ logging.info(f"Sending video to {recipient_id}")
364
+ r = requests.post(self.url, headers=self.headers, json=data)
365
+ if r.status_code == 200:
366
+ logging.info(f"Video sent to {recipient_id}")
367
+ return r.json()
368
+ logging.info(f"Video not sent to {recipient_id}")
369
+ logging.info(f"Status code: {r.status_code}")
370
+ logging.error(f"Response: {r.json()}")
371
+ return r.json()
372
+
373
+ def send_custom_json(self, data, recipient_id=None):
374
+ """
375
+ Sends a custom json to a WhatsApp user. This can be used to send custom objects to the message endpoint.
376
+
377
+ Args:
378
+ data[dict]: Dictionary that should be send
379
+ recipient_id[str]: Phone number of the user with country code wihout +
380
+ Example:
381
+ >>> from whatsapp import WhatsApp
382
+ >>> whatsapp = WhatsApp(token, phone_number_id)
383
+ >>> whatsapp.send_custom_json({
384
+ "messaging_product": "whatsapp",
385
+ "type": "audio",
386
+ "audio": {"id": audio}}, "5511999999999")
387
+ """
388
+
389
+ if recipient_id:
390
+ if "to" in data.keys():
391
+ data_recipient_id = data["to"]
392
+ logging.info(
393
+ f"Recipient Id is defined in data ({data_recipient_id}) and recipient_id parameter ({recipient_id})"
394
+ )
395
+ else:
396
+ data["to"] = recipient_id
397
+
398
+ logging.info(f"Sending custom json to {recipient_id}")
399
+ r = requests.post(self.url, headers=self.headers, json=data)
400
+ if r.status_code == 200:
401
+ logging.info(f"Custom json sent to {recipient_id}")
402
+ return r.json()
403
+ logging.info(f"Custom json not sent to {recipient_id}")
404
+ logging.info(f"Status code: {r.status_code}")
405
+ logging.error(f"Response: {r.json()}")
406
+ return r.json()
407
+
408
+ def send_document(
409
+ self, document, recipient_id, caption=None, link=True
410
+ ) -> Dict[Any, Any]:
411
+ """ "
412
+ Sends a document message to a WhatsApp user
413
+ Document messages can either be sent by passing the document id or by passing the document link.
414
+
415
+ Args:
416
+ document[str]: Document id or link of the document
417
+ recipient_id[str]: Phone number of the user with country code wihout +
418
+ caption[str]: Caption of the document
419
+ link[bool]: Whether to send a document id or a document link, True means that the document is an id, False means that the document is a link
420
+
421
+ Example:
422
+ >>> from whatsapp import WhatsApp
423
+ >>> whatsapp = WhatsApp(token, phone_number_id)
424
+ >>> whatsapp.send_document("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf", "5511999999999")
425
+ """
426
+ if link:
427
+ data = {
428
+ "messaging_product": "whatsapp",
429
+ "to": recipient_id,
430
+ "type": "document",
431
+ "document": {"link": document, "caption": caption},
432
+ }
433
+ else:
434
+ data = {
435
+ "messaging_product": "whatsapp",
436
+ "to": recipient_id,
437
+ "type": "document",
438
+ "document": {"id": document, "caption": caption},
439
+ }
440
+
441
+ logging.info(f"Sending document to {recipient_id}")
442
+ r = requests.post(self.url, headers=self.headers, json=data)
443
+ if r.status_code == 200:
444
+ logging.info(f"Document sent to {recipient_id}")
445
+ return r.json()
446
+ logging.info(f"Document not sent to {recipient_id}")
447
+ logging.info(f"Status code: {r.status_code}")
448
+ logging.error(f"Response: {r.json()}")
449
+ return r.json()
450
+
451
+ def send_contacts(
452
+ self, contacts: List[Dict[Any, Any]], recipient_id: str
453
+ ) -> Dict[Any, Any]:
454
+ """send_contacts
455
+
456
+ Send a list of contacts to a user
457
+
458
+ Args:
459
+ contacts(List[Dict[Any, Any]]): List of contacts to send
460
+ recipient_id(str): Phone number of the user with country code wihout +
461
+
462
+ Example:
463
+ >>> from whatsapp import WhatsApp
464
+ >>> whatsapp = WhatsApp(token, phone_number_id)
465
+ >>> contacts = [{
466
+ "addresses": [{
467
+ "street": "STREET",
468
+ "city": "CITY",
469
+ "state": "STATE",
470
+ "zip": "ZIP",
471
+ "country": "COUNTRY",
472
+ "country_code": "COUNTRY_CODE",
473
+ "type": "HOME"
474
+ },
475
+ ....
476
+ }
477
+ ]
478
+
479
+ REFERENCE: https://developers.facebook.com/docs/whatsapp/cloud-api/reference/messages#contacts-object
480
+ """
481
+
482
+ data = {
483
+ "messaging_product": "whatsapp",
484
+ "to": recipient_id,
485
+ "type": "contacts",
486
+ "contacts": contacts,
487
+ }
488
+ logging.info(f"Sending contacts to {recipient_id}")
489
+ r = requests.post(self.url, headers=self.headers, json=data)
490
+ if r.status_code == 200:
491
+ logging.info(f"Contacts sent to {recipient_id}")
492
+ return r.json()
493
+ logging.info(f"Contacts not sent to {recipient_id}")
494
+ logging.info(f"Status code: {r.status_code}")
495
+ logging.error(f"Response: {r.json()}")
496
+ return r.json()
497
+
498
+ def upload_media(self, media: str) -> Union[Dict[Any, Any], None]:
499
+ """
500
+ Uploads a media to the cloud api and returns the id of the media
501
+
502
+ Args:
503
+ media[str]: Path of the media to be uploaded
504
+
505
+ Example:
506
+ >>> from whatsapp import WhatsApp
507
+ >>> whatsapp = WhatsApp(token, phone_number_id)
508
+ >>> whatsapp.upload_media("/path/to/media")
509
+
510
+ REFERENCE: https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#
511
+ """
512
+ form_data = {
513
+ "file": (
514
+ media,
515
+ open(os.path.realpath(media), "rb"),
516
+ mimetypes.guess_type(media)[0],
517
+ ),
518
+ "messaging_product": "whatsapp",
519
+ "type": mimetypes.guess_type(media)[0],
520
+ }
521
+ form_data = MultipartEncoder(fields=form_data)
522
+ headers = self.headers.copy()
523
+ headers["Content-Type"] = form_data.content_type
524
+ logging.info(f"Content-Type: {form_data.content_type}")
525
+ logging.info(f"Uploading media {media}")
526
+ r = requests.post(
527
+ f"{self.base_url}/{self.phone_number_id}/media",
528
+ headers=headers,
529
+ data=form_data,
530
+ )
531
+ if r.status_code == 200:
532
+ logging.info(f"Media {media} uploaded")
533
+ return r.json()
534
+ logging.info(f"Error uploading media {media}")
535
+ logging.info(f"Status code: {r.status_code}")
536
+ logging.info(f"Response: {r.json()}")
537
+ return None
538
+
539
+ def delete_media(self, media_id: str) -> Union[Dict[Any, Any], None]:
540
+ """
541
+ Deletes a media from the cloud api
542
+
543
+ Args:
544
+ media_id[str]: Id of the media to be deleted
545
+ """
546
+ logging.info(f"Deleting media {media_id}")
547
+ r = requests.delete(f"{self.base_url}/{media_id}", headers=self.headers)
548
+ if r.status_code == 200:
549
+ logging.info(f"Media {media_id} deleted")
550
+ return r.json()
551
+ logging.info(f"Error deleting media {media_id}")
552
+ logging.info(f"Status code: {r.status_code}")
553
+ logging.info(f"Response: {r.json()}")
554
+ return None
555
+
556
+ def mark_as_read(self, message_id: str) -> Dict[Any, Any]:
557
+ """
558
+ Marks a message as read
559
+
560
+ Args:
561
+ message_id[str]: Id of the message to be marked as read
562
+
563
+ Returns:
564
+ Dict[Any, Any]: Response from the API
565
+
566
+ Example:
567
+ >>> from whatsapp import WhatsApp
568
+ >>> whatsapp = WhatsApp(token, phone_number_id)
569
+ >>> whatsapp.mark_as_read("message_id")
570
+ """
571
+ headers = {
572
+ "Authorization": f"Bearer {self.token}",
573
+ "Content-Type": "application/json",
574
+ }
575
+
576
+ json_data = {
577
+ "messaging_product": "whatsapp",
578
+ "status": "read",
579
+ "message_id": message_id,
580
+ }
581
+ logging.info(f"Marking message {message_id} as read")
582
+ response = requests.post(
583
+ f"{self.v15_base_url}/{self.phone_number_id}/messages",
584
+ headers=headers,
585
+ json=json_data,
586
+ ).json()
587
+ if response.status_code == 200:
588
+ logging.info(f"Message {message_id} marked as read")
589
+ return response
590
+ logging.info(f"Error marking message {message_id} as read")
591
+ logging.info(f"Status code: {response.status_code}")
592
+ logging.info(f"Response: {response.json()}")
593
+ return response
594
+
595
+ def mark_as_read_by_winter(self, message_id: str):
596
+ """
597
+ Marks a message as read
598
+
599
+ Args:
600
+ message_id[str]: Id of the message to be marked as read
601
+
602
+ Returns:
603
+ Dict[Any, Any]: Response from the API
604
+
605
+ Example:
606
+ >>> from whatsapp import WhatsApp
607
+ >>> whatsapp = WhatsApp(token, phone_number_id)
608
+ >>> whatsapp.mark_as_read("message_id")
609
+ """
610
+ headers = {
611
+ "Authorization": f"Bearer {self.token}",
612
+ "Content-Type": "application/json",
613
+ }
614
+
615
+ json_data = {
616
+ "messaging_product": "whatsapp",
617
+ "status": "read",
618
+ "message_id": message_id,
619
+ }
620
+ logging.info(f"Marking message {message_id} as read")
621
+ requests.post(
622
+ f"{self.v15_base_url}/{self.phone_number_id}/messages",
623
+ headers=headers,
624
+ json=json_data,
625
+ ).json()
626
+
627
+ def create_button(self, button: Dict[Any, Any]) -> Dict[Any, Any]:
628
+ """
629
+ Method to create a button object to be used in the send_message method.
630
+
631
+ This is method is designed to only be used internally by the send_button method.
632
+
633
+ Args:
634
+ button[dict]: A dictionary containing the button data
635
+ """
636
+ data = {"type": "list", "action": button.get("action")}
637
+ if button.get("header"):
638
+ data["header"] = {"type": "text", "text": button.get("header")}
639
+ if button.get("body"):
640
+ data["body"] = {"text": button.get("body")}
641
+ if button.get("footer"):
642
+ data["footer"] = {"text": button.get("footer")}
643
+ return data
644
+
645
+ def send_button(self, button: Dict[Any, Any], recipient_id: str) -> Dict[Any, Any]:
646
+ """
647
+ Sends an interactive buttons message to a WhatsApp user
648
+
649
+ Args:
650
+ button[dict]: A dictionary containing the button data(rows-title may not exceed 20 characters)
651
+ recipient_id[str]: Phone number of the user with country code wihout +
652
+
653
+ check https://github.com/Neurotech-HQ/heyoo#sending-interactive-reply-buttons for an example.
654
+ """
655
+ data = {
656
+ "messaging_product": "whatsapp",
657
+ "to": recipient_id,
658
+ "type": "interactive",
659
+ "interactive": self.create_button(button),
660
+ }
661
+ logging.info(f"Sending buttons to {recipient_id}")
662
+ r = requests.post(self.url, headers=self.headers, json=data)
663
+ if r.status_code == 200:
664
+ logging.info(f"Buttons sent to {recipient_id}")
665
+ return r.json()
666
+ logging.info(f"Buttons not sent to {recipient_id}")
667
+ logging.info(f"Status code: {r.status_code}")
668
+ logging.info(f"Response: {r.json()}")
669
+ return r.json()
670
+
671
+ def send_reply_button(
672
+ self, button: Dict[Any, Any], recipient_id: str
673
+ ) -> Dict[Any, Any]:
674
+ """
675
+ Sends an interactive reply buttons[menu] message to a WhatsApp user
676
+
677
+ Args:
678
+ button[dict]: A dictionary containing the button data
679
+ recipient_id[str]: Phone number of the user with country code wihout +
680
+
681
+ Note:
682
+ The maximum number of buttons is 3, more than 3 buttons will rise an error.
683
+ """
684
+ data = {
685
+ "messaging_product": "whatsapp",
686
+ "recipient_type": "individual",
687
+ "to": recipient_id,
688
+ "type": "interactive",
689
+ "interactive": button,
690
+ }
691
+ r = requests.post(self.url, headers=self.headers, json=data)
692
+ if r.status_code == 200:
693
+ logging.info(f"Reply buttons sent to {recipient_id}")
694
+ return r.json()
695
+ logging.info(f"Reply buttons not sent to {recipient_id}")
696
+ logging.info(f"Status code: {r.status_code}")
697
+ logging.info(f"Response: {r.json()}")
698
+ return r.json()
699
+
700
+ def query_media_url(self, media_id: str) -> Union[str, None]:
701
+ """
702
+ Query media url from media id obtained either by manually uploading media or received media
703
+
704
+ Args:
705
+ media_id[str]: Media id of the media
706
+
707
+ Returns:
708
+ str: Media url
709
+
710
+ Example:
711
+ >>> from whatsapp import WhatsApp
712
+ >>> whatsapp = WhatsApp(token, phone_number_id)
713
+ >>> whatsapp.query_media_url("media_id")
714
+ """
715
+
716
+ logging.info(f"Querying media url for {media_id}")
717
+ r = requests.get(f"{self.base_url}/{media_id}", headers=self.headers)
718
+ if r.status_code == 200:
719
+ logging.info(f"Media url queried for {media_id}")
720
+ return r.json()["url"]
721
+ logging.info(f"Media url not queried for {media_id}")
722
+ logging.info(f"Status code: {r.status_code}")
723
+ logging.info(f"Response: {r.json()}")
724
+ return None
725
+
726
+ def download_media(
727
+ self, media_url: str, mime_type: str, file_path: str = "temp"
728
+ ) -> Union[str, None]:
729
+ """
730
+ Download media from media url obtained either by manually uploading media or received media
731
+
732
+ Args:
733
+ media_url[str]: Media url of the media
734
+ mime_type[str]: Mime type of the media
735
+ file_path[str]: Path of the file to be downloaded to. Default is "temp"
736
+ Do not include the file extension. It will be added automatically.
737
+
738
+ Returns:
739
+ str: Media url
740
+
741
+ Example:
742
+ >>> from whatsapp import WhatsApp
743
+ >>> whatsapp = WhatsApp(token, phone_number_id)
744
+ >>> whatsapp.download_media("media_url", "image/jpeg")
745
+ >>> whatsapp.download_media("media_url", "video/mp4", "path/to/file") #do not include the file extension
746
+ """
747
+ r = requests.get(media_url, headers=self.headers)
748
+ content = r.content
749
+ extension = mime_type.split("/")[1]
750
+ # create a temporary file
751
+ try:
752
+ save_file_here = (
753
+ f"{file_path}.{extension}" if file_path else f"temp.{extension}"
754
+ )
755
+ with open(save_file_here, "wb") as f:
756
+ f.write(content)
757
+ logging.info(f"Media downloaded to {save_file_here}")
758
+ return f.name
759
+ except Exception as e:
760
+ logging.info(e)
761
+ logging.ERROR(f"Error downloading media to {save_file_here}")
762
+ return None
763
+
764
+ def preprocess(self, data: Dict[Any, Any]) -> Dict[Any, Any]:
765
+ """
766
+ Preprocesses the data received from the webhook.
767
+
768
+ This method is designed to only be used internally.
769
+
770
+ Args:
771
+ data[dict]: The data received from the webhook
772
+ """
773
+ return data["entry"][0]["changes"][0]["value"]
774
+
775
+ def is_message(self, data: Dict[Any, Any]) -> bool:
776
+ """is_message checks if the data received from the webhook is a message.
777
+
778
+ Args:
779
+ data (Dict[Any, Any]): The data received from the webhook
780
+
781
+ Returns:
782
+ bool: True if the data is a message, False otherwise
783
+ """
784
+ data = self.preprocess(data)
785
+ if "messages" in data:
786
+ return True
787
+ else:
788
+ return False
789
+
790
+ def get_mobile(self, data: Dict[Any, Any]) -> Union[str, None]:
791
+ """
792
+ Extracts the mobile number of the sender from the data received from the webhook.
793
+
794
+ Args:
795
+ data[dict]: The data received from the webhook
796
+ Returns:
797
+ str: The mobile number of the sender
798
+
799
+ Example:
800
+ >>> from whatsapp import WhatsApp
801
+ >>> whatsapp = WhatsApp(token, phone_number_id)
802
+ >>> mobile = whatsapp.get_mobile(data)
803
+ """
804
+ data = self.preprocess(data)
805
+ if "contacts" in data:
806
+ return data["contacts"][0]["wa_id"]
807
+
808
+ def get_name(self, data: Dict[Any, Any]) -> Union[str, None]:
809
+ """
810
+ Extracts the name of the sender from the data received from the webhook.
811
+
812
+ Args:
813
+ data[dict]: The data received from the webhook
814
+ Returns:
815
+ str: The name of the sender
816
+ Example:
817
+ >>> from whatsapp import WhatsApp
818
+ >>> whatsapp = WhatsApp(token, phone_number_id)
819
+ >>> mobile = whatsapp.get_name(data)
820
+ """
821
+ contact = self.preprocess(data)
822
+ if contact:
823
+ return contact["contacts"][0]["profile"]["name"]
824
+
825
+ def get_message(self, data: Dict[Any, Any]) -> Union[str, None]:
826
+ """
827
+ Extracts the text message of the sender from the data received from the webhook.
828
+
829
+ Args:
830
+ data[dict]: The data received from the webhook
831
+ Returns:
832
+ str: The text message received from the sender
833
+ Example:
834
+ >>> from whatsapp import WhatsApp
835
+ >>> whatsapp = WhatsApp(token, phone_number_id)
836
+ >>> message = message.get_message(data)
837
+ """
838
+ data = self.preprocess(data)
839
+ if "messages" in data:
840
+ return data["messages"][0]["text"]["body"]
841
+
842
+ def get_message_id(self, data: Dict[Any, Any]) -> Union[str, None]:
843
+ """
844
+ Extracts the message id of the sender from the data received from the webhook.
845
+
846
+ Args:
847
+ data[dict]: The data received from the webhook
848
+ Returns:
849
+ str: The message id of the sender
850
+ Example:
851
+ >>> from whatsapp import WhatsApp
852
+ >>> whatsapp = WhatsApp(token, phone_number_id)
853
+ >>> message_id = whatsapp.get_message_id(data)
854
+ """
855
+ data = self.preprocess(data)
856
+ if "messages" in data:
857
+ return data["messages"][0]["id"]
858
+ return None
859
+
860
+ def get_conversation_id(self, data: Dict[Any, Any]) -> Union[str, None]:
861
+ """
862
+ Extracts the conversation id from the data received from the webhook.
863
+
864
+ Args:
865
+ data[dict]: The data received from the webhook
866
+ Returns:
867
+ str: The conversation id, or None if not found
868
+ Example:
869
+ >>> from whatsapp import WhatsApp
870
+ >>> whatsapp = WhatsApp(token, phone_number_id)
871
+ >>> conversation_id = whatsapp.get_conversation_id(data)
872
+ """
873
+ data = self.preprocess(data)
874
+ if "conversation_id" in data:
875
+ return data.get("conversation_id")
876
+ return None
877
+
878
+ def get_message_timestamp(self, data: Dict[Any, Any]) -> Union[str, None]:
879
+ """ "
880
+ Extracts the timestamp of the message from the data received from the webhook.
881
+
882
+ Args:
883
+ data[dict]: The data received from the webhook
884
+ Returns:
885
+ str: The timestamp of the message
886
+ Example:
887
+ >>> from whatsapp import WhatsApp
888
+ >>> whatsapp = WhatsApp(token, phone_number_id)
889
+ >>> whatsapp.get_message_timestamp(data)
890
+ """
891
+ data = self.preprocess(data)
892
+ if "messages" in data:
893
+ return data["messages"][0]["timestamp"]
894
+
895
+ def get_interactive_response(self, data: Dict[Any, Any]) -> Union[Dict, None]:
896
+ """
897
+ Extracts the response of the interactive message from the data received from the webhook.
898
+
899
+ Args:
900
+ data[dict]: The data received from the webhook
901
+ Returns:
902
+ dict: The response of the interactive message
903
+
904
+ Example:
905
+ >>> from whatsapp import WhatsApp
906
+ >>> whatsapp = WhatsApp(token, phone_number_id)
907
+ >>> response = whatsapp.get_interactive_response(data)
908
+ >>> interactive_type = response.get("type")
909
+ >>> message_id = response[interactive_type]["id"]
910
+ >>> message_text = response[interactive_type]["title"]
911
+ """
912
+ data = self.preprocess(data)
913
+ if "messages" in data:
914
+ if "interactive" in data["messages"][0]:
915
+ return data["messages"][0]["interactive"]
916
+
917
+ def get_location(self, data: Dict[Any, Any]) -> Union[Dict, None]:
918
+ """
919
+ Extracts the location of the sender from the data received from the webhook.
920
+
921
+ Args:
922
+ data[dict]: The data received from the webhook
923
+
924
+ Returns:
925
+ dict: The location of the sender
926
+
927
+ Example:
928
+ >>> from whatsapp import WhatsApp
929
+ >>> whatsapp = WhatsApp(token, phone_number_id)
930
+ >>> whatsapp.get_location(data)
931
+ """
932
+ data = self.preprocess(data)
933
+ if "messages" in data:
934
+ if "location" in data["messages"][0]:
935
+ return data["messages"][0]["location"]
936
+
937
+ def get_image(self, data: Dict[Any, Any]) -> Union[Dict, None]:
938
+ """ "
939
+ Extracts the image of the sender from the data received from the webhook.
940
+
941
+ Args:
942
+ data[dict]: The data received from the webhook
943
+ Returns:
944
+ dict: The image_id of an image sent by the sender
945
+
946
+ Example:
947
+ >>> from whatsapp import WhatsApp
948
+ >>> whatsapp = WhatsApp(token, phone_number_id)
949
+ >>> image_id = whatsapp.get_image(data)
950
+ """
951
+ data = self.preprocess(data)
952
+ if "messages" in data:
953
+ if "image" in data["messages"][0]:
954
+ return data["messages"][0]["image"]
955
+
956
+ def get_document(self, data: Dict[Any, Any]) -> Union[Dict, None]:
957
+ """ "
958
+ Extracts the document of the sender from the data received from the webhook.
959
+
960
+ Args:
961
+ data[dict]: The data received from the webhook
962
+ Returns:
963
+ dict: The document_id of an image sent by the sender
964
+
965
+ Example:
966
+ >>> from whatsapp import WhatsApp
967
+ >>> whatsapp = WhatsApp(token, phone_number_id)
968
+ >>> document_id = whatsapp.get_document(data)
969
+ """
970
+ data = self.preprocess(data)
971
+ if "messages" in data:
972
+ if "document" in data["messages"][0]:
973
+ return data["messages"][0]["document"]
974
+
975
+ def get_audio(self, data: Dict[Any, Any]) -> Union[Dict, None]:
976
+ """
977
+ Extracts the audio of the sender from the data received from the webhook.
978
+
979
+ Args:
980
+ data[dict]: The data received from the webhook
981
+
982
+ Returns:
983
+ dict: The audio of the sender
984
+
985
+ Example:
986
+ >>> from whatsapp import WhatsApp
987
+ >>> whatsapp = WhatsApp(token, phone_number_id)
988
+ >>> whatsapp.get_audio(data)
989
+ """
990
+ data = self.preprocess(data)
991
+ if "messages" in data:
992
+ if "audio" in data["messages"][0]:
993
+ return data["messages"][0]["audio"]
994
+
995
+ def get_video(self, data: Dict[Any, Any]) -> Union[Dict, None]:
996
+ """
997
+ Extracts the video of the sender from the data received from the webhook.
998
+
999
+ Args:
1000
+ data[dict]: The data received from the webhook
1001
+
1002
+ Returns:
1003
+ dict: Dictionary containing the video details sent by the sender
1004
+
1005
+ Example:
1006
+ >>> from whatsapp import WhatsApp
1007
+ >>> whatsapp = WhatsApp(token, phone_number_id)
1008
+ >>> whatsapp.get_video(data)
1009
+ """
1010
+ data = self.preprocess(data)
1011
+ if "messages" in data:
1012
+ if "video" in data["messages"][0]:
1013
+ return data["messages"][0]["video"]
1014
+
1015
+ def get_message_type(self, data: Dict[Any, Any]) -> Union[str, None]:
1016
+ """
1017
+ Gets the type of the message sent by the sender from the data received from the webhook.
1018
+
1019
+
1020
+ Args:
1021
+ data [dict]: The data received from the webhook
1022
+
1023
+ Returns:
1024
+ str: The type of the message sent by the sender
1025
+
1026
+ Example:
1027
+ >>> from whatsapp import WhatsApp
1028
+ >>> whatsapp = WhatsApp(token, phone_number_id)
1029
+ >>> whatsapp.get_message_type(data)
1030
+ """
1031
+ data = self.preprocess(data)
1032
+ if "messages" in data:
1033
+ return data["messages"][0]["type"]
1034
+
1035
+ def get_delivery(self, data: Dict[Any, Any]) -> Union[Dict, None]:
1036
+ """
1037
+ Extracts the delivery status of the message from the data received from the webhook.
1038
+ Args:
1039
+ data [dict]: The data received from the webhook
1040
+
1041
+ Returns:
1042
+ dict: The delivery status of the message and message id of the message
1043
+ """
1044
+ data = self.preprocess(data)
1045
+ if "statuses" in data:
1046
+ return data["statuses"][0]["status"]
1047
+
1048
+ def changed_field(self, data: Dict[Any, Any]) -> str:
1049
+ """
1050
+ Helper function to check if the field changed in the data received from the webhook.
1051
+
1052
+ Args:
1053
+ data [dict]: The data received from the webhook
1054
+
1055
+ Returns:
1056
+ str: The field changed in the data received from the webhook
1057
+
1058
+ Example:
1059
+ >>> from whatsapp import WhatsApp
1060
+ >>> whatsapp = WhatsApp(token, phone_number_id)
1061
+ >>> whatsapp.changed_field(data)
1062
+ """
1063
+ return data["entry"][0]["changes"][0]["field"]