whatsapp-python 4.0.2__py3-none-any.whl → 4.3.0__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,9 +1,13 @@
1
1
  """
2
2
  Unofficial Python wrapper for the WhatsApp Cloud API.
3
3
  """
4
+
4
5
  from __future__ import annotations
5
6
  import requests
6
7
  import logging
8
+ import json
9
+ import asyncio
10
+ import aiohttp
7
11
  from bs4 import BeautifulSoup
8
12
  from fastapi import FastAPI, HTTPException, Request
9
13
  from uvicorn import run as _run
@@ -11,15 +15,95 @@ from .constants import VERSION
11
15
  from .ext._property import authorized
12
16
  from .ext._send_others import send_custom_json, send_contacts
13
17
  from .ext._message import send_template
14
- from .ext._send_media import send_image, send_video, send_audio, send_location, send_sticker, send_document
18
+ from .ext._send_media import (
19
+ send_image,
20
+ send_video,
21
+ send_audio,
22
+ send_location,
23
+ send_sticker,
24
+ send_document,
25
+ )
15
26
  from .ext._media import upload_media, query_media_url, download_media, delete_media
16
27
  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: dict = "", logger: bool = True, update_check: bool = True, verify_token: str = "", debug: bool = True, version: str = "latest"):
28
+ from .ext._static import (
29
+ is_message,
30
+ get_mobile,
31
+ get_author,
32
+ get_name,
33
+ get_message,
34
+ get_message_id,
35
+ get_message_type,
36
+ get_message_timestamp,
37
+ get_audio,
38
+ get_delivery,
39
+ get_document,
40
+ get_image,
41
+ get_sticker,
42
+ get_interactive_response,
43
+ get_location,
44
+ get_video,
45
+ changed_field,
46
+ )
47
+
48
+ from .async_ext._property import authorized as async_authorized
49
+ from .async_ext._send_others import (
50
+ send_custom_json as async_send_custom_json,
51
+ send_contacts as async_send_contacts,
52
+ )
53
+ from .async_ext._message import send_template as async_send_template
54
+ from .async_ext._send_media import (
55
+ send_image as async_send_image,
56
+ send_video as async_send_video,
57
+ send_audio as async_send_audio,
58
+ send_location as async_send_location,
59
+ send_sticker as async_send_sticker,
60
+ send_document as async_send_document,
61
+ )
62
+ from .async_ext._media import (
63
+ upload_media as async_upload_media,
64
+ query_media_url as async_query_media_url,
65
+ download_media as async_download_media,
66
+ delete_media as async_delete_media,
67
+ )
68
+ from .async_ext._buttons import (
69
+ send_button as async_send_button,
70
+ create_button as async_create_button,
71
+ send_reply_button as async_send_reply_button,
72
+ )
73
+ from .async_ext._static import (
74
+ is_message as async_is_message,
75
+ get_mobile as async_get_mobile,
76
+ get_author as async_get_author,
77
+ get_name as async_get_name,
78
+ get_message as async_get_message,
79
+ get_message_id as async_get_message_id,
80
+ get_message_type as async_get_message_type,
81
+ get_message_timestamp as async_get_message_timestamp,
82
+ get_audio as async_get_audio,
83
+ get_delivery as async_get_delivery,
84
+ get_document as async_get_document,
85
+ get_image as async_get_image,
86
+ get_sticker as async_get_sticker,
87
+ get_interactive_response as async_get_interactive_response,
88
+ get_location as async_get_location,
89
+ get_video as async_get_video,
90
+ changed_field as async_changed_field,
91
+ )
92
+
93
+ from .errors import Handle
94
+
95
+
96
+ class WhatsApp:
97
+ def __init__(
98
+ self,
99
+ token: str = "",
100
+ phone_number_id: dict = "",
101
+ logger: bool = True,
102
+ update_check: bool = True,
103
+ verify_token: str = "",
104
+ debug: bool = True,
105
+ version: str = "latest",
106
+ ):
23
107
  """
24
108
  Initialize the WhatsApp Object
25
109
 
@@ -40,42 +124,49 @@ class WhatsApp(object):
40
124
 
41
125
  elif phone_number_id == "":
42
126
  logging.error("Phone number ID not provided")
43
- raise ValueError(
44
- "Phone number ID not provided but required")
127
+ raise ValueError("Phone number ID not provided but required")
45
128
  elif isinstance(phone_number_id, str):
46
129
  logging.critical(
47
- "The phone number ID should be a dictionary of phone numbers and their IDs. Using strings is deprecated.")
130
+ "The phone number ID should be a dictionary of phone numbers and their IDs. Using strings is deprecated."
131
+ )
48
132
  raise ValueError(
49
- "Phone number ID should be a dictionary of phone numbers and their IDs")
133
+ "Phone number ID should be a dictionary of phone numbers and their IDs"
134
+ )
50
135
  else:
51
136
  pass
52
137
 
53
138
  self.VERSION = VERSION # package version
54
- r = requests.get("https://developers.facebook.com/docs/graph-api/changelog/").text
139
+ r = requests.get(
140
+ "https://developers.facebook.com/docs/graph-api/changelog/"
141
+ ).text
55
142
 
56
143
  # dynamically get the latest version of the API
57
144
  if version == "latest":
58
145
  soup = BeautifulSoup(r, features="html.parser")
59
146
  t1 = soup.findAll("table")
60
147
 
61
- def makeversion(table):
148
+ def makeversion(table: BeautifulSoup) -> str:
62
149
  result = []
63
- allrows = table.findAll('tr')
150
+ allrows = table.findAll("tr")
64
151
  for row in allrows:
65
152
  result.append([])
66
- allcols = row.findAll('td')
153
+ allcols = row.findAll("td")
67
154
  for col in allcols:
68
155
  thestrings = [(s) for s in col.findAll(text=True)]
69
- thetext = ''.join(thestrings)
156
+ thetext = "".join(thestrings)
70
157
  result[-1].append(thetext)
71
158
  return result[0][1]
159
+
72
160
  self.LATEST = makeversion(t1[0]) # latest version of the API
73
161
  else:
74
162
  self.LATEST = version
75
163
 
76
164
  if update_check is True:
77
- latest = str(requests.get(
78
- "https://pypi.org/pypi/whatsapp-python/json").json()["info"]["version"])
165
+ latest = str(
166
+ requests.get("https://pypi.org/pypi/whatsapp-python/json").json()[
167
+ "info"
168
+ ]["version"]
169
+ )
79
170
  if self.VERSION != latest:
80
171
  try:
81
172
  version_int = int(self.VERSION.replace(".", ""))
@@ -89,18 +180,19 @@ class WhatsApp(object):
89
180
  if version_int < latest_int:
90
181
  if version_int == 0:
91
182
  logging.critical(
92
- f"There was an error while checking for updates, please check for updates manually. This may be due to the version being a post-release version (e.g. 1.0.0.post1) or a pre-release version (e.g. 1.0.0a1). READ THE CHANGELOG BEFORE UPDATING. NEW VERSIONS MAY BREAK YOUR CODE IF NOT PROPERLY UPDATED.")
183
+ f"There was an error while checking for updates, please check for updates manually. This may be due to the version being a post-release version (e.g. 1.0.0.post1) or a pre-release version (e.g. 1.0.0a1). READ THE CHANGELOG BEFORE UPDATING. NEW VERSIONS MAY BREAK YOUR CODE IF NOT PROPERLY UPDATED."
184
+ )
93
185
  else:
94
186
  logging.critical(
95
- 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.")
187
+ 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."
188
+ )
96
189
 
97
190
  if token == "":
98
191
  logging.error("Token not provided")
99
192
  raise ValueError("Token not provided but required")
100
193
  if phone_number_id == "":
101
194
  logging.error("Phone number ID not provided")
102
- raise ValueError(
103
- "Phone number ID not provided but required")
195
+ raise ValueError("Phone number ID not provided but required")
104
196
  self.token = token
105
197
  self.phone_number_id = phone_number_id
106
198
  self.base_url = f"https://graph.facebook.com/{self.LATEST}"
@@ -109,12 +201,13 @@ class WhatsApp(object):
109
201
 
110
202
  async def base(*args):
111
203
  pass
204
+
112
205
  self.message_handler = base
113
206
  self.other_handler = base
114
207
  self.verification_handler = base
115
208
  self.headers = {
116
209
  "Content-Type": "application/json",
117
- "Authorization": f"Bearer {self.token}"
210
+ "Authorization": f"Bearer {self.token}",
118
211
  }
119
212
  if logger is False:
120
213
  logging.disable(logging.INFO)
@@ -132,8 +225,8 @@ class WhatsApp(object):
132
225
  if r.query_params.get("hub.verify_token") == self.verify_token:
133
226
  logging.debug("Webhook verified successfully")
134
227
  challenge = r.query_params.get("hub.challenge")
135
- self.verification_handler(challenge)
136
- self.other_handler(challenge)
228
+ await self.verification_handler(challenge)
229
+ await self.other_handler(challenge)
137
230
  return int(challenge)
138
231
  logging.error("Webhook Verification failed - token mismatch")
139
232
  await self.verification_handler(False)
@@ -161,10 +254,9 @@ class WhatsApp(object):
161
254
  return {"success": True}
162
255
  except Exception as e:
163
256
  logging.error(f"Error parsing message: {e}")
164
- raise HTTPException(status_code=500, detail={
165
- "success": False,
166
- "error": str(e)
167
- })
257
+ raise HTTPException(
258
+ status_code=500, detail={"success": False, "error": str(e)}
259
+ )
168
260
 
169
261
  # all the files starting with _ are imported here, and should not be imported directly.
170
262
 
@@ -202,7 +294,6 @@ class WhatsApp(object):
202
294
  send_template = send_template
203
295
  send_custom_json = send_custom_json
204
296
  send_contacts = send_contacts
205
-
206
297
  authorized = property(authorized)
207
298
 
208
299
  def create_message(self, **kwargs) -> Message:
@@ -248,9 +339,265 @@ class WhatsApp(object):
248
339
  _run(self.app, host=host, port=port, **options)
249
340
 
250
341
 
251
- class Message(object):
342
+ class AsyncWhatsApp(WhatsApp):
343
+ def __init__(
344
+ self,
345
+ token: str = "",
346
+ phone_number_id: dict = "",
347
+ logger: bool = True,
348
+ update_check: bool = True,
349
+ verify_token: str = "",
350
+ debug: bool = True,
351
+ version: str = "latest",
352
+ ):
353
+ """
354
+ Initialize the WhatsApp Object
355
+
356
+ Args:
357
+ token[str]: Token for the WhatsApp cloud API obtained from the Facebook developer portal
358
+ phone_number_id[str]: Phone number id for the WhatsApp cloud API obtained from the developer portal
359
+ logger[bool]: Whether to enable logging or not (default: True)
360
+ """
361
+
362
+ # Check if the version is up to date
363
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
364
+ self.l = phone_number_id
365
+
366
+ if isinstance(phone_number_id, dict):
367
+ # use first phone number id as default
368
+ phone_number_id = phone_number_id[list(phone_number_id.keys())[0]]
369
+
370
+ elif phone_number_id == "":
371
+ logging.error("Phone number ID not provided")
372
+ raise ValueError("Phone number ID not provided but required")
373
+ elif isinstance(phone_number_id, str):
374
+ logging.critical(
375
+ "The phone number ID should be a dictionary of phone numbers and their IDs. Using strings is deprecated."
376
+ )
377
+ raise ValueError(
378
+ "Phone number ID should be a dictionary of phone numbers and their IDs"
379
+ )
380
+ else:
381
+ pass
382
+
383
+ self.VERSION = VERSION # package version
384
+ r = requests.get(
385
+ "https://developers.facebook.com/docs/graph-api/changelog/"
386
+ ).text
387
+
388
+ # dynamically get the latest version of the API
389
+ if version == "latest":
390
+ soup = BeautifulSoup(r, features="html.parser")
391
+ t1 = soup.findAll("table")
392
+
393
+ def makeversion(table: BeautifulSoup) -> str:
394
+ result = []
395
+ allrows = table.findAll("tr")
396
+ for row in allrows:
397
+ result.append([])
398
+ allcols = row.findAll("td")
399
+ for col in allcols:
400
+ thestrings = [(s) for s in col.findAll(text=True)]
401
+ thetext = "".join(thestrings)
402
+ result[-1].append(thetext)
403
+ return result[0][1]
404
+
405
+ self.LATEST = makeversion(t1[0]) # latest version of the API
406
+ else:
407
+ self.LATEST = version
408
+
409
+ if update_check is True:
410
+ latest = str(
411
+ requests.get("https://pypi.org/pypi/whatsapp-python/json").json()[
412
+ "info"
413
+ ]["version"]
414
+ )
415
+ if self.VERSION != latest:
416
+ try:
417
+ version_int = int(self.VERSION.replace(".", ""))
418
+ except:
419
+ version_int = 0
420
+ try:
421
+ latest_int = int(latest.replace(".", ""))
422
+ except:
423
+ latest_int = 0
424
+ # 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)
425
+ if version_int < latest_int:
426
+ if version_int == 0:
427
+ logging.critical(
428
+ f"There was an error while checking for updates, please check for updates manually. This may be due to the version being a post-release version (e.g. 1.0.0.post1) or a pre-release version (e.g. 1.0.0a1). READ THE CHANGELOG BEFORE UPDATING. NEW VERSIONS MAY BREAK YOUR CODE IF NOT PROPERLY UPDATED."
429
+ )
430
+ else:
431
+ logging.critical(
432
+ 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."
433
+ )
434
+
435
+ if token == "":
436
+ logging.error("Token not provided")
437
+ raise ValueError("Token not provided but required")
438
+ if phone_number_id == "":
439
+ logging.error("Phone number ID not provided")
440
+ raise ValueError("Phone number ID not provided but required")
441
+ self.token = token
442
+ self.phone_number_id = phone_number_id
443
+ self.base_url = f"https://graph.facebook.com/{self.LATEST}"
444
+ self.url = f"{self.base_url}/{phone_number_id}/messages"
445
+ self.verify_token = verify_token
446
+
447
+ async def base(*args):
448
+ pass
449
+
450
+ self.message_handler = base
451
+ self.other_handler = base
452
+ self.verification_handler = base
453
+ self.headers = {
454
+ "Content-Type": "application/json",
455
+ "Authorization": f"Bearer {self.token}",
456
+ }
457
+ if logger is False:
458
+ logging.disable(logging.INFO)
459
+ logging.disable(logging.ERROR)
460
+ if debug is False:
461
+ logging.disable(logging.DEBUG)
462
+ logging.disable(logging.ERROR)
463
+
464
+ self.app = FastAPI()
465
+
466
+ # Verification handler has 1 argument: challenge (str | bool): str if verification is successful, False if not
467
+
468
+ @self.app.get("/")
469
+ async def verify_endpoint(r: Request):
470
+ if r.query_params.get("hub.verify_token") == self.verify_token:
471
+ logging.debug("Webhook verified successfully")
472
+ challenge = r.query_params.get("hub.challenge")
473
+ await self.verification_handler(challenge)
474
+ await self.other_handler(challenge)
475
+ return int(challenge)
476
+ logging.error("Webhook Verification failed - token mismatch")
477
+ await self.verification_handler(False)
478
+ await self.other_handler(False)
479
+ return {"success": False}
480
+
481
+ @self.app.post("/")
482
+ async def hook(r: Request):
483
+ try:
484
+ # Handle Webhook Subscriptions
485
+ data = await r.json()
486
+ if data is None:
487
+ return {"success": False}
488
+ data_str = json.dumps(data, indent=4)
489
+ # log the data received only if the log level is debug
490
+ logging.debug(f"Received webhook data: {data_str}")
491
+
492
+ changed_field = self.changed_field(data)
493
+ if changed_field == "messages":
494
+ new_message = self.is_message(data)
495
+ if new_message:
496
+ msg = Message(instance=self, data=data)
497
+ await self.message_handler(msg)
498
+ await self.other_handler(msg)
499
+ return {"success": True}
500
+ except Exception as e:
501
+ logging.error(f"Error parsing message: {e}")
502
+ raise HTTPException(
503
+ status_code=500, detail={"success": False, "error": str(e)}
504
+ )
505
+
506
+ # all the files starting with _ are imported here, and should not be imported directly.
507
+
508
+ is_message = staticmethod(is_message)
509
+ get_mobile = staticmethod(get_mobile)
510
+ get_name = staticmethod(get_name)
511
+ get_message = staticmethod(get_message)
512
+ get_message_id = staticmethod(get_message_id)
513
+ get_message_type = staticmethod(get_message_type)
514
+ get_message_timestamp = staticmethod(get_message_timestamp)
515
+ get_audio = staticmethod(get_audio)
516
+ get_delivery = staticmethod(get_delivery)
517
+ get_document = staticmethod(get_document)
518
+ get_image = staticmethod(get_image)
519
+ get_sticker = staticmethod(get_sticker)
520
+ get_interactive_response = staticmethod(get_interactive_response)
521
+ get_location = staticmethod(get_location)
522
+ get_video = staticmethod(get_video)
523
+ changed_field = staticmethod(changed_field)
524
+ get_author = staticmethod(get_author)
525
+
526
+ send_button = async_send_button
527
+ create_button = async_create_button
528
+ send_reply_button = async_send_reply_button
529
+ send_image = async_send_image
530
+ send_video = async_send_video
531
+ send_audio = async_send_audio
532
+ send_location = async_send_location
533
+ send_sticker = async_send_sticker
534
+ send_document = async_send_document
535
+ upload_media = async_upload_media
536
+ query_media_url = async_query_media_url
537
+ download_media = async_download_media
538
+ delete_media = async_delete_media
539
+ send_template = async_send_template
540
+ send_custom_json = async_send_custom_json
541
+ send_contacts = async_send_contacts
542
+ authorized = property(async_authorized)
543
+
544
+ def handle(self, data: dict):
545
+ return Handle(data)
546
+
547
+ def create_message(self, **kwargs) -> AsyncMessage:
548
+ """
549
+ Create a message object
550
+
551
+ Args:
552
+ data[dict]: The message data
553
+ content[str]: The message content
554
+ to[str]: The recipient
555
+ rec_type[str]: The recipient type (individual/group)
556
+ """
557
+ return AsyncMessage(**kwargs, instance=self)
558
+
559
+ def on_message(self, handler: function):
560
+ """
561
+ Set the handler for incoming messages
562
+
563
+ Args:
564
+ handler[function]: The handler function
565
+ """
566
+ self.message_handler = handler
567
+
568
+ def on_event(self, handler: function):
569
+ """
570
+ Set the handler for other events
571
+
572
+ Args:
573
+ handler[function]: The handler function
574
+ """
575
+ self.other_handler = handler
576
+
577
+ def on_verification(self, handler: function):
578
+ """
579
+ Set the handler for verification
580
+
581
+ Args:
582
+ handler[function]: The handler function
583
+ """
584
+ self.verification_handler = handler
585
+
586
+ def run(self, host: str = "localhost", port: int = 5000, **options):
587
+ _run(self.app, host=host, port=port, **options)
588
+
589
+
590
+ class Message:
252
591
  # type: ignore
253
- def __init__(self, id: int = None, data: dict = {}, instance: WhatsApp = None, content: str = "", to: str = "", rec_type: str = "individual"):
592
+ def __init__(
593
+ self,
594
+ id: int = None,
595
+ data: dict = {},
596
+ instance: WhatsApp = None,
597
+ content: str = "",
598
+ to: str = "",
599
+ rec_type: str = "individual",
600
+ ):
254
601
  self.instance = instance
255
602
  self.url = self.instance.url
256
603
  self.headers = self.instance.headers
@@ -267,8 +614,7 @@ class Message(object):
267
614
  self.rec = rec_type
268
615
  self.to = to
269
616
  try:
270
- self.content = content if content != "" else self.instance.get_message(
271
- data)
617
+ self.content = content if content != "" else self.instance.get_message(data)
272
618
  except:
273
619
  self.content = content
274
620
  try:
@@ -343,7 +689,8 @@ class Message(object):
343
689
  }
344
690
 
345
691
  response = requests.post(
346
- f"{self.instance.url}", headers=self.instance.headers, json=payload)
692
+ f"{self.instance.url}", headers=self.instance.headers, json=payload
693
+ )
347
694
  if response.status_code == 200:
348
695
  logging.info(response.json())
349
696
  return response.json()
@@ -397,3 +744,201 @@ class Message(object):
397
744
  logging.info(f"Status code: {r.status_code}")
398
745
  logging.debug(f"Response: {r.json()}")
399
746
  return r.json()
747
+
748
+
749
+ class AsyncMessage:
750
+ # type: ignore
751
+ def __init__(
752
+ self,
753
+ id: int = None,
754
+ data: dict = {},
755
+ instance: WhatsApp = None,
756
+ content: str = "",
757
+ to: str = "",
758
+ rec_type: str = "individual",
759
+ ):
760
+ self.instance = instance
761
+ self.url = self.instance.url
762
+ self.headers = self.instance.headers
763
+
764
+ try:
765
+ self.id = instance.get_message_id(data)
766
+ except:
767
+ self.id = id
768
+ try:
769
+ self.type = self.instance.get_message_type(data)
770
+ except:
771
+ self.type = "text"
772
+ self.data = data
773
+ self.rec = rec_type
774
+ self.to = to
775
+ try:
776
+ self.content = content if content != "" else self.instance.get_message(data)
777
+ except:
778
+ self.content = content
779
+ try:
780
+ self.name = self.instance.get_name(data)
781
+ except:
782
+ self.name = None
783
+
784
+ if self.type == "image":
785
+ try:
786
+ self.image = self.instance.get_image(data)
787
+ except:
788
+ self.image = None
789
+ if self.type == "sticker":
790
+ try:
791
+ self.sticker = self.instance.get_sticker(data)
792
+ except:
793
+ self.sticker = None
794
+ elif self.type == "video":
795
+ try:
796
+ self.video = self.instance.get_video(data)
797
+ except:
798
+ self.video = None
799
+ elif self.type == "audio":
800
+ try:
801
+ self.audio = self.instance.get_audio(data)
802
+ except:
803
+ pass
804
+ elif self.type == "document":
805
+ try:
806
+ self.document = self.instance.get_document(data)
807
+ except:
808
+ pass
809
+ elif self.type == "location":
810
+ try:
811
+ self.location = self.instance.get_location(data)
812
+ except:
813
+ pass
814
+ elif self.type == "interactive":
815
+ try:
816
+ self.interactive = self.instance.get_interactive_response(data)
817
+ except:
818
+ pass
819
+
820
+ async def reply(
821
+ self, reply_text: str = "", preview_url: bool = True
822
+ ) -> asyncio.Future:
823
+ if self.data == {}:
824
+ return {"error": "No data provided"}
825
+ author = self.instance.get_author(self.data)
826
+ payload = {
827
+ "messaging_product": "whatsapp",
828
+ "recipient_type": "individual",
829
+ "to": str(author),
830
+ "type": "text",
831
+ "context": {"message_id": self.id},
832
+ "text": {"preview_url": preview_url, "body": reply_text},
833
+ }
834
+ logging.info(f"Replying to {self.id}")
835
+
836
+ async def call():
837
+ async with aiohttp.ClientSession() as session:
838
+ async with session.post(
839
+ self.url, headers=self.headers, json=payload
840
+ ) as response:
841
+ if response.status == 200:
842
+ logging.info(
843
+ f"Message sent to {self.instance.get_author(self.data)}"
844
+ )
845
+ return await response.json()
846
+ logging.info(
847
+ f"Message not sent to {self.instance.get_author(self.data)}"
848
+ )
849
+ logging.info(f"Status code: {response.status}")
850
+ logging.error(f"Response: {await response.json()}")
851
+ return await response.json()
852
+
853
+ f = asyncio.ensure_future(call())
854
+ await asyncio.sleep(0.001) # make asyncio run the task
855
+ return f
856
+
857
+ async def mark_as_read(self) -> asyncio.Future:
858
+
859
+ payload = {
860
+ "messaging_product": "whatsapp",
861
+ "status": "read",
862
+ "message_id": self.id,
863
+ }
864
+
865
+ async def call():
866
+ async with aiohttp.ClientSession() as session:
867
+ async with session.post(
868
+ f"{self.instance.url}", headers=self.instance.headers, json=payload
869
+ ) as response:
870
+ if response.status == 200:
871
+ logging.info(await response.json())
872
+ return await response.json()
873
+ else:
874
+ logging.error(await response.json())
875
+ return await response.json()
876
+
877
+ f = asyncio.ensure_future(call())
878
+ await asyncio.sleep(0.001) # make asyncio run the task
879
+ return f
880
+
881
+ async def send(self, sender=None, preview_url: bool = True) -> asyncio.Future:
882
+ try:
883
+ sender = dict(self.instance.l)[sender]
884
+
885
+ except:
886
+ sender = self.instance.phone_number_id
887
+
888
+ if sender == None:
889
+ sender = self.instance.phone_number_id
890
+
891
+ url = f"https://graph.facebook.com/{self.instance.LATEST}/{sender}/messages"
892
+ data = {
893
+ "messaging_product": "whatsapp",
894
+ "recipient_type": self.rec,
895
+ "to": self.to,
896
+ "type": "text",
897
+ "text": {"preview_url": preview_url, "body": self.content},
898
+ }
899
+ logging.info(f"Sending message to {self.to}")
900
+
901
+ async def call():
902
+ print("sending")
903
+ async with aiohttp.ClientSession() as session:
904
+ async with session.post(
905
+ url, headers=self.headers, json=data
906
+ ) as response:
907
+ if response.status == 200:
908
+ logging.info(f"Message sent to {self.to}")
909
+ return await response.json()
910
+ logging.info(f"Message not sent to {self.to}")
911
+ logging.info(f"Status code: {response.status}")
912
+ logging.error(f"Response: {await response.json()}")
913
+ return await response.json()
914
+
915
+ f = asyncio.ensure_future(call())
916
+ await asyncio.sleep(0.001) # make asyncio run the task
917
+ return f
918
+
919
+ async def react(self, emoji: str) -> asyncio.Future:
920
+ data = {
921
+ "messaging_product": "whatsapp",
922
+ "recipient_type": "individual",
923
+ "to": self.to,
924
+ "type": "reaction",
925
+ "reaction": {"message_id": self.id, "emoji": emoji},
926
+ }
927
+ logging.info(f"Reacting to {self.id}")
928
+
929
+ async def call():
930
+ async with aiohttp.ClientSession() as session:
931
+ async with session.post(
932
+ self.url, headers=self.headers, json=data
933
+ ) as response:
934
+ if response.status == 200:
935
+ logging.info(f"Reaction sent to {self.to}")
936
+ return await response.json()
937
+ logging.info(f"Reaction not sent to {self.to}")
938
+ logging.info(f"Status code: {response.status}")
939
+ logging.debug(f"Response: {await response.json()}")
940
+ return await response.json()
941
+
942
+ f = asyncio.ensure_future(call())
943
+ await asyncio.sleep(0.001)
944
+ return f