universal-mcp-applications 0.1.2__py3-none-any.whl → 0.1.3__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.
- universal_mcp/applications/airtable/app.py +1 -0
- universal_mcp/applications/apollo/app.py +1 -0
- universal_mcp/applications/aws_s3/app.py +3 -4
- universal_mcp/applications/bill/app.py +3 -3
- universal_mcp/applications/box/app.py +2 -6
- universal_mcp/applications/braze/app.py +2 -6
- universal_mcp/applications/cal_com_v2/app.py +22 -64
- universal_mcp/applications/confluence/app.py +1 -0
- universal_mcp/applications/contentful/app.py +8 -19
- universal_mcp/applications/digitalocean/app.py +9 -27
- universal_mcp/applications/{domain-checker → domain_checker}/app.py +2 -1
- universal_mcp/applications/elevenlabs/app.py +98 -3188
- universal_mcp/applications/falai/app.py +1 -0
- universal_mcp/applications/file_system/__init__.py +1 -0
- universal_mcp/applications/file_system/app.py +96 -0
- universal_mcp/applications/fireflies/app.py +4 -3
- universal_mcp/applications/fpl/app.py +1 -0
- universal_mcp/applications/fpl/utils/fixtures.py +1 -1
- universal_mcp/applications/fpl/utils/helper.py +1 -1
- universal_mcp/applications/fpl/utils/position_utils.py +0 -1
- universal_mcp/applications/{ghost-content → ghost_content}/app.py +2 -1
- universal_mcp/applications/github/app.py +3 -1
- universal_mcp/applications/google_calendar/app.py +2 -1
- universal_mcp/applications/google_docs/app.py +1 -1
- universal_mcp/applications/google_drive/app.py +2 -1
- universal_mcp/applications/google_gemini/app.py +138 -618
- universal_mcp/applications/google_mail/app.py +2 -1
- universal_mcp/applications/{google-searchconsole → google_searchconsole}/app.py +1 -1
- universal_mcp/applications/google_sheet/app.py +2 -1
- universal_mcp/applications/google_sheet/helper.py +156 -116
- universal_mcp/applications/hashnode/app.py +1 -0
- universal_mcp/applications/{http-tools → http_tools}/app.py +2 -1
- universal_mcp/applications/hubspot/app.py +4 -1
- universal_mcp/applications/jira/app.py +7 -18
- universal_mcp/applications/markitdown/app.py +2 -3
- universal_mcp/applications/ms_teams/app.py +1 -1
- universal_mcp/applications/openai/app.py +2 -3
- universal_mcp/applications/outlook/app.py +1 -3
- universal_mcp/applications/pipedrive/app.py +2 -6
- universal_mcp/applications/reddit/app.py +1 -0
- universal_mcp/applications/replicate/app.py +3 -3
- universal_mcp/applications/resend/app.py +1 -2
- universal_mcp/applications/rocketlane/app.py +1 -0
- universal_mcp/applications/semrush/app.py +1 -1
- universal_mcp/applications/sentry/README.md +20 -20
- universal_mcp/applications/sentry/app.py +40 -40
- universal_mcp/applications/serpapi/app.py +2 -2
- universal_mcp/applications/sharepoint/app.py +1 -0
- universal_mcp/applications/shopify/app.py +1 -0
- universal_mcp/applications/slack/app.py +3 -3
- universal_mcp/applications/trello/app.py +9 -27
- universal_mcp/applications/twilio/__init__.py +1 -0
- universal_mcp/applications/{twillo → twilio}/app.py +2 -2
- universal_mcp/applications/twitter/README.md +1 -1
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +2 -2
- universal_mcp/applications/twitter/api_segments/lists_api.py +1 -1
- universal_mcp/applications/unipile/app.py +5 -1
- universal_mcp/applications/whatsapp/app.py +18 -17
- universal_mcp/applications/whatsapp/audio.py +110 -0
- universal_mcp/applications/whatsapp/whatsapp.py +398 -0
- universal_mcp/applications/whatsapp_business/app.py +1 -1
- universal_mcp/applications/youtube/app.py +195 -191
- universal_mcp/applications/zenquotes/app.py +1 -1
- {universal_mcp_applications-0.1.2.dist-info → universal_mcp_applications-0.1.3.dist-info}/METADATA +4 -2
- {universal_mcp_applications-0.1.2.dist-info → universal_mcp_applications-0.1.3.dist-info}/RECORD +76 -75
- universal_mcp/applications/google-ads/__init__.py +0 -1
- universal_mcp/applications/google-ads/app.py +0 -23
- universal_mcp/applications/twillo/README.md +0 -0
- universal_mcp/applications/twillo/__init__.py +0 -1
- /universal_mcp/applications/{domain-checker → domain_checker}/README.md +0 -0
- /universal_mcp/applications/{domain-checker → domain_checker}/__init__.py +0 -0
- /universal_mcp/applications/{ghost-content → ghost_content}/README.md +0 -0
- /universal_mcp/applications/{ghost-content → ghost_content}/__init__.py +0 -0
- /universal_mcp/applications/{google-searchconsole → google_searchconsole}/README.md +0 -0
- /universal_mcp/applications/{google-searchconsole → google_searchconsole}/__init__.py +0 -0
- /universal_mcp/applications/{http-tools → http_tools}/README.md +0 -0
- /universal_mcp/applications/{http-tools → http_tools}/__init__.py +0 -0
- /universal_mcp/applications/{google-ads → twilio}/README.md +0 -0
- {universal_mcp_applications-0.1.2.dist-info → universal_mcp_applications-0.1.3.dist-info}/WHEEL +0 -0
- {universal_mcp_applications-0.1.2.dist-info → universal_mcp_applications-0.1.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Optional, List, Tuple
|
|
6
|
+
import os.path
|
|
7
|
+
from . import audio
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
|
|
10
|
+
load_dotenv()
|
|
11
|
+
|
|
12
|
+
WHATSAPP_API_BASE_URL = os.getenv('WHATSAPP_API_BASE_URL', "http://134.209.144.43:8080")
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Message:
|
|
16
|
+
timestamp: datetime
|
|
17
|
+
sender: str
|
|
18
|
+
content: str
|
|
19
|
+
is_from_me: bool
|
|
20
|
+
chat_jid: str
|
|
21
|
+
id: str
|
|
22
|
+
chat_name: Optional[str] = None
|
|
23
|
+
media_type: Optional[str] = None
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class Chat:
|
|
27
|
+
jid: str
|
|
28
|
+
name: Optional[str]
|
|
29
|
+
last_message_time: Optional[datetime]
|
|
30
|
+
last_message: Optional[str] = None
|
|
31
|
+
last_sender: Optional[str] = None
|
|
32
|
+
last_is_from_me: Optional[bool] = None
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def is_group(self) -> bool:
|
|
36
|
+
"""Determine if chat is a group based on JID pattern."""
|
|
37
|
+
return self.jid.endswith("@g.us")
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class Contact:
|
|
41
|
+
phone_number: str
|
|
42
|
+
name: Optional[str]
|
|
43
|
+
jid: str
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class MessageContext:
|
|
47
|
+
message: Message
|
|
48
|
+
before: List[Message]
|
|
49
|
+
after: List[Message]
|
|
50
|
+
|
|
51
|
+
def _make_api_request(endpoint: str, method: str = "GET", data: dict = None, user_id: str = "default_user") -> dict:
|
|
52
|
+
"""Make HTTP request to WhatsApp Bridge API."""
|
|
53
|
+
url = f"{WHATSAPP_API_BASE_URL}/api/{endpoint}"
|
|
54
|
+
|
|
55
|
+
# Add user_id to query parameters for GET requests
|
|
56
|
+
if method.upper() == "GET" and data:
|
|
57
|
+
data["user_id"] = user_id
|
|
58
|
+
elif method.upper() == "GET":
|
|
59
|
+
data = {"user_id": user_id}
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
if method.upper() == "GET":
|
|
63
|
+
response = requests.get(url, params=data, timeout=30)
|
|
64
|
+
elif method.upper() == "POST":
|
|
65
|
+
response = requests.post(url, json=data, timeout=30)
|
|
66
|
+
else:
|
|
67
|
+
raise ValueError(f"Unsupported HTTP method: {method}")
|
|
68
|
+
|
|
69
|
+
if response.status_code == 200:
|
|
70
|
+
return response.json()
|
|
71
|
+
else:
|
|
72
|
+
return {"error": f"HTTP {response.status_code}: {response.text}"}
|
|
73
|
+
|
|
74
|
+
except requests.RequestException as e:
|
|
75
|
+
return {"error": f"Request failed: {str(e)}"}
|
|
76
|
+
except json.JSONDecodeError:
|
|
77
|
+
return {"error": f"Invalid JSON response: {response.text}"}
|
|
78
|
+
|
|
79
|
+
def get_sender_name(sender_jid: str, user_id: str = "default_user") -> str:
|
|
80
|
+
"""Get sender name via API call."""
|
|
81
|
+
result = _make_api_request("sender_name", data={"sender_jid": sender_jid}, user_id=user_id)
|
|
82
|
+
|
|
83
|
+
if "error" in result:
|
|
84
|
+
return sender_jid
|
|
85
|
+
|
|
86
|
+
return result.get("name", sender_jid)
|
|
87
|
+
|
|
88
|
+
def format_message(message: Message, show_chat_info: bool = True, user_id: str = "default_user") -> str:
|
|
89
|
+
"""Print a single message with consistent formatting."""
|
|
90
|
+
output = ""
|
|
91
|
+
|
|
92
|
+
if show_chat_info and message.chat_name:
|
|
93
|
+
output += f"[{message.timestamp:%Y-%m-%d %H:%M:%S}] Chat: {message.chat_name} "
|
|
94
|
+
else:
|
|
95
|
+
output += f"[{message.timestamp:%Y-%m-%d %H:%M:%S}] "
|
|
96
|
+
|
|
97
|
+
content_prefix = ""
|
|
98
|
+
if hasattr(message, 'media_type') and message.media_type:
|
|
99
|
+
content_prefix = f"[{message.media_type} - Message ID: {message.id} - Chat JID: {message.chat_jid}] "
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
sender_name = get_sender_name(message.sender, user_id) if not message.is_from_me else "Me"
|
|
103
|
+
output += f"From: {sender_name}: {content_prefix}{message.content}\n"
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(f"Error formatting message: {e}")
|
|
106
|
+
return output
|
|
107
|
+
|
|
108
|
+
def format_messages_list(messages: List[Message], show_chat_info: bool = True, user_id: str = "default_user") -> str:
|
|
109
|
+
output = ""
|
|
110
|
+
if not messages:
|
|
111
|
+
output += "No messages to display."
|
|
112
|
+
return output
|
|
113
|
+
|
|
114
|
+
for message in messages:
|
|
115
|
+
output += format_message(message, show_chat_info, user_id)
|
|
116
|
+
return output
|
|
117
|
+
|
|
118
|
+
def list_messages(
|
|
119
|
+
after: Optional[str] = None,
|
|
120
|
+
before: Optional[str] = None,
|
|
121
|
+
sender_phone_number: Optional[str] = None,
|
|
122
|
+
chat_jid: Optional[str] = None,
|
|
123
|
+
query: Optional[str] = None,
|
|
124
|
+
limit: int = 20,
|
|
125
|
+
page: int = 0,
|
|
126
|
+
include_context: bool = True,
|
|
127
|
+
context_before: int = 1,
|
|
128
|
+
context_after: int = 1,
|
|
129
|
+
user_id: str = "default_user"
|
|
130
|
+
) -> str:
|
|
131
|
+
"""Get messages matching the specified criteria with optional context via API."""
|
|
132
|
+
params = {
|
|
133
|
+
"after": after,
|
|
134
|
+
"before": before,
|
|
135
|
+
"sender_phone_number": sender_phone_number,
|
|
136
|
+
"chat_jid": chat_jid,
|
|
137
|
+
"query": query,
|
|
138
|
+
"limit": limit,
|
|
139
|
+
"page": page,
|
|
140
|
+
"include_context": include_context,
|
|
141
|
+
"context_before": context_before,
|
|
142
|
+
"context_after": context_after
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# Remove None values
|
|
146
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
147
|
+
|
|
148
|
+
result = _make_api_request("messages", data=params, user_id=user_id)
|
|
149
|
+
|
|
150
|
+
if "error" in result:
|
|
151
|
+
return f"Error retrieving messages: {result['error']}"
|
|
152
|
+
|
|
153
|
+
return result.get("messages", "No messages found")
|
|
154
|
+
|
|
155
|
+
def get_message_context(
|
|
156
|
+
message_id: str,
|
|
157
|
+
before: int = 5,
|
|
158
|
+
after: int = 5,
|
|
159
|
+
user_id: str = "default_user"
|
|
160
|
+
) -> MessageContext:
|
|
161
|
+
"""Get context around a specific message via API."""
|
|
162
|
+
params = {
|
|
163
|
+
"message_id": message_id,
|
|
164
|
+
"before": before,
|
|
165
|
+
"after": after
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
result = _make_api_request("message_context", data=params, user_id=user_id)
|
|
169
|
+
|
|
170
|
+
if "error" in result:
|
|
171
|
+
raise ValueError(f"Error getting message context: {result['error']}")
|
|
172
|
+
|
|
173
|
+
# Parse the response into MessageContext object
|
|
174
|
+
# This would need to be implemented based on the API response structure
|
|
175
|
+
# For now, returning a simple error message
|
|
176
|
+
raise NotImplementedError("Message context API not yet implemented in bridge")
|
|
177
|
+
|
|
178
|
+
def list_chats(
|
|
179
|
+
query: Optional[str] = None,
|
|
180
|
+
limit: int = 20,
|
|
181
|
+
page: int = 0,
|
|
182
|
+
include_last_message: bool = True,
|
|
183
|
+
sort_by: str = "last_active",
|
|
184
|
+
user_id: str = "default_user"
|
|
185
|
+
) -> List[Chat]:
|
|
186
|
+
"""Get chats matching the specified criteria via API."""
|
|
187
|
+
params = {
|
|
188
|
+
"query": query,
|
|
189
|
+
"limit": limit,
|
|
190
|
+
"page": page,
|
|
191
|
+
"include_last_message": include_last_message,
|
|
192
|
+
"sort_by": sort_by
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# Remove None values
|
|
196
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
197
|
+
|
|
198
|
+
result = _make_api_request("chats", data=params, user_id=user_id)
|
|
199
|
+
|
|
200
|
+
if "error" in result:
|
|
201
|
+
print(f"Error retrieving chats: {result['error']}")
|
|
202
|
+
return []
|
|
203
|
+
|
|
204
|
+
chats_data = result.get("chats", [])
|
|
205
|
+
chats = []
|
|
206
|
+
|
|
207
|
+
for chat_data in chats_data:
|
|
208
|
+
chat = Chat(
|
|
209
|
+
jid=chat_data["jid"],
|
|
210
|
+
name=chat_data.get("name"),
|
|
211
|
+
last_message_time=datetime.fromisoformat(chat_data["last_message_time"]) if chat_data.get("last_message_time") else None,
|
|
212
|
+
last_message=chat_data.get("last_message"),
|
|
213
|
+
last_sender=chat_data.get("last_sender"),
|
|
214
|
+
last_is_from_me=chat_data.get("last_is_from_me")
|
|
215
|
+
)
|
|
216
|
+
chats.append(chat)
|
|
217
|
+
|
|
218
|
+
return chats
|
|
219
|
+
|
|
220
|
+
def search_contacts(query: str, user_id: str = "default_user") -> List[Contact]:
|
|
221
|
+
"""Search contacts by name or phone number via API."""
|
|
222
|
+
result = _make_api_request("contacts", data={"query": query}, user_id=user_id)
|
|
223
|
+
|
|
224
|
+
if "error" in result:
|
|
225
|
+
print(f"Error searching contacts: {result['error']}")
|
|
226
|
+
return []
|
|
227
|
+
|
|
228
|
+
contacts_data = result.get("contacts", [])
|
|
229
|
+
contacts = []
|
|
230
|
+
|
|
231
|
+
for contact_data in contacts_data:
|
|
232
|
+
contact = Contact(
|
|
233
|
+
phone_number=contact_data["phone_number"],
|
|
234
|
+
name=contact_data.get("name"),
|
|
235
|
+
jid=contact_data["jid"]
|
|
236
|
+
)
|
|
237
|
+
contacts.append(contact)
|
|
238
|
+
|
|
239
|
+
return contacts
|
|
240
|
+
|
|
241
|
+
def get_contact_chats(jid: str, limit: int = 20, page: int = 0, user_id: str = "default_user") -> List[Chat]:
|
|
242
|
+
"""Get all chats involving the contact via API."""
|
|
243
|
+
params = {
|
|
244
|
+
"jid": jid,
|
|
245
|
+
"limit": limit,
|
|
246
|
+
"page": page
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
result = _make_api_request("contact_chats", data=params, user_id=user_id)
|
|
250
|
+
|
|
251
|
+
if "error" in result:
|
|
252
|
+
print(f"Error getting contact chats: {result['error']}")
|
|
253
|
+
return []
|
|
254
|
+
|
|
255
|
+
chats_data = result.get("chats", [])
|
|
256
|
+
chats = []
|
|
257
|
+
|
|
258
|
+
for chat_data in chats_data:
|
|
259
|
+
chat = Chat(
|
|
260
|
+
jid=chat_data["jid"],
|
|
261
|
+
name=chat_data.get("name"),
|
|
262
|
+
last_message_time=datetime.fromisoformat(chat_data["last_message_time"]) if chat_data.get("last_message_time") else None,
|
|
263
|
+
last_message=chat_data.get("last_message"),
|
|
264
|
+
last_sender=chat_data.get("last_sender"),
|
|
265
|
+
last_is_from_me=chat_data.get("last_is_from_me")
|
|
266
|
+
)
|
|
267
|
+
chats.append(chat)
|
|
268
|
+
|
|
269
|
+
return chats
|
|
270
|
+
|
|
271
|
+
def get_last_interaction(jid: str, user_id: str = "default_user") -> str:
|
|
272
|
+
"""Get most recent message involving the contact via API."""
|
|
273
|
+
result = _make_api_request("last_interaction", data={"jid": jid}, user_id=user_id)
|
|
274
|
+
|
|
275
|
+
if "error" in result:
|
|
276
|
+
return f"Error getting last interaction: {result['error']}"
|
|
277
|
+
|
|
278
|
+
return result.get("message", "No interaction found")
|
|
279
|
+
|
|
280
|
+
def get_chat(chat_jid: str, include_last_message: bool = True, user_id: str = "default_user") -> Optional[Chat]:
|
|
281
|
+
"""Get chat metadata by JID via API."""
|
|
282
|
+
params = {
|
|
283
|
+
"chat_jid": chat_jid,
|
|
284
|
+
"include_last_message": include_last_message
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
result = _make_api_request("chat", data=params, user_id=user_id)
|
|
288
|
+
|
|
289
|
+
if "error" in result:
|
|
290
|
+
print(f"Error getting chat: {result['error']}")
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
chat_data = result.get("chat")
|
|
294
|
+
if not chat_data:
|
|
295
|
+
return None
|
|
296
|
+
|
|
297
|
+
return Chat(
|
|
298
|
+
jid=chat_data["jid"],
|
|
299
|
+
name=chat_data.get("name"),
|
|
300
|
+
last_message_time=datetime.fromisoformat(chat_data["last_message_time"]) if chat_data.get("last_message_time") else None,
|
|
301
|
+
last_message=chat_data.get("last_message"),
|
|
302
|
+
last_sender=chat_data.get("last_sender"),
|
|
303
|
+
last_is_from_me=chat_data.get("last_is_from_me")
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def get_direct_chat_by_contact(sender_phone_number: str, user_id: str = "default_user") -> Optional[Chat]:
|
|
307
|
+
"""Get chat metadata by sender phone number via API."""
|
|
308
|
+
result = _make_api_request("direct_chat", data={"sender_phone_number": sender_phone_number}, user_id=user_id)
|
|
309
|
+
|
|
310
|
+
if "error" in result:
|
|
311
|
+
print(f"Error getting direct chat: {result['error']}")
|
|
312
|
+
return None
|
|
313
|
+
|
|
314
|
+
chat_data = result.get("chat")
|
|
315
|
+
if not chat_data:
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
return Chat(
|
|
319
|
+
jid=chat_data["jid"],
|
|
320
|
+
name=chat_data.get("name"),
|
|
321
|
+
last_message_time=datetime.fromisoformat(chat_data["last_message_time"]) if chat_data.get("last_message_time") else None,
|
|
322
|
+
last_message=chat_data.get("last_message"),
|
|
323
|
+
last_sender=chat_data.get("last_sender"),
|
|
324
|
+
last_is_from_me=chat_data.get("last_is_from_me")
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
def send_message(recipient: str, message: str, user_id: str = "default_user") -> Tuple[bool, str]:
|
|
328
|
+
"""Send message via API."""
|
|
329
|
+
payload = {
|
|
330
|
+
"user_id": user_id,
|
|
331
|
+
"recipient": recipient,
|
|
332
|
+
"message": message
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
result = _make_api_request("send", method="POST", data=payload, user_id=user_id)
|
|
336
|
+
|
|
337
|
+
if "error" in result:
|
|
338
|
+
return False, result["error"]
|
|
339
|
+
|
|
340
|
+
return result.get("success", False), result.get("message", "Unknown response")
|
|
341
|
+
|
|
342
|
+
def send_file(recipient: str, media_path: str, user_id: str = "default_user") -> Tuple[bool, str]:
|
|
343
|
+
"""Send file via API."""
|
|
344
|
+
payload = {
|
|
345
|
+
"user_id": user_id,
|
|
346
|
+
"recipient": recipient,
|
|
347
|
+
"media_path": media_path
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
result = _make_api_request("send", method="POST", data=payload, user_id=user_id)
|
|
351
|
+
|
|
352
|
+
if "error" in result:
|
|
353
|
+
return False, result["error"]
|
|
354
|
+
|
|
355
|
+
return result.get("success", False), result.get("message", "Unknown response")
|
|
356
|
+
|
|
357
|
+
def send_audio_message(recipient: str, media_path: str, user_id: str = "default_user") -> Tuple[bool, str]:
|
|
358
|
+
"""Send audio message via API."""
|
|
359
|
+
if not media_path.endswith(".ogg"):
|
|
360
|
+
try:
|
|
361
|
+
media_path = audio.convert_to_opus_ogg_temp(media_path)
|
|
362
|
+
except Exception as e:
|
|
363
|
+
return False, f"Error converting file to opus ogg. You likely need to install ffmpeg: {str(e)}"
|
|
364
|
+
|
|
365
|
+
payload = {
|
|
366
|
+
"user_id": user_id,
|
|
367
|
+
"recipient": recipient,
|
|
368
|
+
"media_path": media_path
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
result = _make_api_request("send", method="POST", data=payload, user_id=user_id)
|
|
372
|
+
|
|
373
|
+
if "error" in result:
|
|
374
|
+
return False, result["error"]
|
|
375
|
+
|
|
376
|
+
return result.get("success", False), result.get("message", "Unknown response")
|
|
377
|
+
|
|
378
|
+
def download_media(message_id: str, chat_jid: str, user_id: str = "default_user") -> Optional[str]:
|
|
379
|
+
"""Download media from a message via API."""
|
|
380
|
+
payload = {
|
|
381
|
+
"user_id": user_id,
|
|
382
|
+
"message_id": message_id,
|
|
383
|
+
"chat_jid": chat_jid
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
result = _make_api_request("download", method="POST", data=payload, user_id=user_id)
|
|
387
|
+
|
|
388
|
+
if "error" in result:
|
|
389
|
+
print(f"Download failed: {result['error']}")
|
|
390
|
+
return None
|
|
391
|
+
|
|
392
|
+
if result.get("success", False):
|
|
393
|
+
path = result.get("path")
|
|
394
|
+
print(f"Media downloaded successfully: {path}")
|
|
395
|
+
return path
|
|
396
|
+
else:
|
|
397
|
+
print(f"Download failed: {result.get('message', 'Unknown error')}")
|
|
398
|
+
return None
|
|
@@ -6,7 +6,7 @@ from universal_mcp.integrations import Integration
|
|
|
6
6
|
|
|
7
7
|
class WhatsappBusinessApp(APIApplication):
|
|
8
8
|
def __init__(self, integration: Integration = None, **kwargs) -> None:
|
|
9
|
-
super().__init__(name="
|
|
9
|
+
super().__init__(name="whatsapp_business", integration=integration, **kwargs)
|
|
10
10
|
self.base_url = "https://graph.facebook.com"
|
|
11
11
|
|
|
12
12
|
def get_analytics(
|