Skip to content

Commit aeb8000

Browse files
add comment interaction API
1 parent 5dc7f01 commit aeb8000

10 files changed

Lines changed: 163 additions & 9 deletions

File tree

examples/post-actions.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ async function postActionsExample(): Promise<void> {
1313
await createCompanyPost(linkedapi);
1414
await reactToPost(linkedapi);
1515
await commentOnPost(linkedapi);
16+
await reactToComment(linkedapi);
17+
await replyToComment(linkedapi);
1618
} catch (error) {
1719
if (error instanceof LinkedApiError) {
1820
console.error('🚨 Linked API Error:', error.message);
@@ -127,6 +129,46 @@ async function commentOnPost(linkedapi: LinkedApi): Promise<void> {
127129
console.error('🚨 Errors:', JSON.stringify(commentResult.errors, null, 2));
128130
} else {
129131
console.log('✅ Comment added successfully');
132+
console.log('🔗 Comment URN:', commentResult.data?.commentUrn);
133+
console.log('🔗 Comment URL:', commentResult.data?.commentUrl);
134+
}
135+
}
136+
137+
async function reactToComment(linkedapi: LinkedApi): Promise<void> {
138+
console.log('\n👍 Reacting to a comment...');
139+
140+
const workflow = await linkedapi.reactToComment.execute({
141+
commentUrl:
142+
'https://www.linkedin.com/feed/update/urn:li:activity:123/?dashCommentUrn=urn:li:fsd_comment:(456,urn:li:activity:123)',
143+
type: 'like',
144+
});
145+
console.log('👍 React to comment workflow started:', workflow.workflowId);
146+
147+
const reactionResult = await linkedapi.reactToComment.result(workflow.workflowId);
148+
if (reactionResult.errors.length > 0) {
149+
console.error('🚨 Errors:', JSON.stringify(reactionResult.errors, null, 2));
150+
} else {
151+
console.log('✅ Comment reaction added successfully');
152+
}
153+
}
154+
155+
async function replyToComment(linkedapi: LinkedApi): Promise<void> {
156+
console.log('\n💬 Replying to a comment...');
157+
158+
const workflow = await linkedapi.replyToComment.execute({
159+
commentUrl:
160+
'https://www.linkedin.com/feed/update/urn:li:activity:123/?dashCommentUrn=urn:li:fsd_comment:(456,urn:li:activity:123)',
161+
text: 'Totally agree — thanks for adding this!',
162+
});
163+
console.log('💬 Reply to comment workflow started:', workflow.workflowId);
164+
165+
const replyResult = await linkedapi.replyToComment.result(workflow.workflowId);
166+
if (replyResult.errors.length > 0) {
167+
console.error('🚨 Errors:', JSON.stringify(replyResult.errors, null, 2));
168+
} else {
169+
console.log('✅ Reply posted successfully');
170+
console.log('🔗 Reply URN:', replyResult.data?.commentUrn);
171+
console.log('🔗 Reply URL:', replyResult.data?.commentUrl);
130172
}
131173
}
132174

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@linkedapi/node",
3-
"version": "2.3.1",
3+
"version": "2.4.0",
44
"description": "Control your LinkedIn accounts and retrieve real-time data, all through this Node.js SDK.",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

src/core/operation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export const OPERATION_NAME = {
3838
fetchJob: 'fetchJob',
3939
reactToPost: 'reactToPost',
4040
commentOnPost: 'commentOnPost',
41+
reactToComment: 'reactToComment',
42+
replyToComment: 'replyToComment',
4143
createPost: 'createPost',
4244
retrieveSSI: 'retrieveSSI',
4345
retrievePerformance: 'retrievePerformance',

src/index.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import {
2121
NvSendMessage,
2222
NvSyncConversation,
2323
NvSyncInbox,
24+
ReactToComment,
2425
ReactToPost,
2526
RemoveConnection,
27+
ReplyToComment,
2628
RetrieveConnectionRequests,
2729
RetrieveConnections,
2830
RetrievePendingRequests,
@@ -125,6 +127,8 @@ class LinkedApi {
125127
this.fetchJob = new FetchJob(this.httpClient);
126128
this.reactToPost = new ReactToPost(this.httpClient);
127129
this.commentOnPost = new CommentOnPost(this.httpClient);
130+
this.reactToComment = new ReactToComment(this.httpClient);
131+
this.replyToComment = new ReplyToComment(this.httpClient);
128132
this.createPost = new CreatePost(this.httpClient);
129133
this.retrieveSSI = new RetrieveSSI(this.httpClient);
130134
this.retrievePerformance = new RetrievePerformance(this.httpClient);
@@ -162,6 +166,8 @@ class LinkedApi {
162166
this.fetchJob,
163167
this.reactToPost,
164168
this.commentOnPost,
169+
this.reactToComment,
170+
this.replyToComment,
165171
this.createPost,
166172
this.retrieveSSI,
167173
this.retrievePerformance,
@@ -1299,12 +1305,63 @@ class LinkedApi {
12991305
* text: "Great insights! Thanks for sharing this valuable information."
13001306
* });
13011307
*
1302-
* await linkedapi.commentOnPost.result(workflow.workflowId);
1303-
* console.log("Comment posted successfully");
1308+
* const { data } = await linkedapi.commentOnPost.result(workflow.workflowId);
1309+
* console.log("Comment posted:", data?.commentUrn, data?.commentUrl);
13041310
* ```
13051311
*/
13061312
public commentOnPost: CommentOnPost;
13071313

1314+
/**
1315+
* React to a LinkedIn comment with an emoji reaction.
1316+
*
1317+
* This method adds a reaction (like, love, celebrate, support, funny, insightful) to a specific
1318+
* comment, identified by its comment URL (as returned by `commentOnPost` / `fetchPost` comments).
1319+
* The reaction type defaults to `like` when omitted.
1320+
*
1321+
* @param params - Parameters including the comment URL and optional reaction type
1322+
* @returns Promise resolving to the reaction action
1323+
*
1324+
* @see {@link https://linkedapi.io/docs/reacting-and-commenting/ Reacting and Commenting Documentation}
1325+
* @see {@link https://linkedapi.io/docs/action-st-react-to-comment/ st.reactToComment Action Documentation}
1326+
*
1327+
* @example
1328+
* ```typescript
1329+
* const workflow = await linkedapi.reactToComment.execute({
1330+
* commentUrl: "https://www.linkedin.com/feed/update/urn:li:activity:123/?dashCommentUrn=urn:li:fsd_comment:(456,urn:li:activity:123)",
1331+
* type: "like"
1332+
* });
1333+
*
1334+
* await linkedapi.reactToComment.result(workflow.workflowId);
1335+
* console.log("Comment reaction added successfully");
1336+
* ```
1337+
*/
1338+
public reactToComment: ReactToComment;
1339+
1340+
/**
1341+
* Reply to a LinkedIn comment.
1342+
*
1343+
* This method posts a text reply to a specific comment, identified by its comment URL. It returns
1344+
* the created reply's `commentUrn` and `commentUrl` so it can be tracked or acted on afterwards.
1345+
*
1346+
* @param params - Parameters including the comment URL and reply text
1347+
* @returns Promise resolving to the created reply's URN and URL
1348+
*
1349+
* @see {@link https://linkedapi.io/docs/reacting-and-commenting/ Reacting and Commenting Documentation}
1350+
* @see {@link https://linkedapi.io/docs/action-st-reply-to-comment/ st.replyToComment Action Documentation}
1351+
*
1352+
* @example
1353+
* ```typescript
1354+
* const workflow = await linkedapi.replyToComment.execute({
1355+
* commentUrl: "https://www.linkedin.com/feed/update/urn:li:activity:123/?dashCommentUrn=urn:li:fsd_comment:(456,urn:li:activity:123)",
1356+
* text: "Totally agree — thanks for adding this!"
1357+
* });
1358+
*
1359+
* const { data } = await linkedapi.replyToComment.result(workflow.workflowId);
1360+
* console.log("Reply posted:", data?.commentUrn, data?.commentUrl);
1361+
* ```
1362+
*/
1363+
public replyToComment: ReplyToComment;
1364+
13081365
/**
13091366
* Create a LinkedIn post on your personal profile or a company page.
13101367
*

src/operations/comment-on-post.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { Operation, TOperationName } from '../core';
2-
import { VoidWorkflowMapper } from '../mappers';
3-
import { TCommentOnPostParams } from '../types';
2+
import { SimpleWorkflowMapper } from '../mappers';
3+
import { TCommentOnPostParams, TCommentOnPostResult } from '../types';
44

5-
export class CommentOnPost extends Operation<TCommentOnPostParams, void> {
5+
export class CommentOnPost extends Operation<TCommentOnPostParams, TCommentOnPostResult> {
66
public override readonly operationName: TOperationName = 'commentOnPost';
7-
protected override readonly mapper = new VoidWorkflowMapper<TCommentOnPostParams>(
8-
'st.commentOnPost',
9-
);
7+
protected override readonly mapper = new SimpleWorkflowMapper<
8+
TCommentOnPostParams,
9+
TCommentOnPostResult
10+
>({
11+
actionType: 'st.commentOnPost',
12+
});
1013
}

src/operations/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export * from './retrieve-connections';
3030
export * from './remove-connection';
3131
export * from './react-to-post';
3232
export * from './comment-on-post';
33+
export * from './react-to-comment';
34+
export * from './reply-to-comment';
3335
export * from './create-post';
3436
export * from './retrieve-ssi';
3537
export * from './retrieve-performance';

src/operations/react-to-comment.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Operation, TOperationName } from '../core';
2+
import { VoidWorkflowMapper } from '../mappers';
3+
import { TReactToCommentParams } from '../types';
4+
5+
export class ReactToComment extends Operation<TReactToCommentParams, void> {
6+
public override readonly operationName: TOperationName = 'reactToComment';
7+
protected override readonly mapper = new VoidWorkflowMapper<TReactToCommentParams>(
8+
'st.reactToComment',
9+
);
10+
}

src/operations/reply-to-comment.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Operation, TOperationName } from '../core';
2+
import { SimpleWorkflowMapper } from '../mappers';
3+
import { TReplyToCommentParams, TReplyToCommentResult } from '../types';
4+
5+
export class ReplyToComment extends Operation<TReplyToCommentParams, TReplyToCommentResult> {
6+
public override readonly operationName: TOperationName = 'replyToComment';
7+
protected override readonly mapper = new SimpleWorkflowMapper<
8+
TReplyToCommentParams,
9+
TReplyToCommentResult
10+
>({
11+
actionType: 'st.replyToComment',
12+
});
13+
}

src/types/actions/post.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,34 @@ export interface TCommentOnPostParams extends TBaseActionParams {
102102
companyUrl?: string;
103103
}
104104

105+
export interface TCommentResult {
106+
commentUrn: string | null;
107+
commentUrl: string | null;
108+
}
109+
110+
export type TCommentOnPostResult = TCommentResult;
111+
112+
export interface TReactToCommentParams extends TBaseActionParams {
113+
commentUrl: string;
114+
type?: TReactionType;
115+
}
116+
117+
export interface TReplyToCommentParams extends TBaseActionParams {
118+
commentUrl: string;
119+
text: string;
120+
}
121+
122+
export type TReplyToCommentResult = TCommentResult;
123+
105124
export const POST_COMMENTER_TYPE = {
106125
person: 'person',
107126
company: 'company',
108127
} as const;
109128
export type TPostCommenterType = (typeof POST_COMMENTER_TYPE)[keyof typeof POST_COMMENTER_TYPE];
110129

111130
export interface TPostComment {
131+
commentUrn: string | null;
132+
commentUrl: string | null;
112133
commenterUrl: string;
113134
commenterName: string;
114135
commenterHeadline: string;

src/types/errors.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import { TOperationName } from '../core';
2222
* - postNotFound (fetchPost, reactToPost, commentOnPost)
2323
* - jobNotFound (fetchJob)
2424
* - commentingNotAllowed (commentOnPost)
25+
* - commentNotFound (openComment, reactToComment, replyToComment)
26+
* - replyingNotAllowed (replyToComment)
2527
* - noPostingPermission (createPost, reactToPost, commentOnPost)
2628
* - noSalesNavigator (nvSendMessage, nvSyncConversation, nvSyncInbox, nvSearchCompanies, nvSearchPeople, nvFetchCompany, nvFetchPerson, nvManageConversation)
2729
* - conversationsNotSynced (pollConversations)
@@ -45,6 +47,8 @@ export const LINKED_API_ACTION_ERROR = {
4547
postNotFound: 'postNotFound',
4648
jobNotFound: 'jobNotFound',
4749
commentingNotAllowed: 'commentingNotAllowed',
50+
commentNotFound: 'commentNotFound',
51+
replyingNotAllowed: 'replyingNotAllowed',
4852
noPostingPermission: 'noPostingPermission',
4953
noSalesNavigator: 'noSalesNavigator',
5054
conversationsNotSynced: 'conversationsNotSynced',

0 commit comments

Comments
 (0)