Skip to content

Commit e430f50

Browse files
Add sync & poll inbox
1 parent f022d0c commit e430f50

13 files changed

Lines changed: 288 additions & 8 deletions

File tree

linkedapi/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
NvSearchPeople,
5050
NvSendMessage,
5151
NvSyncConversation,
52+
NvSyncInbox,
5253
ReactToPost,
5354
RemoveConnection,
5455
RetrieveConnections,
@@ -61,13 +62,14 @@
6162
SendConnectionRequest,
6263
SendMessage,
6364
SyncConversation,
65+
SyncInbox,
6466
WithdrawConnectionRequest,
6567
)
6668
from linkedapi.types import * # noqa: F403
6769
from linkedapi.types import __all__ as _types_all
6870
from linkedapi.webhooks import parse_webhook_event
6971

70-
__version__ = "1.1.0"
72+
__version__ = "1.1.1"
7173
PredefinedOperation = Operation
7274

7375
__all__ = [
@@ -112,6 +114,7 @@
112114
"NvSearchPeople",
113115
"NvSendMessage",
114116
"NvSyncConversation",
117+
"NvSyncInbox",
115118
"Operation",
116119
"PredefinedOperation",
117120
"ReactToPost",
@@ -128,6 +131,7 @@
128131
"SendMessage",
129132
"SimpleWorkflowMapper",
130133
"SyncConversation",
134+
"SyncInbox",
131135
"ThenWorkflowMapper",
132136
"VoidWorkflowMapper",
133137
"WithdrawConnectionRequest",

linkedapi/client.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
NvSearchPeople,
2323
NvSendMessage,
2424
NvSyncConversation,
25+
NvSyncInbox,
2526
ReactToPost,
2627
RemoveConnection,
2728
RetrieveConnections,
@@ -34,10 +35,16 @@
3435
SendConnectionRequest,
3536
SendMessage,
3637
SyncConversation,
38+
SyncInbox,
3739
WithdrawConnectionRequest,
3840
)
3941
from linkedapi.types import AccountInfo, LinkedApiActionError, serialize_value
40-
from linkedapi.types.message import ConversationPollRequest, ConversationPollResult
42+
from linkedapi.types.message import (
43+
ConversationPollRequest,
44+
ConversationPollResult,
45+
InboxMessage,
46+
InboxPollRequest,
47+
)
4148
from linkedapi.types.statistics import ApiUsageAction, ApiUsageParams
4249

4350

@@ -54,6 +61,7 @@ def __init__(self, config: LinkedApiConfig | HttpClient[Any]) -> None:
5461
self.custom_workflow = CustomWorkflow(self.http_client)
5562
self.send_message = SendMessage(self.http_client)
5663
self.sync_conversation = SyncConversation(self.http_client)
64+
self.sync_inbox = SyncInbox(self.http_client)
5765
self.check_connection_status = CheckConnectionStatus(self.http_client)
5866
self.send_connection_request = SendConnectionRequest(self.http_client)
5967
self.withdraw_connection_request = WithdrawConnectionRequest(self.http_client)
@@ -74,6 +82,7 @@ def __init__(self, config: LinkedApiConfig | HttpClient[Any]) -> None:
7482
self.retrieve_performance = RetrievePerformance(self.http_client)
7583
self.nv_send_message = NvSendMessage(self.http_client)
7684
self.nv_sync_conversation = NvSyncConversation(self.http_client)
85+
self.nv_sync_inbox = NvSyncInbox(self.http_client)
7786
self.nv_search_companies = NvSearchCompanies(self.http_client)
7887
self.nv_search_people = NvSearchPeople(self.http_client)
7988
self.nv_fetch_company = NvFetchCompany(self.http_client)
@@ -83,6 +92,7 @@ def __init__(self, config: LinkedApiConfig | HttpClient[Any]) -> None:
8392
self.custom_workflow,
8493
self.send_message,
8594
self.sync_conversation,
95+
self.sync_inbox,
8696
self.check_connection_status,
8797
self.send_connection_request,
8898
self.withdraw_connection_request,
@@ -103,6 +113,7 @@ def __init__(self, config: LinkedApiConfig | HttpClient[Any]) -> None:
103113
self.retrieve_performance,
104114
self.nv_send_message,
105115
self.nv_sync_conversation,
116+
self.nv_sync_inbox,
106117
self.nv_search_companies,
107118
self.nv_search_people,
108119
self.nv_fetch_company,
@@ -139,6 +150,30 @@ def poll_conversations(
139150
)
140151
raise
141152

