Skip to content

Commit 38056bb

Browse files
committed
Initial commit of Wasender Python SDK
0 parents  commit 38056bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+6053
-0
lines changed

.coverage

52 KB
Binary file not shown.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 YonkoSam
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 936 additions & 0 deletions
Large diffs are not rendered by default.

docs/channel.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Wasender Python SDK: Sending Messages to WhatsApp Channels
2+
3+
This document explains how to send messages to WhatsApp Channels using the Wasender Python SDK.
4+
5+
## SDK Version: [Specify Python SDK Version Here, e.g., 0.1.0]
6+
7+
## Overview
8+
9+
Sending a message to a WhatsApp Channel utilizes the existing generic `client.send()` method from the Wasender Python SDK. The key differences compared to sending a message to a regular user or group are:
10+
11+
1. **Recipient (`to` field):** The `to` field in the message payload must be the unique **Channel ID** (also known as Channel JID). This typically looks like `12345678901234567890@newsletter`.
12+
2. **Message Type Restriction:** Currently, the Wasender API (and thus the SDK) generally **only supports sending text messages** to channels. Attempting to send other message types (images, videos, documents, etc.) to channels via the standard `send()` method may not be supported or could result in an API error. Always refer to the latest official Wasender API documentation for channel messaging capabilities.
13+
14+
## Prerequisites
15+
16+
1. **Obtain a Channel ID:** You need the specific ID of the channel you want to send a message to. One way to obtain a Channel ID is by listening for webhook events (e.g., `message.upsert` or `message.created` if your provider supports it for channels), as this event data for messages originating from a channel will include the channel's JID.
17+
2. **SDK Initialization:** Ensure the Wasender Python SDK is correctly initialized in your project. This involves:
18+
* Installing the SDK: `pip install wasenderapi`
19+
* Setting the environment variable `WASENDER_API_KEY`. This API key should correspond to the specific WhatsApp session you intend to use for sending channel messages.
20+
* Creating an instance of `WasenderClient`.
21+
(Refer to `README.md` or `docs/messages.md` for detailed SDK initialization examples.)
22+
23+
## How to Send a Message to a Channel
24+
25+
You will use the `client.send()` method with a `TextPayload` Pydantic model. The Python SDK also provides a specific type alias `wasenderapi.models.channel.ChannelTextMessage` which is an alias for `TextPayload`, intended for conceptual clarity when working with channels.
26+
27+
### Python Model for Channel Messages
28+
29+
The relevant Pydantic model from `wasenderapi.models.channel` is:
30+
31+
```python
32+
# From wasenderapi/models/channel.py
33+
# (Conceptual - actual import would be from wasenderapi.models)
34+
from ..models.message import TextPayload # Relative import path for illustration
35+
36+
ChannelTextMessage = TextPayload
37+
```
38+
This means you can directly use `TextPayload` from `wasenderapi.models` when constructing your message for a channel.
39+
40+
### Code Example
41+
42+
Here's how you can send a text message to a WhatsApp Channel in Python:
43+
44+
```python
45+
# examples/send_channel_message.py
46+
import asyncio
47+
import os
48+
import logging
49+
from datetime import datetime
50+
from typing import Optional
51+
52+
from wasenderapi import WasenderClient
53+
from wasenderapi.errors import WasenderAPIError
54+
from wasenderapi.models import (
55+
TextPayload, # Equivalent to ChannelTextMessage
56+
WasenderSendResult,
57+
RateLimitInfo
58+
)
59+
60+
# Configure basic logging
61+
logging.basicConfig(level=logging.INFO)
62+
logger = logging.getLogger(__name__)
63+
64+
# --- SDK Initialization (Minimal for this example) ---
65+
api_key = os.getenv("WASENDER_API_KEY") # API Key for the session sending the message
66+
67+
if not api_key:
68+
logger.error("Error: WASENDER_API_KEY environment variable is not set.")
69+
exit(1)
70+
71+
# For sending messages via an existing session, typically only the session's API key is needed.
72+
client = WasenderClient(api_key=api_key)
73+
74+
# The personal_access_token is generally used for account-level session management (create, list, delete sessions),
75+
# not for sending messages through an already established session.
76+
# # personal_access_token = os.getenv("WASENDER_PERSONAL_ACCESS_TOKEN") # Token for account-level session operations
77+
# # client = WasenderClient(api_key=api_key, personal_access_token=personal_access_token) # If needed for specific advanced scenarios or different auth models
78+
79+
# --- Helper to log rate limits (can be imported from a common utils module) ---
80+
def log_rate_limit_info(rate_limit: Optional[RateLimitInfo]):
81+
if rate_limit:
82+
reset_time_str = datetime.fromtimestamp(rate_limit.reset_timestamp).strftime('%Y-%m-%d %H:%M:%S') if rate_limit.reset_timestamp else "N/A"
83+
logger.info(
84+
f"Rate Limit Info: Remaining = {rate_limit.remaining}, Limit = {rate_limit.limit}, Resets at = {reset_time_str}"
85+
)
86+
else:
87+
logger.info("Rate limit information not available for this request.")
88+
89+
# --- Generic Error Handler (can be imported) ---
90+
def handle_channel_api_error(error: Exception, operation: str, channel_jid: Optional[str] = None):
91+
target = f" for channel {channel_jid}" if channel_jid else ""
92+
if isinstance(error, WasenderAPIError):
93+
logger.error(f"API Error during {operation}{target}:")
94+
logger.error(f" Message: {error.message}")
95+
logger.error(f" Status Code: {error.status_code or 'N/A'}")
96+
if error.api_message: logger.error(f" API Message: {error.api_message}")
97+
if error.error_details: logger.error(f" Error Details: {error.error_details}")
98+
if error.rate_limit: log_rate_limit_info(error.rate_limit)
99+
else:
100+
logger.error(f"An unexpected error occurred during {operation}{target}: {error}")
101+
102+
# --- Main function to send message to channel ---
103+
async def send_message_to_channel_example(channel_jid: str, message_text: str):
104+
logger.info(f"\\n--- Attempting to Send Message to Channel: {channel_jid} ---")
105+
if not channel_jid:
106+
logger.error("Channel JID is required.")
107+
return
108+
if not message_text:
109+
logger.error("Message text is required.")
110+
return
111+
112+
# Use TextPayload (aliased as ChannelTextMessage in the SDK for conceptual clarity)
113+
channel_payload = TextPayload(
114+
to=channel_jid,
115+
text=message_text,
116+
# messageType defaults to "text" in TextPayload model, so explicit set is optional
117+
# but good for clarity with channels: message_type="text"
118+
)
119+
120+
try:
121+
result = await client.send(payload=channel_payload)
122+
send_result: WasenderSendResult = result.response.data
123+
124+
logger.info(f"Message sent to channel {channel_jid} successfully.")
125+
logger.info(f" Message ID: {send_result.message_id}")
126+
logger.info(f" Status: {send_result.status}")
127+
if send_result.detail:
128+
logger.info(f" Detail: {send_result.detail}")
129+
130+
log_rate_limit_info(result.rate_limit)
131+
132+
except Exception as e:
133+
handle_channel_api_error(e, "sending message", channel_jid=channel_jid)
134+
135+
async def main():
136+
# Replace with the actual Channel ID you want to send a message to
137+
target_channel_jid = "12345678901234567890@newsletter" # Example JID
138+
message = "Hello Channel! This is a test message from the Python SDK."
139+
140+
if target_channel_jid == "12345678901234567890@newsletter":
141+
logger.warning("Please replace `target_channel_jid` with a real Channel ID before running.")
142+
else:
143+
await send_message_to_channel_example(target_channel_jid, message)
144+
145+
# Example for another channel or message:
146+
# another_channel_jid = "09876543210987654321@newsletter"
147+
# await send_message_to_channel_example(another_channel_jid, "Another important update!")
148+
149+
if __name__ == "__main__":
150+
# Before running, ensure WASENDER_API_KEY is set in your environment.
151+
# Also, replace target_channel_jid in main() with a valid Channel ID.
152+
logger.info("Starting channel message example. Ensure JID and API Key are set.")
153+
asyncio.run(main())
154+
155+
```
156+
157+
### Key Points from the Example:
158+
159+
- **`to`**: Set to the `target_channel_jid`.
160+
- **`TextPayload`**: Used to construct the message. The `message_type` attribute within `TextPayload` defaults to `"text"`, which is what channels typically require. You can explicitly set `message_type="text"` for clarity if desired.
161+
- **`text`**: Contains the content of your message.
162+
- The example includes minimal SDK initialization, error handling, and rate limit logging.
163+
164+
## Important Considerations
165+
166+
- **Channel ID Accuracy:** Ensure the Channel ID (ending in `@newsletter`) is correct. Sending to an incorrect ID will fail.
167+
- **Message Content:** As emphasized, typically only text messages are supported for channels via this standard send method. Sending other types will likely result in an API error. Always verify with current API documentation.
168+
- **API Limitations:** The ability to send messages to channels, supported message types, and any other restrictions are determined by the underlying Wasender API. Refer to the official Wasender API documentation for the most up-to-date information.
169+
- **Webhook for Channel IDs:** Using webhooks to listen for relevant message events (like `message.created` or `message.upsert`) is a practical way to discover Channel IDs your connected number interacts with or is part of, especially if these channels are not self-created.
170+
171+
This guide should provide you with the necessary information to send text messages to WhatsApp Channels using the Wasender Python SDK.

0 commit comments

Comments
 (0)