universal-mcp-applications 0.1.33__py3-none-any.whl → 0.1.39rc8__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/ahrefs/app.py +92 -238
- universal_mcp/applications/airtable/app.py +23 -122
- universal_mcp/applications/apollo/app.py +122 -475
- universal_mcp/applications/asana/app.py +605 -1755
- universal_mcp/applications/aws_s3/app.py +36 -103
- universal_mcp/applications/bill/app.py +644 -2055
- universal_mcp/applications/box/app.py +1246 -4159
- universal_mcp/applications/braze/app.py +410 -1476
- universal_mcp/applications/browser_use/README.md +15 -1
- universal_mcp/applications/browser_use/__init__.py +1 -0
- universal_mcp/applications/browser_use/app.py +86 -24
- universal_mcp/applications/cal_com_v2/app.py +207 -625
- universal_mcp/applications/calendly/app.py +103 -242
- universal_mcp/applications/canva/app.py +75 -140
- universal_mcp/applications/clickup/app.py +331 -798
- universal_mcp/applications/coda/app.py +240 -520
- universal_mcp/applications/confluence/app.py +497 -1285
- universal_mcp/applications/contentful/app.py +36 -151
- universal_mcp/applications/crustdata/app.py +42 -121
- universal_mcp/applications/dialpad/app.py +451 -924
- universal_mcp/applications/digitalocean/app.py +2071 -6082
- universal_mcp/applications/domain_checker/app.py +3 -54
- universal_mcp/applications/e2b/app.py +14 -64
- universal_mcp/applications/elevenlabs/app.py +9 -47
- universal_mcp/applications/exa/README.md +8 -4
- universal_mcp/applications/exa/app.py +408 -186
- universal_mcp/applications/falai/app.py +24 -101
- universal_mcp/applications/figma/app.py +91 -175
- universal_mcp/applications/file_system/app.py +2 -13
- universal_mcp/applications/firecrawl/app.py +186 -163
- universal_mcp/applications/fireflies/app.py +59 -281
- universal_mcp/applications/fpl/app.py +92 -529
- universal_mcp/applications/fpl/utils/fixtures.py +15 -49
- universal_mcp/applications/fpl/utils/helper.py +25 -89
- universal_mcp/applications/fpl/utils/league_utils.py +20 -64
- universal_mcp/applications/ghost_content/app.py +66 -175
- universal_mcp/applications/github/app.py +28 -65
- universal_mcp/applications/gong/app.py +140 -300
- universal_mcp/applications/google_calendar/app.py +26 -78
- universal_mcp/applications/google_docs/app.py +98 -202
- universal_mcp/applications/google_drive/app.py +194 -793
- universal_mcp/applications/google_gemini/app.py +27 -62
- universal_mcp/applications/google_mail/README.md +1 -0
- universal_mcp/applications/google_mail/app.py +93 -214
- universal_mcp/applications/google_searchconsole/app.py +25 -58
- universal_mcp/applications/google_sheet/app.py +171 -624
- universal_mcp/applications/google_sheet/helper.py +26 -53
- universal_mcp/applications/hashnode/app.py +57 -269
- universal_mcp/applications/heygen/app.py +77 -155
- universal_mcp/applications/http_tools/app.py +10 -32
- universal_mcp/applications/hubspot/README.md +1 -1
- universal_mcp/applications/hubspot/app.py +7508 -99
- universal_mcp/applications/jira/app.py +2419 -8334
- universal_mcp/applications/klaviyo/app.py +737 -1619
- universal_mcp/applications/linkedin/README.md +5 -0
- universal_mcp/applications/linkedin/app.py +332 -227
- universal_mcp/applications/mailchimp/app.py +696 -1851
- universal_mcp/applications/markitdown/app.py +8 -20
- universal_mcp/applications/miro/app.py +333 -815
- universal_mcp/applications/ms_teams/app.py +85 -207
- universal_mcp/applications/neon/app.py +144 -250
- universal_mcp/applications/notion/app.py +36 -51
- universal_mcp/applications/onedrive/app.py +26 -48
- universal_mcp/applications/openai/app.py +42 -165
- universal_mcp/applications/outlook/README.md +22 -9
- universal_mcp/applications/outlook/app.py +403 -141
- universal_mcp/applications/perplexity/README.md +2 -1
- universal_mcp/applications/perplexity/app.py +162 -20
- universal_mcp/applications/pipedrive/app.py +1021 -3331
- universal_mcp/applications/posthog/app.py +272 -541
- universal_mcp/applications/reddit/app.py +61 -160
- universal_mcp/applications/resend/app.py +41 -107
- universal_mcp/applications/retell/app.py +23 -50
- universal_mcp/applications/rocketlane/app.py +250 -963
- universal_mcp/applications/scraper/app.py +67 -125
- universal_mcp/applications/semanticscholar/app.py +36 -78
- universal_mcp/applications/semrush/app.py +43 -77
- universal_mcp/applications/sendgrid/app.py +826 -1576
- universal_mcp/applications/sentry/app.py +444 -1079
- universal_mcp/applications/serpapi/app.py +40 -143
- universal_mcp/applications/sharepoint/app.py +27 -49
- universal_mcp/applications/shopify/app.py +1743 -4479
- universal_mcp/applications/shortcut/app.py +272 -534
- universal_mcp/applications/slack/app.py +41 -123
- universal_mcp/applications/spotify/app.py +206 -405
- universal_mcp/applications/supabase/app.py +174 -283
- universal_mcp/applications/tavily/app.py +2 -2
- universal_mcp/applications/trello/app.py +853 -2816
- universal_mcp/applications/twilio/app.py +14 -50
- universal_mcp/applications/twitter/api_segments/compliance_api.py +4 -14
- universal_mcp/applications/twitter/api_segments/dm_conversations_api.py +6 -18
- universal_mcp/applications/twitter/api_segments/likes_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/lists_api.py +5 -15
- universal_mcp/applications/twitter/api_segments/trends_api.py +1 -3
- universal_mcp/applications/twitter/api_segments/tweets_api.py +9 -31
- universal_mcp/applications/twitter/api_segments/usage_api.py +1 -5
- universal_mcp/applications/twitter/api_segments/users_api.py +14 -42
- universal_mcp/applications/whatsapp/app.py +35 -186
- universal_mcp/applications/whatsapp/audio.py +2 -6
- universal_mcp/applications/whatsapp/whatsapp.py +17 -51
- universal_mcp/applications/whatsapp_business/app.py +86 -299
- universal_mcp/applications/wrike/app.py +80 -153
- universal_mcp/applications/yahoo_finance/app.py +19 -65
- universal_mcp/applications/youtube/app.py +120 -306
- universal_mcp/applications/zenquotes/app.py +3 -3
- {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/METADATA +4 -2
- {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/RECORD +109 -113
- {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/WHEEL +1 -1
- universal_mcp/applications/hubspot/api_segments/__init__.py +0 -0
- universal_mcp/applications/hubspot/api_segments/api_segment_base.py +0 -54
- universal_mcp/applications/hubspot/api_segments/crm_api.py +0 -7337
- universal_mcp/applications/hubspot/api_segments/marketing_api.py +0 -1467
- {universal_mcp_applications-0.1.33.dist-info → universal_mcp_applications-0.1.39rc8.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import os
|
|
3
|
-
from
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
import requests
|
|
4
5
|
|
|
5
6
|
from loguru import logger
|
|
6
|
-
from universal_mcp.applications.application import APIApplication
|
|
7
|
+
from universal_mcp.applications.application import APIApplication, BaseApplication
|
|
7
8
|
from universal_mcp.integrations import Integration
|
|
8
9
|
|
|
9
10
|
|
|
@@ -23,7 +24,6 @@ class LinkedinApp(APIApplication):
|
|
|
23
24
|
`{"headers": {"x-api-key": "YOUR_API_KEY"}}`.
|
|
24
25
|
"""
|
|
25
26
|
super().__init__(name="linkedin", integration=integration)
|
|
26
|
-
|
|
27
27
|
self._base_url = None
|
|
28
28
|
self.account_id = None
|
|
29
29
|
if self.integration:
|
|
@@ -36,12 +36,8 @@ class LinkedinApp(APIApplication):
|
|
|
36
36
|
if not self._base_url:
|
|
37
37
|
unipile_dsn = os.getenv("UNIPILE_DSN")
|
|
38
38
|
if not unipile_dsn:
|
|
39
|
-
logger.error(
|
|
40
|
-
|
|
41
|
-
)
|
|
42
|
-
raise ValueError(
|
|
43
|
-
"UnipileApp: UNIPILE_DSN environment variable is required."
|
|
44
|
-
)
|
|
39
|
+
logger.error("UnipileApp: UNIPILE_DSN environment variable is not set.")
|
|
40
|
+
raise ValueError("UnipileApp: UNIPILE_DSN environment variable is required.")
|
|
45
41
|
self._base_url = f"https://{unipile_dsn}"
|
|
46
42
|
return self._base_url
|
|
47
43
|
|
|
@@ -56,31 +52,25 @@ class LinkedinApp(APIApplication):
|
|
|
56
52
|
Overrides the base class method to use X-Api-Key.
|
|
57
53
|
"""
|
|
58
54
|
if not self.integration:
|
|
59
|
-
logger.warning(
|
|
60
|
-
"UnipileApp: No integration configured, returning empty headers."
|
|
61
|
-
)
|
|
55
|
+
logger.warning("UnipileApp: No integration configured, returning empty headers.")
|
|
62
56
|
return {}
|
|
63
|
-
|
|
64
57
|
api_key = os.getenv("UNIPILE_API_KEY")
|
|
65
58
|
if not api_key:
|
|
66
|
-
logger.error(
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
return { # Or return minimal headers if some calls might not need auth (unlikely for Unipile)
|
|
70
|
-
"Content-Type": "application/json",
|
|
71
|
-
"Cache-Control": "no-cache",
|
|
72
|
-
}
|
|
73
|
-
|
|
59
|
+
logger.error("UnipileApp: API key not found in integration credentials for Unipile.")
|
|
60
|
+
return {"Content-Type": "application/json", "Cache-Control": "no-cache"}
|
|
74
61
|
logger.debug("UnipileApp: Using X-Api-Key for authentication.")
|
|
75
|
-
return {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
62
|
+
return {"x-api-key": api_key, "Content-Type": "application/json", "Cache-Control": "no-cache"}
|
|
63
|
+
|
|
64
|
+
async def _aget_headers(self) -> dict[str, str]:
|
|
65
|
+
"""
|
|
66
|
+
Get the headers for Unipile API requests asynchronously.
|
|
67
|
+
Overrides the base class method to use X-Api-Key.
|
|
68
|
+
"""
|
|
69
|
+
return self._get_headers()
|
|
80
70
|
|
|
81
|
-
def
|
|
71
|
+
async def _aget_search_parameter_id(self, param_type: str, keywords: str) -> str:
|
|
82
72
|
"""
|
|
83
|
-
Retrieves the ID for a given LinkedIn search parameter by its name.
|
|
73
|
+
Retrieves the ID for a given LinkedIn search parameter by its name asynchronously.
|
|
84
74
|
|
|
85
75
|
Args:
|
|
86
76
|
param_type: The type of parameter to search for (e.g., "LOCATION", "COMPANY").
|
|
@@ -94,29 +84,52 @@ class LinkedinApp(APIApplication):
|
|
|
94
84
|
httpx.HTTPError: If the API request fails.
|
|
95
85
|
"""
|
|
96
86
|
url = f"{self.base_url}/api/v1/linkedin/search/parameters"
|
|
97
|
-
params = {
|
|
98
|
-
|
|
99
|
-
"keywords": keywords,
|
|
100
|
-
"type": param_type,
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
response = self._get(url, params=params)
|
|
87
|
+
params = {"account_id": self.account_id, "keywords": keywords, "type": param_type}
|
|
88
|
+
response = await self._aget(url, params=params)
|
|
104
89
|
results = self._handle_response(response)
|
|
105
|
-
|
|
106
90
|
items = results.get("items", [])
|
|
107
91
|
if items:
|
|
108
|
-
# Return the ID of the first result, assuming it's the most relevant
|
|
109
92
|
return items[0]["id"]
|
|
110
|
-
|
|
111
93
|
raise ValueError(f'Could not find a matching ID for {param_type}: "{keywords}"')
|
|
112
94
|
|
|
113
|
-
def
|
|
95
|
+
async def start_new_chat(self, provider_id: str, text: str) -> dict[str, Any]:
|
|
96
|
+
"""
|
|
97
|
+
Starts a new chat conversation with a specified user by sending an initial message.
|
|
98
|
+
This function constructs a multipart/form-data request using the `files` parameter
|
|
99
|
+
to ensure correct formatting and headers, working around potential issues in the
|
|
100
|
+
underlying request method.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
provider_id: The LinkedIn provider ID of the user to start the chat with.
|
|
104
|
+
This is available in the response of the `retrieve_user_profile` tool.
|
|
105
|
+
text: The initial message content. For LinkedIn Recruiter accounts, this can include
|
|
106
|
+
HTML tags like <strong>, <em>, <a>, <ul>, <ol>, and <li>.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
A dictionary containing the details of the newly created chat.
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
httpx.HTTPError: If the API request fails.
|
|
113
|
+
|
|
114
|
+
Tags:
|
|
115
|
+
linkedin, chat, create, start, new, messaging, api, important
|
|
116
|
+
"""
|
|
117
|
+
url = f"{self.base_url}/api/v1/chats"
|
|
118
|
+
form_payload = {"account_id": (None, self.account_id), "text": (None, text), "attendees_ids": (None, provider_id)}
|
|
119
|
+
api_key = os.getenv("UNIPILE_API_KEY")
|
|
120
|
+
if not api_key:
|
|
121
|
+
raise ValueError("UNIPILE_API_KEY environment variable is not set.")
|
|
122
|
+
headers = {"x-api-key": api_key}
|
|
123
|
+
response = requests.post(url, files=form_payload, headers=headers)
|
|
124
|
+
return self._handle_response(response)
|
|
125
|
+
|
|
126
|
+
async def list_all_chats(
|
|
114
127
|
self,
|
|
115
128
|
unread: bool | None = None,
|
|
116
129
|
cursor: str | None = None,
|
|
117
|
-
before: str | None = None,
|
|
118
|
-
after: str | None = None,
|
|
119
|
-
limit: int | None = None,
|
|
130
|
+
before: str | None = None,
|
|
131
|
+
after: str | None = None,
|
|
132
|
+
limit: int | None = None,
|
|
120
133
|
account_type: str | None = None,
|
|
121
134
|
) -> dict[str, Any]:
|
|
122
135
|
"""
|
|
@@ -141,9 +154,7 @@ class LinkedinApp(APIApplication):
|
|
|
141
154
|
"""
|
|
142
155
|
url = f"{self.base_url}/api/v1/chats"
|
|
143
156
|
params: dict[str, Any] = {}
|
|
144
|
-
|
|
145
157
|
params["account_id"] = self.account_id
|
|
146
|
-
|
|
147
158
|
if unread is not None:
|
|
148
159
|
params["unread"] = unread
|
|
149
160
|
if cursor:
|
|
@@ -156,18 +167,16 @@ class LinkedinApp(APIApplication):
|
|
|
156
167
|
params["limit"] = limit
|
|
157
168
|
if account_type:
|
|
158
169
|
params["account_type"] = account_type
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
response = self._get(url, params=params)
|
|
162
|
-
return response.json()
|
|
170
|
+
response = await self._aget(url, params=params)
|
|
171
|
+
return self._handle_response(response)
|
|
163
172
|
|
|
164
|
-
def list_chat_messages(
|
|
173
|
+
async def list_chat_messages(
|
|
165
174
|
self,
|
|
166
175
|
chat_id: str,
|
|
167
176
|
cursor: str | None = None,
|
|
168
|
-
before: str | None = None,
|
|
169
|
-
after: str | None = None,
|
|
170
|
-
limit: int | None = None,
|
|
177
|
+
before: str | None = None,
|
|
178
|
+
after: str | None = None,
|
|
179
|
+
limit: int | None = None,
|
|
171
180
|
sender_id: str | None = None,
|
|
172
181
|
) -> dict[str, Any]:
|
|
173
182
|
"""
|
|
@@ -202,15 +211,10 @@ class LinkedinApp(APIApplication):
|
|
|
202
211
|
params["limit"] = limit
|
|
203
212
|
if sender_id:
|
|
204
213
|
params["sender_id"] = sender_id
|
|
214
|
+
response = await self._aget(url, params=params)
|
|
215
|
+
return self._handle_response(response)
|
|
205
216
|
|
|
206
|
-
|
|
207
|
-
return response.json()
|
|
208
|
-
|
|
209
|
-
def send_chat_message(
|
|
210
|
-
self,
|
|
211
|
-
chat_id: str,
|
|
212
|
-
text: str,
|
|
213
|
-
) -> dict[str, Any]:
|
|
217
|
+
async def send_chat_message(self, chat_id: str, text: str) -> dict[str, Any]:
|
|
214
218
|
"""
|
|
215
219
|
Sends a text message to a specific chat conversation using its `chat_id`. This function creates a new message via a POST request, distinguishing it from read-only functions like `list_chat_messages`. It returns the API's response, which typically confirms the successful creation of the message.
|
|
216
220
|
|
|
@@ -230,11 +234,10 @@ class LinkedinApp(APIApplication):
|
|
|
230
234
|
"""
|
|
231
235
|
url = f"{self.base_url}/api/v1/chats/{chat_id}/messages"
|
|
232
236
|
payload: dict[str, Any] = {"text": text}
|
|
237
|
+
response = await self._apost(url, data=payload)
|
|
238
|
+
return self._handle_response(response)
|
|
233
239
|
|
|
234
|
-
|
|
235
|
-
return response.json()
|
|
236
|
-
|
|
237
|
-
def retrieve_chat(self, chat_id: str) -> dict[str, Any]:
|
|
240
|
+
async def retrieve_chat(self, chat_id: str) -> dict[str, Any]:
|
|
238
241
|
"""
|
|
239
242
|
Retrieves a single chat's details using its Unipile or provider-specific ID. This function is distinct from `list_all_chats`, which returns a collection, by targeting one specific conversation.
|
|
240
243
|
|
|
@@ -254,16 +257,15 @@ class LinkedinApp(APIApplication):
|
|
|
254
257
|
params: dict[str, Any] = {}
|
|
255
258
|
if self.account_id:
|
|
256
259
|
params["account_id"] = self.account_id
|
|
260
|
+
response = await self._aget(url, params=params)
|
|
261
|
+
return self._handle_response(response)
|
|
257
262
|
|
|
258
|
-
|
|
259
|
-
return response.json()
|
|
260
|
-
|
|
261
|
-
def list_all_messages(
|
|
263
|
+
async def list_all_messages(
|
|
262
264
|
self,
|
|
263
265
|
cursor: str | None = None,
|
|
264
|
-
before: str | None = None,
|
|
265
|
-
after: str | None = None,
|
|
266
|
-
limit: int | None = None,
|
|
266
|
+
before: str | None = None,
|
|
267
|
+
after: str | None = None,
|
|
268
|
+
limit: int | None = None,
|
|
267
269
|
sender_id: str | None = None,
|
|
268
270
|
) -> dict[str, Any]:
|
|
269
271
|
"""
|
|
@@ -299,16 +301,11 @@ class LinkedinApp(APIApplication):
|
|
|
299
301
|
params["sender_id"] = sender_id
|
|
300
302
|
if self.account_id:
|
|
301
303
|
params["account_id"] = self.account_id
|
|
304
|
+
response = await self._aget(url, params=params)
|
|
305
|
+
return self._handle_response(response)
|
|
302
306
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
def list_profile_posts(
|
|
307
|
-
self,
|
|
308
|
-
identifier: str, # User or Company provider internal ID
|
|
309
|
-
cursor: str | None = None,
|
|
310
|
-
limit: int | None = None, # 1-100 (spec says max 250)
|
|
311
|
-
is_company: bool | None = None,
|
|
307
|
+
async def list_profile_posts(
|
|
308
|
+
self, identifier: str, cursor: str | None = None, limit: int | None = None, is_company: bool | None = None
|
|
312
309
|
) -> dict[str, Any]:
|
|
313
310
|
"""
|
|
314
311
|
Retrieves a paginated list of posts from a specific user or company profile using their provider ID. An authorizing `account_id` is required, and the `is_company` flag must specify the entity type, distinguishing this from `retrieve_post` which fetches a single post by its own ID.
|
|
@@ -336,11 +333,10 @@ class LinkedinApp(APIApplication):
|
|
|
336
333
|
params["limit"] = limit
|
|
337
334
|
if is_company is not None:
|
|
338
335
|
params["is_company"] = is_company
|
|
336
|
+
response = await self._aget(url, params=params)
|
|
337
|
+
return self._handle_response(response)
|
|
339
338
|
|
|
340
|
-
|
|
341
|
-
return response.json()
|
|
342
|
-
|
|
343
|
-
def retrieve_own_profile(self) -> dict[str, Any]:
|
|
339
|
+
async def retrieve_own_profile(self) -> dict[str, Any]:
|
|
344
340
|
"""
|
|
345
341
|
Retrieves the profile details for the user associated with the Unipile account. This function targets the API's 'me' endpoint to fetch the authenticated user's profile, distinct from `retrieve_user_profile` which fetches profiles of other users by their public identifier.
|
|
346
342
|
|
|
@@ -355,10 +351,10 @@ class LinkedinApp(APIApplication):
|
|
|
355
351
|
"""
|
|
356
352
|
url = f"{self.base_url}/api/v1/users/me"
|
|
357
353
|
params: dict[str, Any] = {"account_id": self.account_id}
|
|
358
|
-
response = self.
|
|
359
|
-
return
|
|
354
|
+
response = await self._aget(url, params=params)
|
|
355
|
+
return self._handle_response(response)
|
|
360
356
|
|
|
361
|
-
def retrieve_post(self, post_id: str) -> dict[str, Any]:
|
|
357
|
+
async def retrieve_post(self, post_id: str) -> dict[str, Any]:
|
|
362
358
|
"""
|
|
363
359
|
Fetches a specific post's details by its unique ID. Unlike `list_profile_posts`, which retrieves a collection of posts from a user or company profile, this function targets one specific post and returns its full object.
|
|
364
360
|
|
|
@@ -376,21 +372,17 @@ class LinkedinApp(APIApplication):
|
|
|
376
372
|
"""
|
|
377
373
|
url = f"{self.base_url}/api/v1/posts/{post_id}"
|
|
378
374
|
params: dict[str, Any] = {"account_id": self.account_id}
|
|
379
|
-
response = self.
|
|
380
|
-
return
|
|
375
|
+
response = await self._aget(url, params=params)
|
|
376
|
+
return self._handle_response(response)
|
|
381
377
|
|
|
382
|
-
def list_post_comments(
|
|
383
|
-
self,
|
|
384
|
-
post_id: str,
|
|
385
|
-
comment_id: str | None = None,
|
|
386
|
-
cursor: str | None = None,
|
|
387
|
-
limit: int | None = None,
|
|
378
|
+
async def list_post_comments(
|
|
379
|
+
self, post_id: str, comment_id: str | None = None, cursor: str | None = None, limit: int | None = None
|
|
388
380
|
) -> dict[str, Any]:
|
|
389
381
|
"""
|
|
390
|
-
Fetches comments for a specific post. Providing an optional `comment_id` retrieves threaded replies instead of top-level comments.
|
|
382
|
+
Fetches comments for a specific post. Providing an optional `comment_id` retrieves threaded replies instead of top-level comments. `retrieve_post` or `list_profile_posts` can be used to obtain the `post_id` which is the social_id in their response.
|
|
391
383
|
|
|
392
384
|
Args:
|
|
393
|
-
post_id: The social ID of the post.
|
|
385
|
+
post_id: The social ID of the post which you get from using `retrieve_post` or `list_profile_posts` tools.
|
|
394
386
|
comment_id: If provided, retrieves replies to this comment ID instead of top-level comments.
|
|
395
387
|
cursor: Pagination cursor.
|
|
396
388
|
limit: Number of comments to return. (OpenAPI spec shows type string, passed as string if provided).
|
|
@@ -412,15 +404,11 @@ class LinkedinApp(APIApplication):
|
|
|
412
404
|
params["limit"] = str(limit)
|
|
413
405
|
if comment_id:
|
|
414
406
|
params["comment_id"] = comment_id
|
|
407
|
+
response = await self._aget(url, params=params)
|
|
408
|
+
return self._handle_response(response)
|
|
415
409
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
def create_post(
|
|
420
|
-
self,
|
|
421
|
-
text: str,
|
|
422
|
-
mentions: list[dict[str, Any]] | None = None,
|
|
423
|
-
external_link: str | None = None,
|
|
410
|
+
async def create_post(
|
|
411
|
+
self, text: str, mentions: list[dict[str, Any]] | None = None, external_link: str | None = None
|
|
424
412
|
) -> dict[str, Any]:
|
|
425
413
|
"""
|
|
426
414
|
Publishes a new top-level post from the account, including text, user mentions, and an external link. This function creates original content, distinguishing it from `create_post_comment` which adds replies to existing posts.
|
|
@@ -441,26 +429,16 @@ class LinkedinApp(APIApplication):
|
|
|
441
429
|
linkedin, post, create, share, content, api, important
|
|
442
430
|
"""
|
|
443
431
|
url = f"{self.base_url}/api/v1/posts"
|
|
444
|
-
|
|
445
|
-
params: dict[str, str] = {
|
|
446
|
-
"account_id": self.account_id,
|
|
447
|
-
"text": text,
|
|
448
|
-
}
|
|
449
|
-
|
|
432
|
+
params: dict[str, str] = {"account_id": self.account_id, "text": text}
|
|
450
433
|
if mentions:
|
|
451
434
|
params["mentions"] = mentions
|
|
452
435
|
if external_link:
|
|
453
436
|
params["external_link"] = external_link
|
|
437
|
+
response = await self._apost(url, data=params)
|
|
438
|
+
return self._handle_response(response)
|
|
454
439
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
def list_content_reactions(
|
|
459
|
-
self,
|
|
460
|
-
post_id: str,
|
|
461
|
-
comment_id: str | None = None,
|
|
462
|
-
cursor: str | None = None,
|
|
463
|
-
limit: int | None = None,
|
|
440
|
+
async def list_content_reactions(
|
|
441
|
+
self, post_id: str, comment_id: str | None = None, cursor: str | None = None, limit: int | None = None
|
|
464
442
|
) -> dict[str, Any]:
|
|
465
443
|
"""
|
|
466
444
|
Retrieves a paginated list of reactions for a given post or, optionally, a specific comment. This read-only operation uses the account for the request, distinguishing it from the `create_reaction` function which adds new reactions.
|
|
@@ -488,16 +466,11 @@ class LinkedinApp(APIApplication):
|
|
|
488
466
|
params["limit"] = limit
|
|
489
467
|
if comment_id:
|
|
490
468
|
params["comment_id"] = comment_id
|
|
469
|
+
response = await self._aget(url, params=params)
|
|
470
|
+
return self._handle_response(response)
|
|
491
471
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
def create_post_comment(
|
|
496
|
-
self,
|
|
497
|
-
post_social_id: str,
|
|
498
|
-
text: str,
|
|
499
|
-
comment_id: str | None = None, # If provided, replies to a specific comment
|
|
500
|
-
mentions_body: list[dict[str, Any]] | None = None,
|
|
472
|
+
async def create_post_comment(
|
|
473
|
+
self, post_social_id: str, text: str, comment_id: str | None = None, mentions_body: list[dict[str, Any]] | None = None
|
|
501
474
|
) -> dict[str, Any]:
|
|
502
475
|
"""
|
|
503
476
|
Publishes a comment on a specified post. By providing an optional `comment_id`, it creates a threaded reply to an existing comment instead of a new top-level one. This function's dual capability distinguishes it from `list_post_comments`, which only retrieves comments and their replies.
|
|
@@ -519,33 +492,18 @@ class LinkedinApp(APIApplication):
|
|
|
519
492
|
linkedin, post, comment, create, content, api, important
|
|
520
493
|
"""
|
|
521
494
|
url = f"{self.base_url}/api/v1/posts/{post_social_id}/comments"
|
|
522
|
-
params: dict[str, Any] = {
|
|
523
|
-
"account_id": self.account_id,
|
|
524
|
-
"text": text,
|
|
525
|
-
}
|
|
526
|
-
|
|
495
|
+
params: dict[str, Any] = {"account_id": self.account_id, "text": text}
|
|
527
496
|
if comment_id:
|
|
528
497
|
params["comment_id"] = comment_id
|
|
529
|
-
|
|
530
498
|
if mentions_body:
|
|
531
499
|
params = {"mentions": mentions_body}
|
|
500
|
+
response = await self._apost(url, data=params)
|
|
501
|
+
return self._handle_response(response)
|
|
532
502
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
try:
|
|
536
|
-
return response.json()
|
|
537
|
-
except json.JSONDecodeError:
|
|
538
|
-
return {
|
|
539
|
-
"status": response.status_code,
|
|
540
|
-
"message": "Comment action processed.",
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
def create_reaction(
|
|
503
|
+
async def create_reaction(
|
|
544
504
|
self,
|
|
545
505
|
post_social_id: str,
|
|
546
|
-
reaction_type: Literal[
|
|
547
|
-
"like", "celebrate", "love", "insightful", "funny", "support"
|
|
548
|
-
],
|
|
506
|
+
reaction_type: Literal["like", "celebrate", "love", "insightful", "funny", "support"],
|
|
549
507
|
comment_id: str | None = None,
|
|
550
508
|
) -> dict[str, Any]:
|
|
551
509
|
"""
|
|
@@ -566,32 +524,18 @@ class LinkedinApp(APIApplication):
|
|
|
566
524
|
linkedin, post, reaction, create, like, content, api, important
|
|
567
525
|
"""
|
|
568
526
|
url = f"{self.base_url}/api/v1/posts/reaction"
|
|
569
|
-
|
|
570
|
-
params: dict[str, str] = {
|
|
571
|
-
"account_id": self.account_id,
|
|
572
|
-
"post_id": post_social_id,
|
|
573
|
-
"reaction_type": reaction_type,
|
|
574
|
-
}
|
|
575
|
-
|
|
527
|
+
params: dict[str, str] = {"account_id": self.account_id, "post_id": post_social_id, "reaction_type": reaction_type}
|
|
576
528
|
if comment_id:
|
|
577
529
|
params["comment_id"] = comment_id
|
|
530
|
+
response = await self._apost(url, data=params)
|
|
531
|
+
return self._handle_response(response)
|
|
578
532
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
try:
|
|
582
|
-
return response.json()
|
|
583
|
-
except json.JSONDecodeError:
|
|
584
|
-
return {
|
|
585
|
-
"status": response.status_code,
|
|
586
|
-
"message": "Reaction action processed.",
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
def retrieve_user_profile(self, identifier: str) -> dict[str, Any]:
|
|
533
|
+
async def retrieve_user_profile(self, public_identifier: str) -> dict[str, Any]:
|
|
590
534
|
"""
|
|
591
535
|
Retrieves a specific LinkedIn user's profile using their public or internal ID. Unlike `retrieve_own_profile`, which fetches the authenticated user's details, this function targets and returns data for any specified third-party user profile on the platform.
|
|
592
536
|
|
|
593
537
|
Args:
|
|
594
|
-
|
|
538
|
+
public_identifier: Extract this value from the response of `search_people` tool. The response contains a public_identifier field.For example, for https://www.linkedin.com/in/manojbajaj95/, the identifier is "manojbajaj95".
|
|
595
539
|
|
|
596
540
|
Returns:
|
|
597
541
|
A dictionary containing the user's profile details.
|
|
@@ -602,12 +546,12 @@ class LinkedinApp(APIApplication):
|
|
|
602
546
|
Tags:
|
|
603
547
|
linkedin, user, profile, retrieve, get, api, important
|
|
604
548
|
"""
|
|
605
|
-
url = f"{self.base_url}/api/v1/users/{
|
|
549
|
+
url = f"{self.base_url}/api/v1/users/{public_identifier}"
|
|
606
550
|
params: dict[str, Any] = {"account_id": self.account_id}
|
|
607
|
-
response = self.
|
|
551
|
+
response = await self._aget(url, params=params)
|
|
608
552
|
return self._handle_response(response)
|
|
609
553
|
|
|
610
|
-
def search_people(
|
|
554
|
+
async def search_people(
|
|
611
555
|
self,
|
|
612
556
|
cursor: str | None = None,
|
|
613
557
|
limit: int | None = None,
|
|
@@ -618,7 +562,7 @@ class LinkedinApp(APIApplication):
|
|
|
618
562
|
) -> dict[str, Any]:
|
|
619
563
|
"""
|
|
620
564
|
Searches for LinkedIn user profiles using keywords, with optional filters for location, industry, and company. This function specifically targets the 'people' category, distinguishing it from other search methods like `search_companies` or `search_jobs` that query different entity types through the same API endpoint.
|
|
621
|
-
|
|
565
|
+
|
|
622
566
|
Args:
|
|
623
567
|
cursor: Pagination cursor for the next page of entries.
|
|
624
568
|
limit: Number of items to return (up to 50 for Classic search).
|
|
@@ -626,42 +570,35 @@ class LinkedinApp(APIApplication):
|
|
|
626
570
|
location: The geographical location to filter people by (e.g., "United States").
|
|
627
571
|
industry: The industry to filter people by.(eg., "Information Technology and Services").
|
|
628
572
|
company: The company to filter people by.(e.g., "Google").
|
|
629
|
-
|
|
573
|
+
|
|
630
574
|
Returns:
|
|
631
575
|
A dictionary containing search results and pagination details.
|
|
632
|
-
|
|
576
|
+
|
|
633
577
|
Raises:
|
|
634
578
|
httpx.HTTPError: If the API request fails.
|
|
635
579
|
"""
|
|
636
580
|
url = f"{self.base_url}/api/v1/linkedin/search"
|
|
637
|
-
|
|
638
581
|
params: dict[str, Any] = {"account_id": self.account_id}
|
|
639
582
|
if cursor:
|
|
640
583
|
params["cursor"] = cursor
|
|
641
584
|
if limit is not None:
|
|
642
585
|
params["limit"] = limit
|
|
643
|
-
|
|
644
586
|
payload: dict[str, Any] = {"api": "classic", "category": "people"}
|
|
645
|
-
|
|
646
587
|
if keywords:
|
|
647
588
|
payload["keywords"] = keywords
|
|
648
|
-
|
|
649
589
|
if location:
|
|
650
|
-
location_id = self.
|
|
590
|
+
location_id = await self._aget_search_parameter_id("LOCATION", location)
|
|
651
591
|
payload["location"] = [location_id]
|
|
652
|
-
|
|
653
592
|
if industry:
|
|
654
|
-
industry_id = self.
|
|
593
|
+
industry_id = await self._aget_search_parameter_id("INDUSTRY", industry)
|
|
655
594
|
payload["industry"] = [industry_id]
|
|
656
|
-
|
|
657
595
|
if company:
|
|
658
|
-
company_id = self.
|
|
596
|
+
company_id = await self._aget_search_parameter_id("COMPANY", company)
|
|
659
597
|
payload["company"] = [company_id]
|
|
660
|
-
|
|
661
|
-
response = self._post(url, params=params, data=payload)
|
|
598
|
+
response = await self._apost(url, params=params, data=payload)
|
|
662
599
|
return self._handle_response(response)
|
|
663
600
|
|
|
664
|
-
def search_companies(
|
|
601
|
+
async def search_companies(
|
|
665
602
|
self,
|
|
666
603
|
cursor: str | None = None,
|
|
667
604
|
limit: int | None = None,
|
|
@@ -671,45 +608,39 @@ class LinkedinApp(APIApplication):
|
|
|
671
608
|
) -> dict[str, Any]:
|
|
672
609
|
"""
|
|
673
610
|
Performs a paginated search for companies on LinkedIn using keywords, with optional location and industry filters. Its specific 'companies' search category distinguishes it from other methods like `search_people` or `search_posts`, ensuring that only company profiles are returned.
|
|
674
|
-
|
|
611
|
+
|
|
675
612
|
Args:
|
|
676
613
|
cursor: Pagination cursor for the next page of entries.
|
|
677
614
|
limit: Number of items to return (up to 50 for Classic search).
|
|
678
615
|
keywords: Keywords to search for.
|
|
679
616
|
location: The geographical location to filter companies by (e.g., "United States").
|
|
680
617
|
industry: The industry to filter companies by.(e.g., "Information Technology and Services").
|
|
681
|
-
|
|
618
|
+
|
|
682
619
|
Returns:
|
|
683
620
|
A dictionary containing search results and pagination details.
|
|
684
|
-
|
|
621
|
+
|
|
685
622
|
Raises:
|
|
686
623
|
httpx.HTTPError: If the API request fails.
|
|
687
624
|
"""
|
|
688
625
|
url = f"{self.base_url}/api/v1/linkedin/search"
|
|
689
|
-
|
|
690
626
|
params: dict[str, Any] = {"account_id": self.account_id}
|
|
691
627
|
if cursor:
|
|
692
628
|
params["cursor"] = cursor
|
|
693
629
|
if limit is not None:
|
|
694
630
|
params["limit"] = limit
|
|
695
|
-
|
|
696
631
|
payload: dict[str, Any] = {"api": "classic", "category": "companies"}
|
|
697
|
-
|
|
698
632
|
if keywords:
|
|
699
633
|
payload["keywords"] = keywords
|
|
700
|
-
|
|
701
634
|
if location:
|
|
702
|
-
location_id = self.
|
|
635
|
+
location_id = await self._aget_search_parameter_id("LOCATION", location)
|
|
703
636
|
payload["location"] = [location_id]
|
|
704
|
-
|
|
705
637
|
if industry:
|
|
706
|
-
industry_id = self.
|
|
638
|
+
industry_id = await self._aget_search_parameter_id("INDUSTRY", industry)
|
|
707
639
|
payload["industry"] = [industry_id]
|
|
708
|
-
|
|
709
|
-
response = self._post(url, params=params, data=payload)
|
|
640
|
+
response = await self._apost(url, params=params, data=payload)
|
|
710
641
|
return self._handle_response(response)
|
|
711
642
|
|
|
712
|
-
def search_posts(
|
|
643
|
+
async def search_posts(
|
|
713
644
|
self,
|
|
714
645
|
cursor: str | None = None,
|
|
715
646
|
limit: int | None = None,
|
|
@@ -719,105 +650,272 @@ class LinkedinApp(APIApplication):
|
|
|
719
650
|
) -> dict[str, Any]:
|
|
720
651
|
"""
|
|
721
652
|
Performs a keyword-based search for LinkedIn posts, allowing filters for date and sorting by relevance. This function executes a general, platform-wide content search, distinguishing it from other search functions that target people, companies, or jobs, and from `list_profile_posts` which retrieves from a specific profile.
|
|
722
|
-
|
|
653
|
+
|
|
723
654
|
Args:
|
|
724
655
|
cursor: Pagination cursor for the next page of entries.
|
|
725
656
|
limit: Number of items to return (up to 50 for Classic search).
|
|
726
657
|
keywords: Keywords to search for.
|
|
727
658
|
date_posted: Filter by when the post was posted.
|
|
728
659
|
sort_by: How to sort the results.
|
|
729
|
-
|
|
660
|
+
|
|
730
661
|
Returns:
|
|
731
662
|
A dictionary containing search results and pagination details.
|
|
732
|
-
|
|
663
|
+
|
|
733
664
|
Raises:
|
|
734
665
|
httpx.HTTPError: If the API request fails.
|
|
735
666
|
"""
|
|
736
667
|
url = f"{self.base_url}/api/v1/linkedin/search"
|
|
737
|
-
|
|
738
668
|
params: dict[str, Any] = {"account_id": self.account_id}
|
|
739
669
|
if cursor:
|
|
740
670
|
params["cursor"] = cursor
|
|
741
671
|
if limit is not None:
|
|
742
672
|
params["limit"] = limit
|
|
743
|
-
|
|
744
673
|
payload: dict[str, Any] = {"api": "classic", "category": "posts"}
|
|
745
|
-
|
|
746
674
|
if keywords:
|
|
747
675
|
payload["keywords"] = keywords
|
|
748
676
|
if date_posted:
|
|
749
677
|
payload["date_posted"] = date_posted
|
|
750
678
|
if sort_by:
|
|
751
679
|
payload["sort_by"] = sort_by
|
|
752
|
-
|
|
753
|
-
response = self._post(url, params=params, data=payload)
|
|
680
|
+
response = await self._apost(url, params=params, data=payload)
|
|
754
681
|
return self._handle_response(response)
|
|
755
682
|
|
|
756
|
-
def search_jobs(
|
|
683
|
+
async def search_jobs(
|
|
757
684
|
self,
|
|
758
685
|
cursor: str | None = None,
|
|
759
686
|
limit: int | None = None,
|
|
760
687
|
keywords: str | None = None,
|
|
761
688
|
region: str | None = None,
|
|
762
689
|
sort_by: Literal["relevance", "date"] = "relevance",
|
|
763
|
-
minimum_salary_value:
|
|
690
|
+
minimum_salary_value: Literal[40, 60, 80, 100, 120, 140, 160, 180, 200] = 40,
|
|
764
691
|
industry: str | None = None,
|
|
765
692
|
) -> dict[str, Any]:
|
|
766
693
|
"""
|
|
767
694
|
Performs a LinkedIn search for jobs, filtering results by keywords, region, industry, and minimum salary. Unlike other search functions (`search_people`, `search_companies`), this method is specifically configured to query the 'jobs' category, providing a paginated list of relevant employment opportunities.
|
|
768
|
-
|
|
695
|
+
|
|
769
696
|
Args:
|
|
770
697
|
cursor: Pagination cursor for the next page of entries.
|
|
771
698
|
limit: Number of items to return (up to 50 for Classic search).
|
|
772
699
|
keywords: Keywords to search for.
|
|
773
700
|
region: The geographical region to filter jobs by (e.g., "United States").
|
|
774
701
|
sort_by: How to sort the results.(e.g., "relevance" or "date".)
|
|
775
|
-
minimum_salary_value: The minimum salary to filter for.
|
|
702
|
+
minimum_salary_value: The minimum salary to filter for. Allowed values are 40, 60, 80, 100, 120, 140, 160, 180, 200.
|
|
776
703
|
industry: The industry to filter jobs by.(e.g., "Software Development").
|
|
777
|
-
|
|
704
|
+
|
|
778
705
|
Returns:
|
|
779
706
|
A dictionary containing search results and pagination details.
|
|
780
|
-
|
|
707
|
+
|
|
781
708
|
Raises:
|
|
782
709
|
httpx.HTTPError: If the API request fails.
|
|
783
710
|
ValueError: If the specified location is not found.
|
|
784
711
|
"""
|
|
785
712
|
url = f"{self.base_url}/api/v1/linkedin/search"
|
|
786
|
-
|
|
787
713
|
params: dict[str, Any] = {"account_id": self.account_id}
|
|
788
714
|
if cursor:
|
|
789
715
|
params["cursor"] = cursor
|
|
790
716
|
if limit is not None:
|
|
791
717
|
params["limit"] = limit
|
|
792
|
-
|
|
793
718
|
payload: dict[str, Any] = {
|
|
794
719
|
"api": "classic",
|
|
795
720
|
"category": "jobs",
|
|
796
|
-
"minimum_salary": {
|
|
797
|
-
"currency": "USD",
|
|
798
|
-
"value": minimum_salary_value,
|
|
799
|
-
},
|
|
721
|
+
"minimum_salary": {"currency": "USD", "value": minimum_salary_value},
|
|
800
722
|
}
|
|
801
|
-
|
|
802
723
|
if keywords:
|
|
803
724
|
payload["keywords"] = keywords
|
|
804
725
|
if sort_by:
|
|
805
726
|
payload["sort_by"] = sort_by
|
|
806
|
-
|
|
807
|
-
# If location is provided, get its ID and add it to the payload
|
|
808
727
|
if region:
|
|
809
|
-
location_id = self.
|
|
728
|
+
location_id = await self._aget_search_parameter_id("LOCATION", region)
|
|
810
729
|
payload["region"] = location_id
|
|
811
|
-
|
|
812
730
|
if industry:
|
|
813
|
-
industry_id = self.
|
|
731
|
+
industry_id = await self._aget_search_parameter_id("INDUSTRY", industry)
|
|
814
732
|
payload["industry"] = [industry_id]
|
|
733
|
+
response = await self._apost(url, params=params, data=payload)
|
|
734
|
+
return self._handle_response(response)
|
|
735
|
+
|
|
736
|
+
async def send_invitation(self, provider_id: str, user_email: str | None = None, message: str | None = None) -> dict[str, Any]:
|
|
737
|
+
"""
|
|
738
|
+
Sends a connection invitation to a LinkedIn user specified by their provider ID. An optional message and the user's email can be included.
|
|
739
|
+
|
|
740
|
+
Args:
|
|
741
|
+
provider_id: The LinkedIn provider ID of the user to invite. This is available in response of `retrieve_user_profile` tool.
|
|
742
|
+
user_email: Optional. The email address of the user, which may be required by LinkedIn.
|
|
743
|
+
message: Optional. A personalized message to include with the invitation (max 300 characters).
|
|
744
|
+
|
|
745
|
+
Returns:
|
|
746
|
+
A dictionary confirming the invitation was sent.
|
|
747
|
+
|
|
748
|
+
Raises:
|
|
749
|
+
httpx.HTTPError: If the API request fails.
|
|
750
|
+
ValueError: If the message exceeds 300 characters.
|
|
751
|
+
|
|
752
|
+
Tags:
|
|
753
|
+
linkedin, user, invite, connect, contact, api, important
|
|
754
|
+
"""
|
|
755
|
+
url = f"{self.base_url}/api/v1/users/invite"
|
|
756
|
+
payload: dict[str, Any] = {"account_id": self.account_id, "provider_id": provider_id}
|
|
757
|
+
if user_email:
|
|
758
|
+
payload["user_email"] = user_email
|
|
759
|
+
if message:
|
|
760
|
+
if len(message) > 300:
|
|
761
|
+
raise ValueError("Message cannot exceed 300 characters.")
|
|
762
|
+
payload["message"] = message
|
|
763
|
+
response = await self._apost(url, data=payload)
|
|
764
|
+
return self._handle_response(response)
|
|
765
|
+
|
|
766
|
+
async def list_sent_invitations(self, cursor: str | None = None, limit: int | None = None) -> dict[str, Any]:
|
|
767
|
+
"""
|
|
768
|
+
Retrieves a paginated list of all sent connection invitations that are currently pending. This function allows for iterating through the history of outstanding connection requests made from the specified account.
|
|
769
|
+
|
|
770
|
+
Args:
|
|
771
|
+
cursor: A pagination cursor for retrieving the next page of entries.
|
|
772
|
+
limit: The number of items to return, ranging from 1 to 100. Defaults to 10 if not specified.
|
|
815
773
|
|
|
816
|
-
|
|
774
|
+
Returns:
|
|
775
|
+
A dictionary containing a list of sent invitation objects and pagination details.
|
|
776
|
+
|
|
777
|
+
Raises:
|
|
778
|
+
httpx.HTTPError: If the API request fails.
|
|
779
|
+
|
|
780
|
+
Tags:
|
|
781
|
+
linkedin, user, invite, sent, list, contacts, api
|
|
782
|
+
"""
|
|
783
|
+
url = f"{self.base_url}/api/v1/users/invite/sent"
|
|
784
|
+
params: dict[str, Any] = {"account_id": self.account_id}
|
|
785
|
+
if cursor:
|
|
786
|
+
params["cursor"] = cursor
|
|
787
|
+
if limit is not None:
|
|
788
|
+
params["limit"] = limit
|
|
789
|
+
response = await self._aget(url, params=params)
|
|
790
|
+
return self._handle_response(response)
|
|
791
|
+
|
|
792
|
+
async def list_received_invitations(self, cursor: str | None = None, limit: int | None = None) -> dict[str, Any]:
|
|
793
|
+
"""
|
|
794
|
+
Retrieves a paginated list of all received connection invitations. This function allows for reviewing and processing incoming connection requests to the specified account.
|
|
795
|
+
|
|
796
|
+
Args:
|
|
797
|
+
cursor: A pagination cursor for retrieving the next page of entries.
|
|
798
|
+
limit: The number of items to return, ranging from 1 to 100. Defaults to 10 if not specified.
|
|
799
|
+
|
|
800
|
+
Returns:
|
|
801
|
+
A dictionary containing a list of received invitation objects and pagination details.
|
|
802
|
+
|
|
803
|
+
Raises:
|
|
804
|
+
httpx.HTTPError: If the API request fails.
|
|
805
|
+
|
|
806
|
+
Tags:
|
|
807
|
+
linkedin, user, invite, received, list, contacts, api
|
|
808
|
+
"""
|
|
809
|
+
url = f"{self.base_url}/api/v1/users/invite/received"
|
|
810
|
+
params: dict[str, Any] = {"account_id": self.account_id}
|
|
811
|
+
if cursor:
|
|
812
|
+
params["cursor"] = cursor
|
|
813
|
+
if limit is not None:
|
|
814
|
+
params["limit"] = limit
|
|
815
|
+
response = await self._aget(url, params=params)
|
|
816
|
+
return self._handle_response(response)
|
|
817
|
+
|
|
818
|
+
async def handle_received_invitation(
|
|
819
|
+
self, invitation_id: str, action: Literal["accept", "decline"], shared_secret: str
|
|
820
|
+
) -> dict[str, Any]:
|
|
821
|
+
"""
|
|
822
|
+
Accepts or declines a received LinkedIn connection invitation using its ID and a required shared secret. This function performs a POST request to update the invitation's status, distinguishing it from read-only functions like `list_received_invitations`.
|
|
823
|
+
|
|
824
|
+
Args:
|
|
825
|
+
invitation_id: The ID of the invitation to handle.Get this ID from the 'list_received_invitations' tool.
|
|
826
|
+
action: The action to perform, either "accept" or "decline".
|
|
827
|
+
shared_secret: The token provided by LinkedIn, retrieved from the 'list_received_invitations' tool, which is mandatory for this action.
|
|
828
|
+
|
|
829
|
+
Returns:
|
|
830
|
+
A dictionary confirming the action was processed.
|
|
831
|
+
|
|
832
|
+
Raises:
|
|
833
|
+
httpx.HTTPError: If the API request fails.
|
|
834
|
+
|
|
835
|
+
Tags:
|
|
836
|
+
linkedin, user, invite, received, handle, accept, decline, api
|
|
837
|
+
"""
|
|
838
|
+
url = f"{self.base_url}/api/v1/users/invite/received/{invitation_id}"
|
|
839
|
+
payload: dict[str, Any] = {"provider": "LINKEDIN", "action": action, "shared_secret": shared_secret, "account_id": self.account_id}
|
|
840
|
+
response = await self._apost(url, data=payload)
|
|
841
|
+
return self._handle_response(response)
|
|
842
|
+
|
|
843
|
+
async def cancel_sent_invitation(self, invitation_id: str) -> dict[str, Any]:
|
|
844
|
+
"""
|
|
845
|
+
Cancels a sent LinkedIn connection invitation that is currently pending. This function performs a DELETE request to remove the invitation, withdrawing the connection request.
|
|
846
|
+
|
|
847
|
+
Args:
|
|
848
|
+
invitation_id: The unique ID of the invitation to cancel. This ID can be obtained from the 'list_sent_invitations' tool.
|
|
849
|
+
|
|
850
|
+
Returns:
|
|
851
|
+
A dictionary confirming the invitation was cancelled.
|
|
852
|
+
|
|
853
|
+
Raises:
|
|
854
|
+
httpx.HTTPError: If the API request fails.
|
|
855
|
+
|
|
856
|
+
Tags:
|
|
857
|
+
linkedin, user, invite, sent, cancel, delete, api
|
|
858
|
+
"""
|
|
859
|
+
url = f"{self.base_url}/api/v1/users/invite/sent/{invitation_id}"
|
|
860
|
+
params = {"account_id": self.account_id}
|
|
861
|
+
response = await self._adelete(url, params=params)
|
|
862
|
+
return self._handle_response(response)
|
|
863
|
+
|
|
864
|
+
async def list_followers(self, cursor: str | None = None, limit: int | None = None) -> dict[str, Any]:
|
|
865
|
+
"""
|
|
866
|
+
Retrieves a paginated list of all followers for the current user's account. This function is distinct from `list_following` as it shows who follows the user, not who the user follows.
|
|
867
|
+
|
|
868
|
+
Args:
|
|
869
|
+
cursor: A pagination cursor for retrieving the next page of entries.
|
|
870
|
+
limit: The number of items to return, ranging from 1 to 1000.
|
|
871
|
+
|
|
872
|
+
Returns:
|
|
873
|
+
A dictionary containing a list of follower objects and pagination details.
|
|
874
|
+
|
|
875
|
+
Raises:
|
|
876
|
+
httpx.HTTPError: If the API request fails.
|
|
877
|
+
|
|
878
|
+
Tags:
|
|
879
|
+
linkedin, user, followers, list, contacts, api
|
|
880
|
+
"""
|
|
881
|
+
url = f"{self.base_url}/api/v1/users/followers"
|
|
882
|
+
params: dict[str, Any] = {"account_id": self.account_id}
|
|
883
|
+
if cursor:
|
|
884
|
+
params["cursor"] = cursor
|
|
885
|
+
if limit is not None:
|
|
886
|
+
params["limit"] = limit
|
|
887
|
+
response = await self._aget(url, params=params)
|
|
888
|
+
return self._handle_response(response)
|
|
889
|
+
|
|
890
|
+
async def list_following(self, cursor: str | None = None, limit: int | None = None) -> dict[str, Any]:
|
|
891
|
+
"""
|
|
892
|
+
Retrieves a paginated list of all accounts that the current user is following. This function is the counterpart to `list_followers`, focusing on the user's outgoing connections rather than incoming ones.
|
|
893
|
+
|
|
894
|
+
Args:
|
|
895
|
+
cursor: A pagination cursor for retrieving the next page of entries.
|
|
896
|
+
limit: The number of items to return, ranging from 1 to 1000.
|
|
897
|
+
|
|
898
|
+
Returns:
|
|
899
|
+
A dictionary containing a list of followed account objects and pagination details.
|
|
900
|
+
|
|
901
|
+
Raises:
|
|
902
|
+
httpx.HTTPError: If the API request fails.
|
|
903
|
+
|
|
904
|
+
Tags:
|
|
905
|
+
linkedin, user, following, list, contacts, api
|
|
906
|
+
"""
|
|
907
|
+
url = f"{self.base_url}/api/v1/users/following"
|
|
908
|
+
params: dict[str, Any] = {"account_id": self.account_id}
|
|
909
|
+
if cursor:
|
|
910
|
+
params["cursor"] = cursor
|
|
911
|
+
if limit is not None:
|
|
912
|
+
params["limit"] = limit
|
|
913
|
+
response = self._get(url, params=params)
|
|
817
914
|
return self._handle_response(response)
|
|
818
915
|
|
|
819
916
|
def list_tools(self) -> list[Callable]:
|
|
820
917
|
return [
|
|
918
|
+
self.start_new_chat,
|
|
821
919
|
self.list_all_chats,
|
|
822
920
|
self.list_chat_messages,
|
|
823
921
|
self.send_chat_message,
|
|
@@ -836,4 +934,11 @@ class LinkedinApp(APIApplication):
|
|
|
836
934
|
self.search_jobs,
|
|
837
935
|
self.search_people,
|
|
838
936
|
self.search_posts,
|
|
937
|
+
self.send_invitation,
|
|
938
|
+
self.list_sent_invitations,
|
|
939
|
+
self.cancel_sent_invitation,
|
|
940
|
+
self.list_received_invitations,
|
|
941
|
+
self.handle_received_invitation,
|
|
942
|
+
self.list_followers,
|
|
943
|
+
# self.list_following this endpoint is not yet implemented by unipile
|
|
839
944
|
]
|