153+
def poll_inbox(
154+
self,
155+
request: InboxPollRequest | None = None,
156+
) -> MappedResponse[list[InboxMessage]]:
157+
"""Read the monitored standard or Sales Navigator inbox, newest messages first."""
158+
159+
payload = serialize_value(request) if request is not None else {}
160+
response = self.http_client.post("/inbox/poll", payload)
161+
if response.success and response.result is not None:
162+
messages = response.result.get("messages", [])
163+
return MappedResponse(
164+
data=[InboxMessage.model_validate(item) for item in messages],
165+
errors=[],
166+
)
167+
return MappedResponse(
168+
data=None,
169+
errors=[
170+
LinkedApiActionError(
171+
type=response.error.type if response.error else "",
172+
message=response.error.message if response.error else "",
173+
),
174+
],
175+
)
176+
142177
def get_account_info(self) -> MappedResponse[AccountInfo]:
143178
"""Retrieve basic information about the current LinkedIn account."""
144179

linkedapi/operations/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from linkedapi.operations.nv_search_people import NvSearchPeople
1313
from linkedapi.operations.nv_send_message import NvSendMessage
1414
from linkedapi.operations.nv_sync_conversation import NvSyncConversation
15+
from linkedapi.operations.nv_sync_inbox import NvSyncInbox
1516
from linkedapi.operations.react_to_post import ReactToPost
1617
from linkedapi.operations.remove_connection import RemoveConnection
1718
from linkedapi.operations.retrieve_connections import RetrieveConnections
@@ -24,6 +25,7 @@
2425
from linkedapi.operations.send_connection_request import SendConnectionRequest
2526
from linkedapi.operations.send_message import SendMessage
2627
from linkedapi.operations.sync_conversation import SyncConversation
28+
from linkedapi.operations.sync_inbox import SyncInbox
2729
from linkedapi.operations.withdraw_connection_request import WithdrawConnectionRequest
2830

2931
__all__ = [
@@ -47,6 +49,7 @@
4749
"NvSearchPeople",
4850
"NvSendMessage",
4951
"NvSyncConversation",
52+
"NvSyncInbox",
5053
"ReactToPost",
5154
"RemoveConnection",
5255
"RetrieveConnections",
@@ -59,5 +62,6 @@
5962
"SendConnectionRequest",
6063
"SendMessage",
6164
"SyncConversation",
65+
"SyncInbox",
6266
"WithdrawConnectionRequest",
6367
]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from __future__ import annotations
2+
3+
from linkedapi.core import Operation
4+
from linkedapi.mappers import VoidWorkflowMapper
5+
from linkedapi.types import NvSyncInboxParams
6+
7+
8+
class NvSyncInbox(Operation[NvSyncInboxParams, None]):
9+
"""Enable whole-inbox monitoring for Sales Navigator conversations."""
10+
11+
operation_name = "nvSyncInbox"
12+
mapper = VoidWorkflowMapper[NvSyncInboxParams]("nv.syncInbox")

linkedapi/operations/sync_inbox.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from __future__ import annotations
2+
3+
from linkedapi.core import Operation
4+
from linkedapi.mappers import VoidWorkflowMapper
5+
from linkedapi.types import SyncInboxParams
6+
7+
8+
class SyncInbox(Operation[SyncInboxParams, None]):
9+
"""Enable whole-inbox monitoring for standard LinkedIn conversations."""
10+
11+
operation_name = "syncInbox"
12+
mapper = VoidWorkflowMapper[SyncInboxParams]("st.syncInbox")

