Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update API Version #107

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 49 additions & 21 deletions heyoo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from colorama import Fore, Style
from requests_toolbelt.multipart.encoder import MultipartEncoder
from typing import Optional, Dict, Any, List, Union, Tuple, Callable
from requests.adapters import HTTPAdapter, Retry


# Setup logging
Expand All @@ -30,15 +31,23 @@ def __init__(self, token=None, phone_number_id=None):
"""
self.token = token
self.phone_number_id = phone_number_id
self.base_url = "https://graph.facebook.com/v14.0"
self.v15_base_url = "https://graph.facebook.com/v15.0"
self.base_url = "https://graph.facebook.com/v21.0"
self.v15_base_url = "https://graph.facebook.com/v21.0"
self.url = f"{self.base_url}/{phone_number_id}/messages"

self.headers = {
"Content-Type": "application/json",
"Authorization": "Bearer {}".format(self.token),
}

def _get_session_with_retries(self):
session = requests.Session()
retries = Retry(total=5, backoff_factor=1, status_forcelist=[502, 503, 504])
adapter = HTTPAdapter(max_retries=retries)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session

def send_message(
self, message, recipient_id, recipient_type="individual", preview_url=True
):
Expand Down Expand Up @@ -67,7 +76,8 @@ def send_message(
"text": {"preview_url": preview_url, "body": message},
}
logging.info(f"Sending message to {recipient_id}")
r = requests.post(f"{self.url}", headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(f"{self.url}", headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Message sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -103,7 +113,8 @@ def send_reaction(
"reaction": {"message_id": message_id, "emoji": emoji},
}
logging.info(f"Sending reaction to number {recipient_id} message id {message_id}")
r = requests.post(f"{self.url}", headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(f"{self.url}", headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Reaction sent to number {recipient_id} message id {message_id}")
return r.json()
Expand Down Expand Up @@ -134,7 +145,8 @@ def reply_to_message(
}

logging.info(f"Replying to {message_id}")
r = requests.post(f"{self.url}", headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(f"{self.url}", headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Message sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -173,7 +185,8 @@ def send_template(self, template, recipient_id, components, lang: str = "en_US")
},
}
logging.info(f"Sending template to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Template sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -210,7 +223,8 @@ def send_location(self, lat, long, name, address, recipient_id):
},
}
logging.info(f"Sending location to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Location sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -263,7 +277,8 @@ def send_image(
"image": {"id": image, "caption": caption},
}
logging.info(f"Sending image to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Image sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -308,7 +323,8 @@ def send_sticker(self, sticker: str, recipient_id: str, recipient_type="individu
"sticker": {"id": sticker},
}
logging.info(f"Sending sticker to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Sticker sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -347,7 +363,8 @@ def send_audio(self, audio, recipient_id, link=True):
"audio": {"id": audio},
}
logging.info(f"Sending audio to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Audio sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -389,7 +406,8 @@ def send_video(
"video": {"id": video, "caption": caption},
}
logging.info(f"Sending video to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Video sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -422,7 +440,8 @@ def send_custom_json(self, data, recipient_id=None):
data["to"] = recipient_id

logging.info(f"Sending custom json to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Custom json sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -466,7 +485,8 @@ def send_document(
}

logging.info(f"Sending document to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Document sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -513,7 +533,8 @@ def send_contacts(
"contacts": contacts,
}
logging.info(f"Sending contacts to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Contacts sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -550,7 +571,8 @@ def upload_media(self, media: str) -> Union[Dict[Any, Any], None]:
headers["Content-Type"] = form_data.content_type
logging.info(f"Content-Type: {form_data.content_type}")
logging.info(f"Uploading media {media}")
r = requests.post(
session = self._get_session_with_retries()
r = session.post(
f"{self.base_url}/{self.phone_number_id}/media",
headers=headers,
data=form_data,
Expand All @@ -571,7 +593,8 @@ def delete_media(self, media_id: str) -> Union[Dict[Any, Any], None]:
media_id[str]: Id of the media to be deleted
"""
logging.info(f"Deleting media {media_id}")
r = requests.delete(f"{self.base_url}/{media_id}", headers=self.headers)
session = self._get_session_with_retries()
r = session.delete(f"{self.base_url}/{media_id}", headers=self.headers)
if r.status_code == 200:
logging.info(f"Media {media_id} deleted")
return r.json()
Expand Down Expand Up @@ -606,7 +629,8 @@ def mark_as_read(self, message_id: str) -> Dict[Any, Any]:
"message_id": message_id,
}
logging.info(f"Marking message {message_id} as read")
response = requests.post(
session = self._get_session_with_retries()
response = session.post(
f"{self.v15_base_url}/{self.phone_number_id}/messages",
headers=headers,
json=json_data,
Expand Down Expand Up @@ -654,7 +678,8 @@ def send_button(self, button: Dict[Any, Any], recipient_id: str) -> Dict[Any, An
"interactive": self.create_button(button),
}
logging.info(f"Sending buttons to {recipient_id}")
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Buttons sent to {recipient_id}")
return r.json()
Expand Down Expand Up @@ -683,7 +708,8 @@ def send_reply_button(
"type": "interactive",
"interactive": button,
}
r = requests.post(self.url, headers=self.headers, json=data)
session = self._get_session_with_retries()
r = session.post(self.url, headers=self.headers, json=data)
if r.status_code == 200:
logging.info(f"Reply buttons sent to {recipient_id}")
return r.json()
Expand All @@ -709,7 +735,8 @@ def query_media_url(self, media_id: str) -> Union[str, None]:
"""

logging.info(f"Querying media url for {media_id}")
r = requests.get(f"{self.base_url}/{media_id}", headers=self.headers)
session = self._get_session_with_retries()
r = session.get(f"{self.base_url}/{media_id}", headers=self.headers)
if r.status_code == 200:
logging.info(f"Media url queried for {media_id}")
return r.json()["url"]
Expand Down Expand Up @@ -739,7 +766,8 @@ def download_media(
>>> whatsapp.download_media("media_url", "image/jpeg")
>>> whatsapp.download_media("media_url", "video/mp4", "path/to/file") #do not include the file extension
"""
r = requests.get(media_url, headers=self.headers)
session = self._get_session_with_retries()
r = session.get(media_url, headers=self.headers)
content = r.content
extension = mime_type.split("/")[1].split(";")[0].strip()
# create a temporary file
Expand Down
16 changes: 16 additions & 0 deletions tests/test_sending_audio.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from os import getenv
from heyoo import WhatsApp
from dotenv import load_dotenv
import requests
from requests.adapters import HTTPAdapter, Retry
from unittest.mock import patch

def test_sending_audio():
load_dotenv()
Expand All @@ -14,3 +17,16 @@ def test_sending_audio():
assert(response["contacts"][0]["input"]==getenv("RECIPIENT_ID"))
assert(response["contacts"][0]["wa_id"]==getenv("RECIPIENT_ID"))
assert(response["messaging_product"]=="whatsapp")

@patch.object(requests.Session, 'send')
def test_send_audio_retries(mock_send):
mock_send.side_effect = [requests.exceptions.RequestException] * 2 + [requests.Response()]
load_dotenv()
messenger = WhatsApp(token=getenv("TOKEN"), phone_number_id=getenv("PHONE_NUMBER_ID"))

response = messenger.send_audio(
audio="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
recipient_id=getenv("RECIPIENT_ID"),
)

assert mock_send.call_count == 3
36 changes: 36 additions & 0 deletions tests/test_sending_button.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from os import getenv
from heyoo import WhatsApp
from dotenv import load_dotenv
import requests
from requests.adapters import HTTPAdapter, Retry
from unittest.mock import patch

def test_sending_button():
load_dotenv()
Expand Down Expand Up @@ -34,3 +37,36 @@ def test_sending_button():
assert(response["contacts"][0]["input"]==getenv("RECIPIENT_ID"))
assert(response["contacts"][0]["wa_id"]==getenv("RECIPIENT_ID"))
assert(response["messaging_product"]=="whatsapp")

@patch.object(requests.Session, 'send')
def test_send_button_retries(mock_send):
mock_send.side_effect = [requests.exceptions.RequestException] * 2 + [requests.Response()]
load_dotenv()
messenger = WhatsApp(token=getenv("TOKEN"), phone_number_id=getenv("PHONE_NUMBER_ID"))

response = messenger.send_button(
recipient_id=getenv("RECIPIENT_ID"),
button={
"header": "Header Testing",
"body": "Body Testing",
"footer": "Footer Testing",
"action": {
"button": "Button Testing",
"sections": [
{
"title": "iBank",
"rows": [
{"id": "row 1", "title": "Send Money", "description": ""},
{
"id": "row 2",
"title": "Withdraw money",
"description": "",
},
],
}
],
},
},
)

assert mock_send.call_count == 3
16 changes: 16 additions & 0 deletions tests/test_sending_document.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from os import getenv
from heyoo import WhatsApp
from dotenv import load_dotenv
import requests
from requests.adapters import HTTPAdapter, Retry
from unittest.mock import patch

def test_sending_document():
load_dotenv()
Expand All @@ -15,3 +18,16 @@ def test_sending_document():
assert(response["contacts"][0]["input"]==getenv("RECIPIENT_ID"))
assert(response["contacts"][0]["wa_id"]==getenv("RECIPIENT_ID"))
assert(response["messaging_product"]=="whatsapp")

@patch.object(requests.Session, 'send')
def test_send_document_retries(mock_send):
mock_send.side_effect = [requests.exceptions.RequestException] * 2 + [requests.Response()]
load_dotenv()
messenger = WhatsApp(token=getenv("TOKEN"), phone_number_id=getenv("PHONE_NUMBER_ID"))

response = messenger.send_document(
document="http://www.africau.edu/images/default/sample.pdf",
recipient_id=getenv("RECIPIENT_ID"),
)

assert mock_send.call_count == 3
16 changes: 16 additions & 0 deletions tests/test_sending_image.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from os import getenv
from heyoo import WhatsApp
from dotenv import load_dotenv
import requests
from requests.adapters import HTTPAdapter, Retry
from unittest.mock import patch

def test_sending_image():
load_dotenv()
Expand All @@ -14,3 +17,16 @@ def test_sending_image():
assert(response["contacts"][0]["input"]==getenv("RECIPIENT_ID"))
assert(response["contacts"][0]["wa_id"]==getenv("RECIPIENT_ID"))
assert(response["messaging_product"]=="whatsapp")

@patch.object(requests.Session, 'send')
def test_send_image_retries(mock_send):
mock_send.side_effect = [requests.exceptions.RequestException] * 2 + [requests.Response()]
load_dotenv()
messenger = WhatsApp(token=getenv("TOKEN"), phone_number_id=getenv("PHONE_NUMBER_ID"))

response = messenger.send_image(
image="https://i.imgur.com/Fh7XVYY.jpeg",
recipient_id=getenv("RECIPIENT_ID"),
)

assert mock_send.call_count == 3
19 changes: 19 additions & 0 deletions tests/test_sending_location.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from os import getenv
from heyoo import WhatsApp
from dotenv import load_dotenv
import requests
from requests.adapters import HTTPAdapter, Retry
from unittest.mock import patch

def test_sending_location():
load_dotenv()
Expand All @@ -17,3 +20,19 @@ def test_sending_location():
assert(response["contacts"][0]["input"]==getenv("RECIPIENT_ID"))
assert(response["contacts"][0]["wa_id"]==getenv("RECIPIENT_ID"))
assert(response["messaging_product"]=="whatsapp")

@patch.object(requests.Session, 'send')
def test_send_location_retries(mock_send):
mock_send.side_effect = [requests.exceptions.RequestException] * 2 + [requests.Response()]
load_dotenv()
messenger = WhatsApp(token=getenv("TOKEN"), phone_number_id=getenv("PHONE_NUMBER_ID"))

response = messenger.send_location(
lat=1.29,
long=103.85,
name="Singapore",
address="Singapore",
recipient_id=getenv("RECIPIENT_ID"),
)

assert mock_send.call_count == 3
Loading