whatsapp-python 3.1.1__py3-none-any.whl → 3.2.5__py3-none-any.whl

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.
whatsapp/__init__.py CHANGED
@@ -1,280 +1,275 @@
1
- """
2
- Unofficial Python wrapper for the WhatsApp Cloud API.
3
- """
4
- from __future__ import annotations
5
-
6
- import requests
7
- import logging
8
- from fastapi import FastAPI, HTTPException, Request
9
- from uvicorn import run as _run
10
- from .constants import VERSION
11
- from . import ext
12
- from .ext._property import authorized
13
- from .ext._send_others import send_custom_json, send_contacts
14
- from .ext._message import send_template
15
- from .ext._send_media import send_image, send_video, send_audio, send_location, send_sticker, send_document
16
- from .ext._media import upload_media, query_media_url, download_media, delete_media
17
- from .ext._buttons import send_button, create_button, send_reply_button
18
- from .ext._static import is_message, get_mobile, get_author, get_name, get_message, get_message_id, get_message_type, get_message_timestamp, get_audio, get_delivery, get_document, get_image, get_interactive_response, get_location, get_video, changed_field
19
- import json
20
-
21
- class WhatsApp(object):
22
- def __init__(self, token: str = "", phone_number_id: str = "", logger: bool = True, update_check: bool = True, verify_token: str = ""):
23
- """
24
- Initialize the WhatsApp Object
25
-
26
- Args:
27
- token[str]: Token for the WhatsApp cloud API obtained from the Facebook developer portal
28
- phone_number_id[str]: Phone number id for the WhatsApp cloud API obtained from the developer portal
29
- logger[bool]: Whether to enable logging or not (default: True)
30
- """
31
-
32
- # Check if the version is up to date
33
- logging.getLogger(__name__).addHandler(logging.NullHandler())
34
-
35
- self.VERSION = VERSION
36
- if update_check is True:
37
- latest = str(requests.get(
38
- "https://pypi.org/pypi/whatsapp-python/json").json()["info"]["version"])
39
- if self.VERSION != latest:
40
- try:
41
- version_int = int(self.VERSION.replace(".", ""))
42
- except:
43
- version_int = 0
44
- latest_int = int(latest.replace(".", ""))
45
- # this is to avoid the case where the version is 1.0.10 and the latest is 1.0.2 (possible if user is using the github version)
46
- if version_int < latest_int:
47
- if version_int == 0:
48
- logging.critical(
49
- f"There was an error while checking for updates, please update the package manually or report the issue on GitHub.")
50
- else:
51
- logging.critical(
52
- f"Whatsapp-python is out of date. Please update to the latest version {latest}. READ THE CHANGELOG BEFORE UPDATING. NEW VERSIONS MAY BREAK YOUR CODE IF NOT PROPERLY UPDATED.")
53
- if version_int > latest_int:
54
- latest_beta = int(str(requests.get(
55
- "https://raw.githubusercontent.com/filipporomani/whatsapp/main/.version").text).replace(".", ""))
56
- if latest_beta > version_int:
57
- logging.critical(
58
- "A new beta version is available. Please update to the latest version. READ THE CHANGELOG BEFORE UPDATING. NEW VERSIONS MAY BREAK YOUR CODE IF NOT PROPERLY UPDATED.")
59
- logging.critical(
60
- f"You are using a development version of whatsapp-python. Please report any issue on GitHub.")
61
-
62
-
63
- if token == "":
64
- logging.error("Token not provided")
65
- raise ValueError("Token not provided but required")
66
- if phone_number_id == "":
67
- logging.error("Phone number id not provided")
68
- raise ValueError(
69
- "Phone number ID not provided but required")
70
- self.token = token
71
- self.phone_number_id = phone_number_id
72
- self.base_url = "https://graph.facebook.com/v18.0"
73
- self.url = f"{self.base_url}/{phone_number_id}/messages"
74
- self.verify_token = verify_token
75
-
76
-
77
- async def base(*args):
78
- pass
79
- self.message_handler = base
80
- self.other_handler = base
81
- self.verification_handler = base
82
- self.headers = {
83
- "Content-Type": "application/json",
84
- "Authorization": f"Bearer {self.token}"
85
- }
86
- if logger is False:
87
- logging.disable(logging.INFO)
88
- logging.disable(logging.ERROR)
89
-
90
- self.app = FastAPI()
91
-
92
- # Verification handler has 1 argument: challenge (str | bool): str if verification is successful, False if not
93
-
94
- @self.app.get("/")
95
- async def verify_endpoint(r: Request):
96
- if r.query_params.get("hub.verify_token") == self.verify_token:
97
- logging.info("Verified webhook")
98
- challenge = r.query_params.get("hub.challenge")
99
- self.verification_handler(challenge)
100
- self.other_handler(challenge)
101
- return int(challenge)
102
- logging.error("Webhook Verification failed")
103
- await self.verification_handler(False)
104
- await self.other_handler(False)
105
- return {"success": False}
106
-
107
- @self.app.post("/")
108
- async def hook(r: Request):
109
- try:
110
- # Handle Webhook Subscriptions
111
- data = await r.json()
112
- if data is None:
113
- return {"success": False}
114
- logging.info("Received webhook data: %s", data)
115
- data_str = json.dumps(data, indent=4)
116
- logging.debug(f"Received webhook data: {data_str}")
117
-
118
- changed_field = self.changed_field(data)
119
- if changed_field == "messages":
120
- new_message = self.is_message(data)
121
- if new_message:
122
- msg = Message(instance=self, data=data)
123
- await self.message_handler(msg)
124
- await self.other_handler(msg)
125
- return {"success": True}
126
- except Exception as e:
127
- logging.error(f"Error parsing message: {e}")
128
- raise HTTPException(status_code=500, detail={
129
- "success": False,
130
- "error": str(e)
131
- })
132
-
133
-
134
-
135
-
136
- # all the files starting with _ are imported here, and should not be imported directly.
137
-
138
- is_message = staticmethod(is_message)
139
- get_mobile = staticmethod(get_mobile)
140
- get_name = staticmethod(get_name)
141
- get_message = staticmethod(get_message)
142
- get_message_id = staticmethod(get_message_id)
143
- get_message_type = staticmethod(get_message_type)
144
- get_message_timestamp = staticmethod(get_message_timestamp)
145
- get_audio = staticmethod(get_audio)
146
- get_delivery = staticmethod(get_delivery)
147
- get_document = staticmethod(get_document)
148
- get_image = staticmethod(get_image)
149
- get_interactive_response = staticmethod(get_interactive_response)
150
- get_location = staticmethod(get_location)
151
- get_video = staticmethod(get_video)
152
- changed_field = staticmethod(changed_field)
153
- get_author = staticmethod(get_author)
154
-
155
- send_button = send_button
156
- create_button = create_button
157
- send_reply_button = send_reply_button
158
- send_image = send_image
159
- send_video = send_video
160
- send_audio = send_audio
161
- send_location = send_location
162
- send_sticker = send_sticker
163
- send_document = send_document
164
- upload_media = upload_media
165
- query_media_url = query_media_url
166
- download_media = download_media
167
- delete_media = delete_media
168
- send_template = send_template
169
- send_custom_json = send_custom_json
170
- send_contacts = send_contacts
171
-
172
- authorized = property(authorized)
173
-
174
- def create_message(self, **kwargs) -> Message:
175
- """
176
- Create a message object
177
-
178
- Args:
179
- data[dict]: The message data
180
- content[str]: The message content
181
- to[str]: The recipient
182
- rec_type[str]: The recipient type (individual/group)
183
- """
184
- return Message(**kwargs, instance=self)
185
-
186
- def on_message(self, handler: function):
187
- """
188
- Set the handler for incoming messages
189
-
190
- Args:
191
- handler[function]: The handler function
192
- """
193
- self.message_handler = handler
194
-
195
- def on_event(self, handler: function):
196
- """
197
- Set the handler for other events
198
-
199
- Args:
200
- handler[function]: The handler function
201
- """
202
- self.other_handler = handler
203
-
204
- def on_verification(self, handler: function):
205
- """
206
- Set the handler for verification
207
-
208
- Args:
209
- handler[function]: The handler function
210
- """
211
- self.verification_handler = handler
212
-
213
- def run(self, host: str = "localhost", port: int = 5000, **options):
214
- _run(self.app, host=host, port=port, **options)
215
-
216
-
217
- class Message(object):
218
- def __init__(self, id: int = None, data: dict = {}, instance: WhatsApp = None, content: str = "", to: str = "", rec_type: str = "individual"): # type: ignore
219
- self.instance = instance
220
- self.url = self.instance.url
221
- self.headers = self.instance.headers
222
-
223
- try:
224
- self.id = instance.get_message_id(data)
225
- except:
226
- self.id = id
227
- try:
228
- self.type = self.instance.get_message_type(data)
229
- except:
230
- self.type = "text"
231
- self.data = data
232
- self.rec = rec_type
233
- self.to = to
234
- try:
235
- self.content = content if content != "" else self.instance.get_message(
236
- data)
237
- except:
238
- self.content = content
239
- try:
240
- self.sender = self.instance.get_mobile(data)
241
- except:
242
- self.sender = None
243
- try:
244
- self.name = self.instance.get_name(data)
245
- except:
246
- self.name = None
247
-
248
- if self.type == "image":
249
- try:
250
- self.image = self.instance.get_image(data)
251
- except:
252
- self.image = None
253
- elif self.type == "video":
254
- try:
255
- self.video = self.instance.get_video(data)
256
- except:
257
- self.video = None
258
- elif self.type == "audio":
259
- try:
260
- self.audio = self.instance.get_audio(data)
261
- except:
262
- pass
263
- elif self.type == "document":
264
- try:
265
- self.document = self.instance.get_document(data)
266
- except:
267
- pass
268
- elif self.type == "location":
269
- try:
270
- self.location = self.instance.get_location(data)
271
- except:
272
- pass
273
- elif self.type == "interactive":
274
- try:
275
- self.interactive = self.instance.get_interactive_response(data)
276
- except:
277
- pass
278
-
279
-
280
- from .ext._message import send, reply, mark_as_read
1
+ """
2
+ Unofficial Python wrapper for the WhatsApp Cloud API.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ import requests
7
+ import logging
8
+ from fastapi import FastAPI, HTTPException, Request
9
+ from uvicorn import run as _run
10
+ from .constants import VERSION
11
+ from .ext._property import authorized
12
+ from .ext._send_others import send_custom_json, send_contacts
13
+ from .ext._message import send_template
14
+ from .ext._send_media import send_image, send_video, send_audio, send_location, send_sticker, send_document
15
+ from .ext._media import upload_media, query_media_url, download_media, delete_media
16
+ from .ext._buttons import send_button, create_button, send_reply_button
17
+ from .ext._static import is_message, get_mobile, get_author, get_name, get_message, get_message_id, get_message_type, get_message_timestamp, get_audio, get_delivery, get_document, get_image, get_sticker, get_interactive_response, get_location, get_video, changed_field
18
+ import json
19
+
20
+
21
+ class WhatsApp(object):
22
+ def __init__(self, token: str = "", phone_number_id: str = "", logger: bool = True, update_check: bool = True, verify_token: str = "", debug: bool = True):
23
+ """
24
+ Initialize the WhatsApp Object
25
+
26
+ Args:
27
+ token[str]: Token for the WhatsApp cloud API obtained from the Facebook developer portal
28
+ phone_number_id[str]: Phone number id for the WhatsApp cloud API obtained from the developer portal
29
+ logger[bool]: Whether to enable logging or not (default: True)
30
+ """
31
+
32
+ # Check if the version is up to date
33
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
34
+
35
+ self.VERSION = VERSION
36
+ if update_check is True:
37
+ latest = str(requests.get(
38
+ "https://pypi.org/pypi/whatsapp-python/json").json()["info"]["version"])
39
+ if self.VERSION != latest:
40
+ try:
41
+ version_int = int(self.VERSION.replace(".", ""))
42
+ except:
43
+ version_int = 0
44
+ latest_int = int(latest.replace(".", ""))
45
+ # this is to avoid the case where the version is 1.0.10 and the latest is 1.0.2 (possible if user is using the github version)
46
+ if version_int < latest_int:
47
+ if version_int == 0:
48
+ logging.critical(
49
+ f"There was an error while checking for updates, please update the package manually or report the issue on GitHub.")
50
+ else:
51
+ logging.critical(
52
+ f"Whatsapp-python is out of date. Please update to the latest version {latest}. READ THE CHANGELOG BEFORE UPDATING. NEW VERSIONS MAY BREAK YOUR CODE IF NOT PROPERLY UPDATED.")
53
+
54
+ if token == "":
55
+ logging.error("Token not provided")
56
+ raise ValueError("Token not provided but required")
57
+ if phone_number_id == "":
58
+ logging.error("Phone number ID not provided")
59
+ raise ValueError(
60
+ "Phone number ID not provided but required")
61
+ self.token = token
62
+ self.phone_number_id = phone_number_id
63
+ self.base_url = "https://graph.facebook.com/v18.0"
64
+ self.url = f"{self.base_url}/{phone_number_id}/messages"
65
+ self.verify_token = verify_token
66
+
67
+ async def base(*args):
68
+ pass
69
+ self.message_handler = base
70
+ self.other_handler = base
71
+ self.verification_handler = base
72
+ self.headers = {
73
+ "Content-Type": "application/json",
74
+ "Authorization": f"Bearer {self.token}"
75
+ }
76
+ if logger is False:
77
+ logging.disable(logging.INFO)
78
+ logging.disable(logging.ERROR)
79
+ if debug is False:
80
+ logging.disable(logging.DEBUG)
81
+ logging.disable(logging.ERROR)
82
+
83
+ self.app = FastAPI()
84
+
85
+ # Verification handler has 1 argument: challenge (str | bool): str if verification is successful, False if not
86
+
87
+ @self.app.get("/")
88
+ async def verify_endpoint(r: Request):
89
+ if r.query_params.get("hub.verify_token") == self.verify_token:
90
+ logging.debug("Webhook verified successfully")
91
+ challenge = r.query_params.get("hub.challenge")
92
+ self.verification_handler(challenge)
93
+ self.other_handler(challenge)
94
+ return int(challenge)
95
+ logging.error("Webhook Verification failed - token mismatch")
96
+ await self.verification_handler(False)
97
+ await self.other_handler(False)
98
+ return {"success": False}
99
+
100
+ @self.app.post("/")
101
+ async def hook(r: Request):
102
+ try:
103
+ # Handle Webhook Subscriptions
104
+ data = await r.json()
105
+ if data is None:
106
+ return {"success": False}
107
+ data_str = json.dumps(data, indent=4)
108
+ # log the data received only if the log level is debug
109
+ logging.debug(f"Received webhook data: {data_str}")
110
+
111
+ changed_field = self.changed_field(data)
112
+ if changed_field == "messages":
113
+ new_message = self.is_message(data)
114
+ if new_message:
115
+ msg = Message(instance=self, data=data)
116
+ await self.message_handler(msg)
117
+ await self.other_handler(msg)
118
+ return {"success": True}
119
+ except Exception as e:
120
+ logging.error(f"Error parsing message: {e}")
121
+ raise HTTPException(status_code=500, detail={
122
+ "success": False,
123
+ "error": str(e)
124
+ })
125
+
126
+ # all the files starting with _ are imported here, and should not be imported directly.
127
+
128
+ is_message = staticmethod(is_message)
129
+ get_mobile = staticmethod(get_mobile)
130
+ get_name = staticmethod(get_name)
131
+ get_message = staticmethod(get_message)
132
+ get_message_id = staticmethod(get_message_id)
133
+ get_message_type = staticmethod(get_message_type)
134
+ get_message_timestamp = staticmethod(get_message_timestamp)
135
+ get_audio = staticmethod(get_audio)
136
+ get_delivery = staticmethod(get_delivery)
137
+ get_document = staticmethod(get_document)
138
+ get_image = staticmethod(get_image)
139
+ get_sticker = staticmethod(get_sticker)
140
+ get_interactive_response = staticmethod(get_interactive_response)
141
+ get_location = staticmethod(get_location)
142
+ get_video = staticmethod(get_video)
143
+ changed_field = staticmethod(changed_field)
144
+ get_author = staticmethod(get_author)
145
+
146
+ send_button = send_button
147
+ create_button = create_button
148
+ send_reply_button = send_reply_button
149
+ send_image = send_image
150
+ send_video = send_video
151
+ send_audio = send_audio
152
+ send_location = send_location
153
+ send_sticker = send_sticker
154
+ send_document = send_document
155
+ upload_media = upload_media
156
+ query_media_url = query_media_url
157
+ download_media = download_media
158
+ delete_media = delete_media
159
+ send_template = send_template
160
+ send_custom_json = send_custom_json
161
+ send_contacts = send_contacts
162
+
163
+ authorized = property(authorized)
164
+
165
+ def create_message(self, **kwargs) -> Message:
166
+ """
167
+ Create a message object
168
+
169
+ Args:
170
+ data[dict]: The message data
171
+ content[str]: The message content
172
+ to[str]: The recipient
173
+ rec_type[str]: The recipient type (individual/group)
174
+ """
175
+ return Message(**kwargs, instance=self)
176
+
177
+ def on_message(self, handler: function):
178
+ """
179
+ Set the handler for incoming messages
180
+
181
+ Args:
182
+ handler[function]: The handler function
183
+ """
184
+ self.message_handler = handler
185
+
186
+ def on_event(self, handler: function):
187
+ """
188
+ Set the handler for other events
189
+
190
+ Args:
191
+ handler[function]: The handler function
192
+ """
193
+ self.other_handler = handler
194
+
195
+ def on_verification(self, handler: function):
196
+ """
197
+ Set the handler for verification
198
+
199
+ Args:
200
+ handler[function]: The handler function
201
+ """
202
+ self.verification_handler = handler
203
+
204
+ def run(self, host: str = "localhost", port: int = 5000, **options):
205
+ _run(self.app, host=host, port=port, **options)
206
+
207
+
208
+ class Message(object):
209
+ def __init__(self, id: int = None, data: dict = {}, instance: WhatsApp = None, content: str = "", to: str = "", rec_type: str = "individual"): # type: ignore
210
+ self.instance = instance
211
+ self.url = self.instance.url
212
+ self.headers = self.instance.headers
213
+
214
+ try:
215
+ self.id = instance.get_message_id(data)
216
+ except:
217
+ self.id = id
218
+ try:
219
+ self.type = self.instance.get_message_type(data)
220
+ except:
221
+ self.type = "text"
222
+ self.data = data
223
+ self.rec = rec_type
224
+ self.to = to
225
+ try:
226
+ self.content = content if content != "" else self.instance.get_message(
227
+ data)
228
+ except:
229
+ self.content = content
230
+ try:
231
+ self.sender = self.instance.get_mobile(data)
232
+ except:
233
+ self.sender = None
234
+ try:
235
+ self.name = self.instance.get_name(data)
236
+ except:
237
+ self.name = None
238
+
239
+ if self.type == "image":
240
+ try:
241
+ self.image = self.instance.get_image(data)
242
+ except:
243
+ self.image = None
244
+ if self.type == "sticker":
245
+ try:
246
+ self.sticker = self.instance.get_sticker(data)
247
+ except:
248
+ self.sticker = None
249
+ elif self.type == "video":
250
+ try:
251
+ self.video = self.instance.get_video(data)
252
+ except:
253
+ self.video = None
254
+ elif self.type == "audio":
255
+ try:
256
+ self.audio = self.instance.get_audio(data)
257
+ except:
258
+ pass
259
+ elif self.type == "document":
260
+ try:
261
+ self.document = self.instance.get_document(data)
262
+ except:
263
+ pass
264
+ elif self.type == "location":
265
+ try:
266
+ self.location = self.instance.get_location(data)
267
+ except:
268
+ pass
269
+ elif self.type == "interactive":
270
+ try:
271
+ self.interactive = self.instance.get_interactive_response(data)
272
+ except:
273
+ pass
274
+
275
+ from .ext._message import send, reply, mark_as_read, react
whatsapp/constants.py CHANGED
@@ -1,3 +1,4 @@
1
- # internal use only - numbers and . only
2
-
3
- VERSION = "3.1.1"
1
+ # This file contains the constants used in the project.
2
+ # The VERSION constant is used to store the version of the project - it's not only used in the __init__.py file, but also in the pyproject.toml file.
3
+
4
+ VERSION = "3.2.5"