@meltwater/conversations-api-services 1.0.0
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.
- package/.drone.yml +13 -0
- package/.github/workflows/release.yml +42 -0
- package/.nvmrc +1 -0
- package/README.md +91 -0
- package/catalog-info.yaml +12 -0
- package/docs/index.md +28 -0
- package/mkdocs.yml +8 -0
- package/package.json +45 -0
- package/src/data-access/http/InstagramVideoClient.js +37 -0
- package/src/data-access/http/WarpZoneApi.client.js +39 -0
- package/src/data-access/http/amazonS3.js +41 -0
- package/src/data-access/http/asset-manager-tvm.client.js +32 -0
- package/src/data-access/http/companiesApi.client.js +35 -0
- package/src/data-access/http/credentialsApi.client.js +137 -0
- package/src/data-access/http/entitlementsApi.client.js +41 -0
- package/src/data-access/http/facebookApi.client.js +701 -0
- package/src/data-access/http/featureToggleApi.client.js +35 -0
- package/src/data-access/http/identityServices.client.js +117 -0
- package/src/data-access/http/instagramApi.client.js +496 -0
- package/src/data-access/http/ir.client.js +309 -0
- package/src/data-access/http/linkedInApi.client.js +624 -0
- package/src/data-access/http/masf.client.js +93 -0
- package/src/data-access/http/tiktokApi.client.js +477 -0
- package/src/data-access/index.js +33 -0
- package/src/lib/applicationTags.helpers.js +22 -0
- package/src/lib/configuration.js +10 -0
- package/src/lib/document-action-events.js +6 -0
- package/src/lib/externalId.helpers.js +15 -0
- package/src/lib/hiddenComment.helper.js +100 -0
- package/src/lib/logger.js +14 -0
- package/src/lib/metrics.helper.js +107 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import configuration from '../../lib/configuration.js';
|
|
3
|
+
import { CredentialsApiClient } from '../http/credentialsApi.client.js';
|
|
4
|
+
import logger from '../../lib/logger.js';
|
|
5
|
+
|
|
6
|
+
export class LinkedInApiClient {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.credentialsAPI = new CredentialsApiClient();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async likeMessage(document) {
|
|
12
|
+
const {
|
|
13
|
+
documentId,
|
|
14
|
+
systemData: {
|
|
15
|
+
connectionsCredential: credentialId,
|
|
16
|
+
policies: {
|
|
17
|
+
storage: { privateTo: companyId },
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
metaData: {
|
|
21
|
+
externalId,
|
|
22
|
+
source: { id: socialAccountId },
|
|
23
|
+
},
|
|
24
|
+
enrichments: {
|
|
25
|
+
socialScores: { li_liked_by_user: likedByUser },
|
|
26
|
+
},
|
|
27
|
+
} = document;
|
|
28
|
+
|
|
29
|
+
let response;
|
|
30
|
+
let payload = { actor: socialAccountId, object: externalId };
|
|
31
|
+
let query = likedByUser
|
|
32
|
+
? `${configuration.get(
|
|
33
|
+
'LINKEDIN_API'
|
|
34
|
+
)}/socialActions/${fixedEncodeURIComponent(externalId)}/likes`
|
|
35
|
+
: `${configuration.get(
|
|
36
|
+
'LINKEDIN_API'
|
|
37
|
+
)}/socialActions/${fixedEncodeURIComponent(
|
|
38
|
+
externalId
|
|
39
|
+
)}/likes/${fixedEncodeURIComponent(
|
|
40
|
+
payload.actor
|
|
41
|
+
)}?actor=${fixedEncodeURIComponent(payload.actor)}`;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
let credential = await this.credentialsAPI.getToken(
|
|
45
|
+
credentialId,
|
|
46
|
+
companyId
|
|
47
|
+
);
|
|
48
|
+
if (!likedByUser) {
|
|
49
|
+
logger.debug(`${documentId} - trying Delete `, {
|
|
50
|
+
query,
|
|
51
|
+
payload,
|
|
52
|
+
});
|
|
53
|
+
response = await superagent
|
|
54
|
+
.delete(query)
|
|
55
|
+
.timeout(5000)
|
|
56
|
+
.set({
|
|
57
|
+
Authorization: `Bearer ${credential.token}`,
|
|
58
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
59
|
+
'LINKEDIN_API_VERSION'
|
|
60
|
+
),
|
|
61
|
+
'LinkedIn-Version': '202305',
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
logger.debug(`${documentId} - sending to linkedin `, {
|
|
65
|
+
query,
|
|
66
|
+
payload,
|
|
67
|
+
});
|
|
68
|
+
response = await superagent
|
|
69
|
+
.post(query)
|
|
70
|
+
.timeout(5000) // 5 seconds
|
|
71
|
+
.set({
|
|
72
|
+
Authorization: `Bearer ${credential.token}`,
|
|
73
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
74
|
+
'LINKEDIN_API_VERSION'
|
|
75
|
+
),
|
|
76
|
+
'LinkedIn-Version': '202305',
|
|
77
|
+
})
|
|
78
|
+
.send(payload);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
logger.info(
|
|
82
|
+
`Native Linkedin API Like Comment Response for documentId ${documentId}`,
|
|
83
|
+
{
|
|
84
|
+
ok: response.ok,
|
|
85
|
+
status: response.status,
|
|
86
|
+
message: JSON.parse(response.text),
|
|
87
|
+
likedByUser,
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
logger.error(documentId + ' - exception details', {
|
|
92
|
+
err,
|
|
93
|
+
});
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
return response.connection || response;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async deleteMessage(document) {
|
|
100
|
+
const {
|
|
101
|
+
documentId,
|
|
102
|
+
systemData: {
|
|
103
|
+
connectionsCredential: credentialId,
|
|
104
|
+
policies: {
|
|
105
|
+
storage: { privateTo: companyId },
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
metaData: {
|
|
109
|
+
externalId,
|
|
110
|
+
source: { id: actorURN },
|
|
111
|
+
inReplyTo: { id: inReplyToId },
|
|
112
|
+
},
|
|
113
|
+
} = document;
|
|
114
|
+
|
|
115
|
+
const [, comment] = externalId.split(',');
|
|
116
|
+
const commentId = comment.split(')')[0];
|
|
117
|
+
|
|
118
|
+
let response;
|
|
119
|
+
let query = `${configuration.get(
|
|
120
|
+
'LINKEDIN_API'
|
|
121
|
+
)}/socialActions/${fixedEncodeURIComponent(
|
|
122
|
+
inReplyToId
|
|
123
|
+
)}/comments/${commentId}?actor=${fixedEncodeURIComponent(actorURN)}`;
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
let credential = await this.credentialsAPI.getToken(
|
|
127
|
+
credentialId,
|
|
128
|
+
companyId
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
logger.debug(`${documentId} - trying Delete `, {
|
|
132
|
+
query,
|
|
133
|
+
});
|
|
134
|
+
response = await superagent
|
|
135
|
+
.delete(query)
|
|
136
|
+
.timeout(5000)
|
|
137
|
+
.set({
|
|
138
|
+
Authorization: `Bearer ${credential.token}`,
|
|
139
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
140
|
+
'LINKEDIN_API_VERSION'
|
|
141
|
+
),
|
|
142
|
+
'LinkedIn-Version': '202305',
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
logger.info(
|
|
146
|
+
`Native Linkedin API Delete Comment Response for documentId ${documentId}`,
|
|
147
|
+
{ status: response.status, ok: response.ok }
|
|
148
|
+
);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
let errorText = '';
|
|
151
|
+
if (err && err.response && err.response.text) {
|
|
152
|
+
errorText = `Call to linkedin api with documentId ${documentId} failed`;
|
|
153
|
+
logger.error(errorText, {
|
|
154
|
+
message: JSON.parse(err.response.text),
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
errorText = `Call to linkedin api with documentId ${documentId} failed`;
|
|
158
|
+
logger.error(errorText, err);
|
|
159
|
+
}
|
|
160
|
+
throw new Error(errorText);
|
|
161
|
+
}
|
|
162
|
+
return response;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async publishComment(document, mediaId) {
|
|
166
|
+
// command
|
|
167
|
+
const {
|
|
168
|
+
documentId,
|
|
169
|
+
systemData: {
|
|
170
|
+
connectionsCredential: credentialId,
|
|
171
|
+
policies: {
|
|
172
|
+
storage: { privateTo: companyId },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
body: {
|
|
176
|
+
content: { text: messageText },
|
|
177
|
+
},
|
|
178
|
+
metaData: {
|
|
179
|
+
discussionType,
|
|
180
|
+
inReplyTo: { id: parentUrn },
|
|
181
|
+
source: { id: socialAccountId },
|
|
182
|
+
},
|
|
183
|
+
} = document;
|
|
184
|
+
let messageType = discussionType === 're' ? 'reply' : 'comment';
|
|
185
|
+
|
|
186
|
+
// for now this is needed to make the urns from the images api work until the docs are updated to reflect how to use these new ids.
|
|
187
|
+
if (mediaId) {
|
|
188
|
+
mediaId = mediaId.replace(
|
|
189
|
+
'urn:li:image:',
|
|
190
|
+
'urn:li:digitalmediaAsset:'
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let body = {
|
|
195
|
+
actor: socialAccountId,
|
|
196
|
+
message: {
|
|
197
|
+
attributes: [],
|
|
198
|
+
text: messageText,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
if (messageType === 'reply') {
|
|
202
|
+
body.parentComment = parentUrn;
|
|
203
|
+
}
|
|
204
|
+
if (mediaId) {
|
|
205
|
+
body.content = [
|
|
206
|
+
{
|
|
207
|
+
entity: {
|
|
208
|
+
digitalmediaAsset: mediaId,
|
|
209
|
+
},
|
|
210
|
+
type: 'IMAGE',
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let query = `${configuration.get(
|
|
216
|
+
'LINKEDIN_API'
|
|
217
|
+
)}/socialActions/${fixedEncodeURIComponent(parentUrn)}/comments`;
|
|
218
|
+
|
|
219
|
+
// data-listener -> linkedin_api.client sendComment
|
|
220
|
+
let response, updatedDocument;
|
|
221
|
+
try {
|
|
222
|
+
let credential = await this.credentialsAPI.getToken(
|
|
223
|
+
credentialId,
|
|
224
|
+
companyId
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
let response = await superagent
|
|
228
|
+
.post(query)
|
|
229
|
+
.timeout(5000)
|
|
230
|
+
.set({
|
|
231
|
+
Authorization: `Bearer ${credential.token}`,
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
234
|
+
'LINKEDIN_API_VERSION'
|
|
235
|
+
),
|
|
236
|
+
'LinkedIn-Version': '202305',
|
|
237
|
+
})
|
|
238
|
+
.send(body);
|
|
239
|
+
|
|
240
|
+
response = {
|
|
241
|
+
status: 200,
|
|
242
|
+
message: JSON.parse(response.text),
|
|
243
|
+
};
|
|
244
|
+
} catch (err) {
|
|
245
|
+
logger.error(
|
|
246
|
+
documentId + ' - exception linkedin publish ' + err,
|
|
247
|
+
err
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return response;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async getMediaUploadURL(
|
|
255
|
+
companyId,
|
|
256
|
+
credentialId,
|
|
257
|
+
socialAccountId,
|
|
258
|
+
documentId
|
|
259
|
+
) {
|
|
260
|
+
let query = `${configuration.get(
|
|
261
|
+
'LINKEDIN_API'
|
|
262
|
+
)}/images?action=initializeUpload`;
|
|
263
|
+
let body = {
|
|
264
|
+
initializeUploadRequest: {
|
|
265
|
+
owner: socialAccountId,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
let response;
|
|
270
|
+
try {
|
|
271
|
+
let credential = await this.credentialsAPI.getToken(
|
|
272
|
+
credentialId,
|
|
273
|
+
companyId
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
response = await superagent
|
|
277
|
+
.post(query)
|
|
278
|
+
.timeout(5000)
|
|
279
|
+
.set({
|
|
280
|
+
Authorization: `Bearer ${credential.token}`,
|
|
281
|
+
'Content-type': 'application/json',
|
|
282
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
283
|
+
'LINKEDIN_API_VERSION'
|
|
284
|
+
),
|
|
285
|
+
'LinkedIn-Version': '202305',
|
|
286
|
+
})
|
|
287
|
+
.send(body);
|
|
288
|
+
|
|
289
|
+
response = {
|
|
290
|
+
status: 200,
|
|
291
|
+
message: JSON.parse(response.text),
|
|
292
|
+
};
|
|
293
|
+
return response;
|
|
294
|
+
} catch (err) {
|
|
295
|
+
logger.error(
|
|
296
|
+
documentId + ' - exception linkedin media register ' + err,
|
|
297
|
+
err
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async getProfile(urn, token) {
|
|
303
|
+
let [, , , id] = urn.split(':');
|
|
304
|
+
|
|
305
|
+
let profile;
|
|
306
|
+
try {
|
|
307
|
+
profile = await superagent
|
|
308
|
+
.get(`${configuration.get('LINKEDIN_API')}/people/(id:${id})`)
|
|
309
|
+
.query({
|
|
310
|
+
projection:
|
|
311
|
+
'(id,localizedFirstName,localizedLastName,vanityName,profilePicture(displayImage~:playableStreams))',
|
|
312
|
+
})
|
|
313
|
+
.timeout(5000)
|
|
314
|
+
.set({
|
|
315
|
+
Authorization: `Bearer ${token}`,
|
|
316
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
317
|
+
'LINKEDIN_API_VERSION'
|
|
318
|
+
),
|
|
319
|
+
'LinkedIn-Version': '202305',
|
|
320
|
+
})
|
|
321
|
+
.then((result) => result.body);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
logger.error(
|
|
324
|
+
`Failed requesting LinkedIn API for profileId ${urn}`,
|
|
325
|
+
error
|
|
326
|
+
);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
logger.info(`Finished requesting LinkedIn API for profileId ${urn}`);
|
|
331
|
+
|
|
332
|
+
return profile;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async getOrganization(urn, token) {
|
|
336
|
+
let [, , , id] = urn.split(':');
|
|
337
|
+
|
|
338
|
+
let organization;
|
|
339
|
+
try {
|
|
340
|
+
organization = await superagent
|
|
341
|
+
.get(`${configuration.get('LINKEDIN_API')}/organizations/${id}`)
|
|
342
|
+
.query({
|
|
343
|
+
projection:
|
|
344
|
+
'(id,localizedName,vanityName,logoV2(original~:playableStreams))',
|
|
345
|
+
})
|
|
346
|
+
.timeout(5000) // 5 seconds
|
|
347
|
+
.set({
|
|
348
|
+
Authorization: `Bearer ${token}`,
|
|
349
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
350
|
+
'LINKEDIN_API_VERSION'
|
|
351
|
+
),
|
|
352
|
+
'LinkedIn-Version': '202305',
|
|
353
|
+
})
|
|
354
|
+
.then((result) => result.body);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
logger.error(
|
|
357
|
+
`Failed requesting LinkedIn API for organizationId ${urn}`,
|
|
358
|
+
error
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
logger.info(
|
|
363
|
+
`Finished requesting LinkedIn API for organizationId ${urn}`
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
return organization;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async getAllStats(externalId, token) {
|
|
370
|
+
let response = {};
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
response = await superagent
|
|
374
|
+
.get(
|
|
375
|
+
configuration.get('LINKEDIN_API') +
|
|
376
|
+
'/socialMetadata/' +
|
|
377
|
+
fixedEncodeURIComponent(externalId)
|
|
378
|
+
)
|
|
379
|
+
.timeout(5000) // 5 seconds
|
|
380
|
+
.set({
|
|
381
|
+
Authorization: `Bearer ${token}`,
|
|
382
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
383
|
+
'LINKEDIN_API_VERSION'
|
|
384
|
+
),
|
|
385
|
+
'LinkedIn-Version': '202305',
|
|
386
|
+
});
|
|
387
|
+
} catch (error) {
|
|
388
|
+
logger.error('Error in getAllStats ' + error, error);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
let { commentSummary, reactionSummaries } = response.body || {};
|
|
392
|
+
if (commentSummary && reactionSummaries) {
|
|
393
|
+
return { commentSummary, reactionSummaries };
|
|
394
|
+
} else {
|
|
395
|
+
logger.error(
|
|
396
|
+
`Invalid response getting all Linkedin Stats for externalId = ${externalId} ` +
|
|
397
|
+
response,
|
|
398
|
+
response
|
|
399
|
+
);
|
|
400
|
+
return {};
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
async getSocialStats(externalId, token) {
|
|
405
|
+
try {
|
|
406
|
+
// get all stats (likes are not to be trusted) seems to only work
|
|
407
|
+
// for og posts.
|
|
408
|
+
const { commentSummary, reactionSummaries } =
|
|
409
|
+
await this.getAllStats(externalId, token);
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
commentSummary,
|
|
413
|
+
reactionSummaries,
|
|
414
|
+
};
|
|
415
|
+
} catch (error) {
|
|
416
|
+
logger.error('Error in getSocialStats: ' + error, error);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async getImageMedia(mediaId, credentialId, companyId) {
|
|
421
|
+
let response = {};
|
|
422
|
+
|
|
423
|
+
let credential = await this.credentialsAPI.getToken(
|
|
424
|
+
credentialId,
|
|
425
|
+
companyId
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
if (mediaId && mediaId.includes('digitalmediaAsset')) {
|
|
429
|
+
mediaId = mediaId.replace(
|
|
430
|
+
'urn:li:digitalmediaAsset:',
|
|
431
|
+
'urn:li:image:'
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
try {
|
|
436
|
+
response = await superagent
|
|
437
|
+
.get(
|
|
438
|
+
configuration.get('LINKEDIN_API') +
|
|
439
|
+
'/images/' +
|
|
440
|
+
fixedEncodeURIComponent(mediaId)
|
|
441
|
+
)
|
|
442
|
+
.timeout(5000) // 5 seconds
|
|
443
|
+
.set({
|
|
444
|
+
Authorization: `Bearer ${credential.token}`,
|
|
445
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
446
|
+
'LINKEDIN_API_VERSION'
|
|
447
|
+
),
|
|
448
|
+
'LinkedIn-Version': '202305',
|
|
449
|
+
});
|
|
450
|
+
} catch (error) {
|
|
451
|
+
logger.error('Error in getImageMedia ' + error, error);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
let { downloadUrl } = response.body || {};
|
|
455
|
+
if (downloadUrl) {
|
|
456
|
+
return { downloadUrl };
|
|
457
|
+
} else {
|
|
458
|
+
logger.error(
|
|
459
|
+
`Invalid response getting Linkedin media for mediaId = ${mediaId} ` +
|
|
460
|
+
response,
|
|
461
|
+
response
|
|
462
|
+
);
|
|
463
|
+
return {};
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
async getVideoMedia(mediaId, credentialId, companyId) {
|
|
468
|
+
let response = {};
|
|
469
|
+
let tempValue;
|
|
470
|
+
|
|
471
|
+
let credential = await this.credentialsAPI.getToken(
|
|
472
|
+
credentialId,
|
|
473
|
+
companyId
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
if (mediaId && mediaId.includes('digitalmediaMediaArtifact')) {
|
|
477
|
+
tempValue = mediaId.split('digitalmediaMediaArtifact:(')[1];
|
|
478
|
+
tempValue = tempValue.split(
|
|
479
|
+
',urn:li:digitalmediaMediaArtifactClass'
|
|
480
|
+
)[0];
|
|
481
|
+
mediaId = tempValue;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (mediaId && mediaId.includes('digitalmediaAsset')) {
|
|
485
|
+
mediaId = mediaId.replace(
|
|
486
|
+
'urn:li:digitalmediaAsset:',
|
|
487
|
+
'urn:li:video:'
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
response = await superagent
|
|
493
|
+
.get(
|
|
494
|
+
configuration.get('LINKEDIN_API') +
|
|
495
|
+
'/videos/' +
|
|
496
|
+
fixedEncodeURIComponent(mediaId)
|
|
497
|
+
)
|
|
498
|
+
.timeout(5000) // 5 seconds
|
|
499
|
+
.set({
|
|
500
|
+
Authorization: `Bearer ${credential.token}`,
|
|
501
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
502
|
+
'LINKEDIN_API_VERSION'
|
|
503
|
+
),
|
|
504
|
+
'LinkedIn-Version': '202305',
|
|
505
|
+
});
|
|
506
|
+
} catch (error) {
|
|
507
|
+
logger.error('Error in getVideoMedia ' + error, error);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
let { thumbnail, downloadUrl } = response.body || {};
|
|
511
|
+
if (downloadUrl) {
|
|
512
|
+
return { thumbnail, downloadUrl };
|
|
513
|
+
} else {
|
|
514
|
+
logger.error(
|
|
515
|
+
`Invalid response getting Linkedin media for mediaId = ${mediaId} ` +
|
|
516
|
+
response,
|
|
517
|
+
response
|
|
518
|
+
);
|
|
519
|
+
return {};
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async getMedia(mediaId, type, credentialId, companyId) {
|
|
524
|
+
// needed due to issue with polled data
|
|
525
|
+
if (type.includes('video') && mediaId.includes('image')) {
|
|
526
|
+
type = 'image/jpeg';
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
if (type.includes('image')) {
|
|
531
|
+
const { downloadUrl } = await this.getImageMedia(
|
|
532
|
+
mediaId,
|
|
533
|
+
credentialId,
|
|
534
|
+
companyId
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
return {
|
|
538
|
+
downloadUrl,
|
|
539
|
+
};
|
|
540
|
+
} else if (type.includes('video')) {
|
|
541
|
+
const { thumbnail, downloadUrl } = await this.getVideoMedia(
|
|
542
|
+
mediaId,
|
|
543
|
+
credentialId,
|
|
544
|
+
companyId
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
return {
|
|
548
|
+
thumbnail,
|
|
549
|
+
downloadUrl,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return {};
|
|
554
|
+
} catch (error) {
|
|
555
|
+
logger.error('Error in getMedia: ' + error, error);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
async getLikedByUser(
|
|
560
|
+
externalId,
|
|
561
|
+
authorExternalId,
|
|
562
|
+
credentialId,
|
|
563
|
+
companyId
|
|
564
|
+
) {
|
|
565
|
+
let result;
|
|
566
|
+
try {
|
|
567
|
+
let credential = await this.credentialsAPI.getToken(
|
|
568
|
+
credentialId,
|
|
569
|
+
companyId
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
result = await superagent
|
|
573
|
+
.get(
|
|
574
|
+
`${configuration.get(
|
|
575
|
+
'LINKEDIN_API'
|
|
576
|
+
)}/reactions/(actor:${fixedEncodeURIComponent(
|
|
577
|
+
authorExternalId
|
|
578
|
+
)},entity:${fixedEncodeURIComponent(externalId)})`
|
|
579
|
+
)
|
|
580
|
+
.set({
|
|
581
|
+
Authorization: `Bearer ${credential.token}`,
|
|
582
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
583
|
+
'LINKEDIN_API_VERSION'
|
|
584
|
+
),
|
|
585
|
+
'LinkedIn-Version': '202305',
|
|
586
|
+
})
|
|
587
|
+
.then((result) => result.body);
|
|
588
|
+
} catch (error) {
|
|
589
|
+
logger.error('Error in getLikesByUser: ' + error, error);
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
async uploadMedia(query) {
|
|
596
|
+
const { socialAccountId, url, token, data } = query;
|
|
597
|
+
|
|
598
|
+
logger.info(
|
|
599
|
+
`Starting to upload media to linkedin for ${socialAccountId}`,
|
|
600
|
+
{ url: query.url }
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
return await superagent
|
|
604
|
+
.put(url)
|
|
605
|
+
.set({
|
|
606
|
+
Accept: '*/*',
|
|
607
|
+
'Content-Type': 'application/octet-stream',
|
|
608
|
+
Authorization: `Bearer ${token}`,
|
|
609
|
+
'X-RestLi-Protocol-Version': configuration.get(
|
|
610
|
+
'LINKEDIN_API_VERSION'
|
|
611
|
+
),
|
|
612
|
+
'LinkedIn-Version': '202305',
|
|
613
|
+
})
|
|
614
|
+
.send(data)
|
|
615
|
+
.catch((err) => {
|
|
616
|
+
logger.error(err);
|
|
617
|
+
throw err;
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function fixedEncodeURIComponent(str) {
|
|
623
|
+
return encodeURIComponent(str).replace('(', '%28').replace(')', '%29');
|
|
624
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import configuration from '../../lib/configuration.js';
|
|
4
|
+
|
|
5
|
+
export class MasfClient {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.searchServicesUrlMasf = configuration.get(
|
|
8
|
+
'MASF_SEARCH_SERVICE_URL'
|
|
9
|
+
);
|
|
10
|
+
this.searchServicesVersionMasf = configuration.get(
|
|
11
|
+
'MASF_SEARCH_SERVICE_VERSION'
|
|
12
|
+
);
|
|
13
|
+
this.authorListVersionMasf = 'v3';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async getSearches({ companyId, token }) {
|
|
17
|
+
try {
|
|
18
|
+
let uri = `${this.searchServicesUrlMasf}/${this.searchServicesVersionMasf}/company/${companyId}/searches`;
|
|
19
|
+
return (
|
|
20
|
+
await superagent.get(uri).set({
|
|
21
|
+
Authorization: `${token}`,
|
|
22
|
+
})
|
|
23
|
+
).body;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
logger.error(
|
|
26
|
+
`Failed requesting Search Services for search list `,
|
|
27
|
+
error
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getAuthorsLists({ companyId, token }) {
|
|
33
|
+
try {
|
|
34
|
+
let uri = `${this.searchServicesUrlMasf}/v3/companies/${companyId}/authorlists`;
|
|
35
|
+
return (
|
|
36
|
+
await superagent.get(uri).set({
|
|
37
|
+
Authorization: `${token}`,
|
|
38
|
+
})
|
|
39
|
+
).body.sort(({ name: n1 }, { name: n2 }) => {
|
|
40
|
+
if (n1.toUpperCase() < n2.toUpperCase()) {
|
|
41
|
+
return -1;
|
|
42
|
+
}
|
|
43
|
+
if (n1.toUpperCase() > n2.toUpperCase()) {
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
return 0;
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
logger.error(
|
|
50
|
+
`Failed requesting Search Services for author list `,
|
|
51
|
+
error
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getAuthorsList({ companyId, authorListId, token }) {
|
|
57
|
+
try {
|
|
58
|
+
let uri = `${this.searchServicesUrlMasf}/${this.authorListVersionMasf}/companies/${companyId}/authorlists/${authorListId}`;
|
|
59
|
+
return (
|
|
60
|
+
await superagent.get(uri).set({
|
|
61
|
+
Authorization: `${token}`,
|
|
62
|
+
})
|
|
63
|
+
).body;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.error(
|
|
66
|
+
`Failed requesting Search Services for author list `,
|
|
67
|
+
error
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getRune({ searchId, token }) {
|
|
73
|
+
let rune;
|
|
74
|
+
try {
|
|
75
|
+
let uri = `${this.searchServicesUrlMasf}/${this.searchServicesVersionMasf}/search/getSavedSearchAsRunes/${searchId}`;
|
|
76
|
+
rune = await superagent.get(uri).set({
|
|
77
|
+
Authorization: `${token}`,
|
|
78
|
+
});
|
|
79
|
+
} catch (error) {
|
|
80
|
+
logger.error(
|
|
81
|
+
`Failed requesting Search Services for search list `,
|
|
82
|
+
error
|
|
83
|
+
);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logger.info(
|
|
88
|
+
`Finished requesting Search Services for rune ${this.searchServicesUrlMasf} searchId: ${searchId}`
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return rune.body;
|
|
92
|
+
}
|
|
93
|
+
}
|