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