linkedapi/types/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,16 @@
101101
ConversationPollRequest,
102102
ConversationPollResult,
103103
ConversationType,
104+
InboxMessage,
105+
InboxPollRequest,
104106
Message,
105107
MessageSender,
106108
NvSendMessageParams,
107109
NvSyncConversationParams,
110+
NvSyncInboxParams,
108111
SendMessageParams,
109112
SyncConversationParams,
113+
SyncInboxParams,
110114
)
111115
from linkedapi.types.params import BaseActionParams, LimitParams, LimitSinceParams
112116
from linkedapi.types.person import (
@@ -183,6 +187,8 @@
183187
AccountWebhookEventData,
184188
AccountWebhookStatus,
185189
DeleteWebhookParams,
190+
MessageWebhookEvent,
191+
MessageWebhookEventData,
186192
ReplayWebhookDeliveryParams,
187193
SetWebhookParams,
188194
SetWebhookPayloadModeParams,
@@ -269,6 +275,8 @@
269275
"GetConnectionSessionParams",
270276
"GetLimitsParams",
271277
"GetLimitsUsageParams",
278+
"InboxMessage",
279+
"InboxPollRequest",
272280
"Job",
273281
"JobCurrency",
274282
"JobDatePosted",
@@ -293,6 +301,8 @@
293301
"MaxAnnualRevenue",
294302
"Message",
295303
"MessageSender",
304+
"MessageWebhookEvent",
305+
"MessageWebhookEventData",
296306
"MinAnnualRevenue",
297307
"NvBaseFetchCompanyParams",
298308
"NvBaseFetchCompanyParamsWide",
@@ -313,6 +323,7 @@
313323
"NvSearchPeopleResult",
314324
"NvSendMessageParams",
315325
"NvSyncConversationParams",
326+
"NvSyncInboxParams",
316327
"PendingConnectionSession",
317328
"Person",
318329
"PersonEducation",
@@ -377,6 +388,7 @@
377388
"SubscriptionStatus",
378389
"SubscriptionStatusValue",
379390
"SyncConversationParams",
391+
"SyncInboxParams",
380392
"WebhookDelivery",
381393
"WebhookDeliveryStatus",
382394
"WebhookEvent",

linkedapi/types/message.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,34 @@
1010

1111

1212
class SendMessageParams(BaseActionParams):
13-
person_url: str
1413
text: str
14+
person_url: str | None = None
15+
thread_id: str | None = None
1516

1617

1718
class SyncConversationParams(BaseActionParams):
1819
person_url: str
1920

2021

22+
class SyncInboxParams(BaseActionParams):
23+
pass
24+
25+
2126
class NvSendMessageParams(BaseActionParams):
22-
person_url: str
2327
text: str
24-
subject: str
28+
person_url: str | None = None
29+
subject: str | None = None
30+
thread_id: str | None = None
2531

2632

2733
class NvSyncConversationParams(BaseActionParams):
2834
person_url: str
2935

3036

37+
class NvSyncInboxParams(BaseActionParams):
38+
pass
39+
40+
3141
class ConversationPollRequest(LinkedApiModel):
3242
person_url: str
3343
type: ConversationType
@@ -39,10 +49,27 @@ class Message(LinkedApiModel):
3949
sender: MessageSender | None = None
4050
text: str | None = None
4151
time: str | None = None
52+
thread_id: str | None = None
4253

4354

4455
class ConversationPollResult(LinkedApiModel):
4556
person_url: str | None = None
4657
type: ConversationType | None = None
4758
messages: list[Message] | None = None
4859
since: str | None = None
60+
61+
62+
class InboxPollRequest(LinkedApiModel):
63+
since: str | None = None
64+
type: ConversationType | None = None
65+
thread_id: str | None = None
66+
67+
68+
class InboxMessage(LinkedApiModel):
69+
id: str | None = None
70+
type: ConversationType | None = None
71+
thread_id: str | None = None
72+
person_url: str | None = None
73+
sender: MessageSender | None = None
74+
text: str | None = None
75+
time: str | None = None

linkedapi/types/webhooks.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Any, Literal
44

55
from linkedapi.types.base import LinkedApiModel
6+
from linkedapi.types.message import ConversationType, MessageSender
67

78
WebhookPayloadMode = Literal["thin", "fat"]
89
WebhookEventType = Literal[
@@ -13,6 +14,8 @@
1314
"account.reconnectionRequired",
1415
"account.frozen",
1516
"account.deleted",
17+
"linkedin.messageReceived",
18+
"linkedin.messageSent",
1619
"webhook.test",
1720
]
1821
WebhookDeliveryStatus = Literal["pending", "delivering", "success", "failed"]
@@ -91,6 +94,24 @@ class AccountWebhookEvent(LinkedApiModel):
9194
data: AccountWebhookEventData
9295

9396

97+
class MessageWebhookEventData(LinkedApiModel):
98+
account_id: str | None = None
99+
type: ConversationType | None = None
100+
thread_id: str | None = None
101+
person_url: str | None = None
102+
message_id: str | None = None
103+
sender: MessageSender | None = None
104+
text: str | None = None
105+
time: str | None = None
106+
107+
108+
class MessageWebhookEvent(LinkedApiModel):
109+
id: str
110+
type: Literal["linkedin.messageReceived", "linkedin.messageSent"]
111+
created_at: str | None = None
112+
data: MessageWebhookEventData
113+
114+
94115
class WebhookTestEventData(LinkedApiModel):
95116
message: str | None = None
96117

@@ -102,4 +123,6 @@ class WebhookTestEvent(LinkedApiModel):
102123
data: WebhookTestEventData
103124

104125

105-
WebhookEvent = WorkflowWebhookEvent | AccountWebhookEvent | WebhookTestEvent
126+
WebhookEvent = (
127+
WorkflowWebhookEvent | AccountWebhookEvent | MessageWebhookEvent | WebhookTestEvent
128+
)

linkedapi/webhooks/parse.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from linkedapi.types.webhooks import (
66
AccountWebhookEvent,
7+
MessageWebhookEvent,
78
WebhookEvent,
89
WebhookTestEvent,
910
WorkflowWebhookEvent,
@@ -41,6 +42,8 @@ def parse_webhook_event(raw_body: str | bytes) -> WebhookEvent:
4142
return WorkflowWebhookEvent.model_validate(parsed)
4243
if event_type.startswith("account."):
4344
return AccountWebhookEvent.model_validate(parsed)
45+
if event_type.startswith("linkedin."):
46+
return MessageWebhookEvent.model_validate(parsed)
4447
if event_type == "webhook.test":
4548
return WebhookTestEvent.model_validate(parsed)
4649

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "linkedapi"
7-
version = "1.1.0"
7+
version = "1.1.1"
88
description = "Official synchronous Python SDK for Linked API."
99
readme = "README.md"
1010
requires-python = ">=3.10"

0 commit comments

Comments
 (0)