Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Various components used when sending messages in Facebook Messenger are wrapped
**NOTE :** Please be aware that while this package includes commonly used features of the Messenger Platform, not all features have been implemented. If you would like to contribute and add a feature to this package, you are welcome to submit a pull request. I will review it promptly.

## Prerequisite
- **Python 3.7+** installed
- **Python 3.9+** installed
- You'll need to setup a [Facebook App](https://developers.facebook.com/apps/), Facebook Page, get the Page Access Token and link the App to the Page.
## How to install
### From GitHub
Expand Down
131 changes: 131 additions & 0 deletions messenger_api_python.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
Metadata-Version: 2.4
Name: messenger-api-python
Version: 2026.7.22
Summary: Python wrapper to the various APIs in Facebook Messenger Platform
Home-page: https://github.com/krishna2206/messenger-api-python
Author: krishna2206
Author-email: fitiavana.krishna@gmail.com
License: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: python-magic<1,>=0.4.27
Requires-Dist: requests<3,>=2.31.0
Requires-Dist: requests-toolbelt<2,>=1.0.0
Dynamic: license-file

# messenger-api-python
[![PyPI](https://img.shields.io/pypi/v/messenger-api-python.svg?maxAge=2592000)](https://pypi.python.org/pypi/messenger-api-python)

Python Wrapper to various APIs from [Facebook Messenger Platform](https://developers.facebook.com/docs/messenger-platform).


## Features

### Send API (v19.0)
- Send text messages
- Send attachments from a remote file (image, audio, video, file)
- Send attachments from a local file (image, audio, video, file)
- Send templates (generic messages)
- Send quick replies
- Send buttons
### Profile API (v19.0)
- Set welcome screen
- Set persistent menu
### Attachment Upload API (v19.0)
- Upload attachments from a remote file (image, audio, video, file)
- Upload attachments from a local file (image, audio video, file)
### Reusable components
Various components used when sending messages in Facebook Messenger are wrapped into Python objects to make them reusable and easy to use.
- **Elements:** used to contains various Element objects
- **Element:** a card-like component that holds various other components
- **Buttons:** used to contains various Button objects
- **Button:** button used in various other components, can also be used alone
- **QuickReplies:** used to contains various QuickReply objects
- **QuickReply:** used when sending messages accompanied with quick replies
- **PersistentMenu:** used when setting up persistent menu

**NOTE :** Please be aware that while this package includes commonly used features of the Messenger Platform, not all features have been implemented. If you would like to contribute and add a feature to this package, you are welcome to submit a pull request. I will review it promptly.

## Prerequisite
- **Python 3.9+** installed
- You'll need to setup a [Facebook App](https://developers.facebook.com/apps/), Facebook Page, get the Page Access Token and link the App to the Page.
## How to install
### From GitHub
```bash
pip install git+https://github.com/krishna2206/messenger-api-python.git#egg=messenger-api-python
```
### From Pypi
Package from Pypi.org may not be the latest one, if you want the latest version of this package, install it from the GitHub repository (see above)
```bash
pip install messenger-api-python
```
## Usage
### Send API
```python
from messengerapi import SendApi
send_api = SendApi(<page_access_token>)
send_api.send_text_message(<message>, <recipient_id>)
```
**Note**: From Facebook regarding User IDs

> These ids are page-scoped. These ids differ from those returned from Facebook Login apps which are app-scoped. You must use ids retrieved from a Messenger integration for this page in order to function properly.

> The Facebook Graph API allows messages to be sent only to User IDs that have recently interacted with the Facebook Page. Attempting to send messages to User IDs that fall outside of this interaction window will result in a failed delivery. (More to read on : https://developers.facebook.com/docs/messenger-platform/overview)

##### Sending a generic template message:

> [Generic Template Messages](https://developers.facebook.com/docs/messenger-platform/implementation#receive_message) allows you to add cool elements like images, text all in a single bubble.
```python
from messengerapi import SendApi
from messengerapi.components import Elements, Element, Buttons, Button, POSTBACK

send_api = SendApi(<page_access_token>)

elements = Elements()
buttons = Buttons()

button = Button(button_type=POSTBACK, title="My button")
buttons.add_button(button.get_content())
element = Element(title="My element", subtitle="The element's subtitle, image_url=<image_url>, buttons=buttons)
elements.add_element(element.get_content())

send_api.send_generic_message(elements.get_content() , recipient_id , image_aspect_ratio="horizontal")
```
##### Sending remote (from URL) image/audio/video/file:
```python
from messengerapi import SendApi

send_api = SendApi(<page_access_token>, <page_id>)

# To send an image
send_api.send_image_attachment(<image_url> , <recipient_id>)
# To send an audio
send_api.send_audio_attachment(<audio_url> , <recipient_id>)
# To send a video
send_api.send_video_attachment(<video_url> , <recipient_id>)
# To send a file
send_api.send_file_attachment(<file_url> , <recipient_id>)
```
##### Sending local image/audio/video/file:
```python
from messengerapi import SendApi

send_api = SendApi(<page_access_token>, <page_id>)

# To send an image
send_api.send_local_image(<image_location> , <recipient_id>)
# To send an audio
send_api.send_local_audio(<audio_location> , <recipient_id>)
# To send a video
send_api.send_local_video(<video_location> , <recipient_id>)
# To send a file
send_api.send_local_file(<file_location> , <recipient_id>)
```
16 changes: 16 additions & 0 deletions messenger_api_python.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
LICENSE
README.md
pyproject.toml
setup.cfg
messenger_api_python.egg-info/PKG-INFO
Comment on lines +1 to +5
messenger_api_python.egg-info/SOURCES.txt
messenger_api_python.egg-info/dependency_links.txt
messenger_api_python.egg-info/requires.txt
messenger_api_python.egg-info/top_level.txt
messengerapi/__init__.py
messengerapi/_base_api.py
messengerapi/attachment_upload_api.py
messengerapi/components.py
messengerapi/constants.py
messengerapi/messenger_profile_api.py
messengerapi/send_api.py
1 change: 1 addition & 0 deletions messenger_api_python.egg-info/dependency_links.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

3 changes: 3 additions & 0 deletions messenger_api_python.egg-info/requires.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
python-magic<1,>=0.4.27
requests<3,>=2.31.0
requests-toolbelt<2,>=1.0.0
1 change: 1 addition & 0 deletions messenger_api_python.egg-info/top_level.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
messengerapi
49 changes: 49 additions & 0 deletions messengerapi/_base_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Shared HTTP client behavior for Messenger API wrappers."""

from __future__ import annotations

from typing import Any, Mapping

import requests


class BaseApiClient:
"""Base class with shared request behavior."""

def __init__(
self,
page_access_token: str,
*,
timeout: float = 30.0,
session: requests.Session | None = None,
) -> None:
if not isinstance(page_access_token, str) or not page_access_token.strip():
raise ValueError("page_access_token must be a non-empty string")
if timeout <= 0:
raise ValueError("timeout must be greater than 0")

self._page_access_token = page_access_token
self._timeout = timeout
self._session = session or requests.Session()

def get_access_token(self) -> str:
return self._page_access_token

def _post_json(self, url: str, body: Mapping[str, Any]) -> dict[str, Any]:
response = self._session.post(
url,
params={"access_token": self.get_access_token()},
json=body,
timeout=self._timeout,
)
return response.json()

def _post_multipart(self, url: str, data: Any, content_type: str) -> dict[str, Any]:
response = self._session.post(
url,
params={"access_token": self.get_access_token()},
data=data,
headers={"content-type": content_type},
timeout=self._timeout,
)
return response.json()
65 changes: 31 additions & 34 deletions messengerapi/attachment_upload_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@
import json

import magic
import requests
from requests_toolbelt import MultipartEncoder

from ._base_api import BaseApiClient
from .constants import API_VERSION


class AttachmentUploadApi:
def __init__(self, page_access_token: str, page_id: str) -> None:
class AttachmentUploadApi(BaseApiClient):
def __init__(self, page_access_token: str, page_id: str, *, timeout: float = 30.0) -> None:
super().__init__(page_access_token, timeout=timeout)
if not isinstance(page_id, str) or not page_id.strip():
raise ValueError("page_id must be a non-empty string")
self.__graph_version = API_VERSION
self.__api_url = f"https://graph.facebook.com/v{self.__graph_version}/{page_id}/message_attachments"
self.__page_access_token = page_access_token

def get_api_url(self):
return self.__api_url

def get_access_token(self):
return self.__page_access_token
return super().get_access_token()

def get_graph_version(self):
return self.__graph_version
Expand Down Expand Up @@ -63,32 +65,29 @@ def __upload_local_attachment(self, asset_type: str, file_location: str):
else:
mimetype = magic.Magic(mime=True).from_file(file_location)

print(f"File MIMETYPE : {mimetype}")

request_body = MultipartEncoder(
fields={
"message": json.dumps({
"attachment": {
"type": asset_type,
"payload": {
"is_reusable": "true"
with open(file_location, "rb") as file_data:
request_body = MultipartEncoder(
fields={
"message": json.dumps({
"attachment": {
"type": asset_type,
"payload": {
"is_reusable": "true"
}
}
}
}),
"filedata": (
os.path.basename(file_location),
open(file_location, "rb"),
mimetype
)
}
)
headers = {"content-type": request_body.content_type}

return requests.post(
self.get_api_url(),
params={"access_token": self.get_access_token()},
data=request_body,
headers=headers).json()
}),
"filedata": (
os.path.basename(file_location),
file_data,
mimetype
)
}
)
return self._post_multipart(
self.get_api_url(),
request_body,
request_body.content_type,
)

def __upload_remote_attachement(self, asset_type: str, file_url: str):
request_body = {
Expand All @@ -103,7 +102,5 @@ def __upload_remote_attachement(self, asset_type: str, file_url: str):
}
}

return requests.post(
self.get_api_url(),
params={"access_token": self.get_access_token()},
json=request_body).json()["attachment_id"]
response = self._post_json(self.get_api_url(), request_body)
return response["attachment_id"]
38 changes: 19 additions & 19 deletions messengerapi/messenger_profile_api.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
"""Wrapper for the Profile API"""

import requests

from ._base_api import BaseApiClient
from .constants import API_VERSION


class ProfileApi:
def __init__(self, page_access_token: str):
class ProfileApi(BaseApiClient):
def __init__(self, page_access_token: str, *, timeout: float = 30.0):
super().__init__(page_access_token, timeout=timeout)
self.__graph_version = API_VERSION
self.__api_url = f"https://graph.facebook.com/v{self.__graph_version}/me"
self.__page_access_token = page_access_token
self.__global_level_endpoint = "/messenger_profile"
self.__user_level_endpoint = "/custom_user_settings"

def get_api_url(self):
return self.__api_url

def get_access_token(self):
return self.__page_access_token
return super().get_access_token()

def get_graph_version(self):
return self.__graph_version
Expand All @@ -37,9 +36,10 @@ def set_welcome_screen(self, get_started_button_payload: str, greetings: list =
[{"locale": "default", "text": "Welcome , {{user_full_name}} !"}] if greetings is None
else greetings)

assert isinstance(greetings, list) and isinstance(
greetings[0], dict), "param greetings must be a list of dicts"
assert greetings[0]["locale"] == "default", "first element of param greetings must be the default locale used"
if not isinstance(greetings, list) or not greetings or not isinstance(greetings[0], dict):
raise TypeError("greetings must be a non-empty list of dictionaries")
if greetings[0].get("locale") != "default":
raise ValueError("the first greetings item must use locale='default'")

request_body = {
"get_started":
Expand All @@ -49,10 +49,10 @@ def set_welcome_screen(self, get_started_button_payload: str, greetings: list =
"greeting": greetings
}

return requests.post(
return self._post_json(
self.get_api_url() + self.__global_level_endpoint,
params={"access_token": self.get_access_token()},
json=request_body).json()
request_body,
)

def set_user_persistent_menu(self, user_id: str, persistent_menu: list):
"""Set the persistent menu for any user of the page.
Expand All @@ -62,13 +62,13 @@ def set_user_persistent_menu(self, user_id: str, persistent_menu: list):
persistent_menu (PersistentMenu object) : The content of the PersistentMenu object , obtained via the PersistentMenu().get_content() method.
"""

return requests.post(
return self._post_json(
self.get_api_url() + self.__user_level_endpoint,
params={"access_token": self.get_access_token()},
json={
{
"psid": user_id,
"persistent_menu": persistent_menu
}).json()
},
)

def set_persistent_menu(self, persistent_menu: list):
"""Set the persistent menu for the page.
Expand All @@ -77,7 +77,7 @@ def set_persistent_menu(self, persistent_menu: list):
persistent_menu (PersistentMenu object) : The content of the PersistentMenu object , obtained via the PersistentMenu().get_content() method.
"""

return requests.post(
return self._post_json(
self.get_api_url() + self.__global_level_endpoint,
params={"access_token": self.get_access_token()},
json={"persistent_menu": persistent_menu}).json()
{"persistent_menu": persistent_menu},
)
Loading