@meltwater/conversations-api-services 1.0.22 → 1.0.23

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.
@@ -0,0 +1,406 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.comment = comment;
7
+ exports.deleteMessage = deleteMessage;
8
+ exports.getMedia = getMedia;
9
+ exports.getOrganization = getOrganization;
10
+ exports.getProfile = getProfile;
11
+ exports.getSocialStats = getSocialStats;
12
+ exports.getVideoMedia = getVideoMedia;
13
+ exports.like = like;
14
+ exports.likedByUser = likedByUser;
15
+ exports.mediaUploadURL = mediaUploadURL;
16
+ exports.reply = reply;
17
+ exports.unlike = unlike;
18
+ exports.uploadMedia = uploadMedia;
19
+ var _superagent = _interopRequireDefault(require("superagent"));
20
+ var _loggerHelpers = require("../../lib/logger.helpers.js");
21
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
+ const LINKEDIN_API = "https://api.linkedin.com/v2";
23
+ const LINKEDIN_API_VERSION = "2.0.0";
24
+ async function like(token, externalId, socialAccountId, logger) {
25
+ let response;
26
+ let payload = {
27
+ actor: socialAccountId,
28
+ object: externalId
29
+ };
30
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(externalId)}/likes`;
31
+ try {
32
+ (0, _loggerHelpers.loggerDebug)(logger, `Linkedin trying to Like `, {
33
+ query,
34
+ payload: JSON.stringify(payload)
35
+ });
36
+ response = await _superagent.default.post(query).timeout(5000) // 5 seconds
37
+ .set({
38
+ Authorization: `Bearer ${token}`,
39
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
40
+ 'LinkedIn-Version': '202311'
41
+ }).send(payload);
42
+ (0, _loggerHelpers.loggerInfo)(logger, `Native Linkedin API Like Mutation Response`, {
43
+ response: JSON.stringify(response)
44
+ });
45
+ } catch (err) {
46
+ (0, _loggerHelpers.loggerError)(logger, `Linkedin Like or Unlike Exception`, err);
47
+ throw err;
48
+ }
49
+ return response.connection || response;
50
+ }
51
+ async function unlike(token, externalId, socialAccountId, logger) {
52
+ let response;
53
+ let payload = {
54
+ actor: socialAccountId,
55
+ object: externalId
56
+ };
57
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(externalId)}/likes/${fixedEncodeURIComponent(payload.actor)}?actor=${fixedEncodeURIComponent(payload.actor)}`;
58
+ try {
59
+ (0, _loggerHelpers.loggerDebug)(logger, `Linkedin trying Delete Previous Like `, {
60
+ query,
61
+ payload: JSON.stringify(payload)
62
+ });
63
+ response = await _superagent.default.delete(query).timeout(5000).set({
64
+ Authorization: `Bearer ${token}`,
65
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
66
+ 'LinkedIn-Version': '202311'
67
+ });
68
+ (0, _loggerHelpers.loggerInfo)(logger, `Native Linkedin API UNLike Mutation Response`, {
69
+ response: JSON.stringify(response)
70
+ });
71
+ } catch (err) {
72
+ (0, _loggerHelpers.loggerError)(logger, `Linkedin Like or Unlike Exception`, err);
73
+ throw err;
74
+ }
75
+ return response.connection || response;
76
+ }
77
+ async function deleteMessage(token, externalId, sourceId, inReplyToId, logger) {
78
+ const [, comment] = externalId.split(',');
79
+ const commentId = comment.split(')')[0];
80
+ let response;
81
+ const query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(inReplyToId)}/comments/${commentId}?actor=${fixedEncodeURIComponent(sourceId)}`;
82
+ try {
83
+ (0, _loggerHelpers.loggerDebug)(logger, `Linkedin trying Delete `, {
84
+ query
85
+ });
86
+ response = await _superagent.default.delete(query).timeout(5000).set({
87
+ Authorization: `Bearer ${token}`,
88
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
89
+ 'LinkedIn-Version': '202311'
90
+ });
91
+ (0, _loggerHelpers.loggerInfo)(logger, `Native Linkedin API Delete Comment Response`, {
92
+ response: JSON.stringify(response)
93
+ });
94
+ } catch (err) {
95
+ (0, _loggerHelpers.loggerError)(logger, `Linkedin Delete exception details`, {
96
+ err
97
+ });
98
+ throw err;
99
+ }
100
+ return response;
101
+ }
102
+ async function comment(token, mediaId, inReplyToId, socialAccountId, messageText, logger) {
103
+ return publish(token, 'qt', mediaId, inReplyToId, socialAccountId, messageText, logger);
104
+ }
105
+ async function reply(token, mediaId, inReplyToId, socialAccountId, messageText, logger) {
106
+ return publish(token, 're', mediaId, inReplyToId, socialAccountId, messageText, logger);
107
+ }
108
+ async function publish(token, discussionType, mediaId, inReplyToId, socialAccountId, messageText, logger) {
109
+ // 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.
110
+ if (mediaId) {
111
+ mediaId = mediaId.replace('urn:li:image:', 'urn:li:digitalmediaAsset:');
112
+ }
113
+ let body = {
114
+ actor: socialAccountId,
115
+ message: {
116
+ attributes: [],
117
+ text: messageText
118
+ }
119
+ };
120
+ if (discussionType === 're') {
121
+ body.parentComment = inReplyToId;
122
+ }
123
+ if (mediaId) {
124
+ body.content = [{
125
+ entity: {
126
+ digitalmediaAsset: mediaId
127
+ },
128
+ type: 'IMAGE'
129
+ }];
130
+ }
131
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(inReplyToId)}/comments`;
132
+
133
+ // data-listener -> linkedin_api.client sendComment
134
+ let response;
135
+ let downloadUrl = '';
136
+ try {
137
+ response = await _superagent.default.post(query).timeout(5000).set({
138
+ Authorization: `Bearer ${token}`,
139
+ 'Content-Type': 'application/json',
140
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
141
+ 'LinkedIn-Version': '202311'
142
+ }).send(body);
143
+ (0, _loggerHelpers.loggerInfo)(logger, `Native Linkedin API Publish Comment Response`, {
144
+ response: JSON.stringify(response)
145
+ });
146
+ response = {
147
+ status: 200,
148
+ message: JSON.parse(response.text)
149
+ };
150
+ if (mediaId) {
151
+ (0, _loggerHelpers.loggerInfo)(logger, `mediaId`, {
152
+ mediaId
153
+ });
154
+ downloadUrl = await getImageMedia(mediaId, token, logger);
155
+ (0, _loggerHelpers.loggerInfo)(logger, `downloadUrl`, {
156
+ downloadUrl
157
+ });
158
+ }
159
+ } catch (err) {
160
+ (0, _loggerHelpers.loggerError)(logger`Exception in linkedin publish `, err);
161
+ throw err;
162
+ }
163
+ return {
164
+ response,
165
+ downloadUrl
166
+ };
167
+ }
168
+ async function getImageMedia(mediaId, token, logger) {
169
+ let response = {};
170
+ if (mediaId && mediaId.includes('digitalmediaAsset')) {
171
+ mediaId = mediaId.replace('urn:li:digitalmediaAsset:', 'urn:li:image:');
172
+ }
173
+ try {
174
+ response = await _superagent.default.get(LINKEDIN_API + '/images/' + fixedEncodeURIComponent(mediaId)).timeout(5000) // 5 seconds
175
+ .set({
176
+ Authorization: `Bearer ${token}`,
177
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
178
+ 'LinkedIn-Version': '202311'
179
+ });
180
+ } catch (error) {
181
+ (0, _loggerHelpers.loggerError)(logger, 'Error in getImageMedia', error);
182
+ }
183
+ let {
184
+ downloadUrl
185
+ } = response.body || {};
186
+ if (downloadUrl) {
187
+ return {
188
+ downloadUrl
189
+ };
190
+ } else {
191
+ (0, _loggerHelpers.loggerError)(logger, `Invalid response getting Linkedin media for mediaId: ${mediaId} `, {
192
+ response: JSON.stringify(response)
193
+ });
194
+ return {};
195
+ }
196
+ }
197
+ async function getVideoMedia(mediaId, token, logger) {
198
+ let response = {};
199
+ let tempValue;
200
+ if (mediaId && mediaId.includes('digitalmediaMediaArtifact')) {
201
+ tempValue = mediaId.split('digitalmediaMediaArtifact:(')[1];
202
+ tempValue = tempValue.split(',urn:li:digitalmediaMediaArtifactClass')[0];
203
+ mediaId = tempValue;
204
+ }
205
+ if (mediaId && mediaId.includes('digitalmediaAsset')) {
206
+ mediaId = mediaId.replace('urn:li:digitalmediaAsset:', 'urn:li:video:');
207
+ }
208
+ try {
209
+ response = await _superagent.default.get(LINKEDIN_API + '/videos/' + fixedEncodeURIComponent(mediaId)).timeout(5000) // 5 seconds
210
+ .set({
211
+ Authorization: `Bearer ${token}`,
212
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
213
+ 'LinkedIn-Version': '202311'
214
+ });
215
+ } catch (error) {
216
+ (0, _loggerHelpers.loggerError)(logger, 'Error in getVideoMedia', error);
217
+ }
218
+ let {
219
+ thumbnail,
220
+ downloadUrl
221
+ } = response.body || {};
222
+ if (downloadUrl) {
223
+ return {
224
+ thumbnail,
225
+ downloadUrl
226
+ };
227
+ } else {
228
+ (0, _loggerHelpers.loggerError)(logger, `Invalid response getting Linkedin media for mediaId: ${mediaId} `, {
229
+ response: JSON.stringify(response)
230
+ });
231
+ return {};
232
+ }
233
+ }
234
+ async function getMedia(token, mediaId, type, logger) {
235
+ // needed due to issue with polled data
236
+ if (type.includes('video') && mediaId.includes('image')) {
237
+ type = 'image/jpeg';
238
+ }
239
+ try {
240
+ if (type.includes('image')) {
241
+ const {
242
+ downloadUrl
243
+ } = await getImageMedia(mediaId, token, logger);
244
+ return {
245
+ downloadUrl
246
+ };
247
+ } else if (type.includes('video')) {
248
+ const {
249
+ thumbnail,
250
+ downloadUrl
251
+ } = await getVideoMedia(mediaId, token, logger);
252
+ return {
253
+ thumbnail,
254
+ downloadUrl
255
+ };
256
+ }
257
+ return {};
258
+ } catch (error) {
259
+ (0, _loggerHelpers.loggerError)(logger, 'Error in getMedia', error);
260
+ }
261
+ }
262
+ async function mediaUploadURL(token, socialAccountId, logger) {
263
+ let query = `${LINKEDIN_API}/images?action=initializeUpload`;
264
+ let body = {
265
+ initializeUploadRequest: {
266
+ owner: socialAccountId
267
+ }
268
+ };
269
+ let response;
270
+ try {
271
+ response = await _superagent.default.post(query).timeout(5000).set({
272
+ Authorization: `Bearer ${token}`,
273
+ 'Content-type': 'application/json',
274
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
275
+ 'LinkedIn-Version': '202311'
276
+ }).send(body);
277
+ response = {
278
+ status: 200,
279
+ message: JSON.parse(response.text)
280
+ };
281
+ return response;
282
+ } catch (err) {
283
+ (0, _loggerHelpers.loggerError)(logger, `Exception linkedin media register `, err);
284
+ }
285
+ }
286
+ async function getProfile(urn, token, logger) {
287
+ let [,,, id] = urn.split(':');
288
+ let profile;
289
+ try {
290
+ profile = await _superagent.default.get(`${LINKEDIN_API}/people/(id:${id})`).query({
291
+ projection: '(id,localizedFirstName,localizedLastName,vanityName,profilePicture(displayImage~:playableStreams))'
292
+ }).timeout(5000).set({
293
+ Authorization: `Bearer ${token}`,
294
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
295
+ 'LinkedIn-Version': '202311'
296
+ }).then(result => result.body);
297
+ } catch (error) {
298
+ (0, _loggerHelpers.loggerError)(logger, `Failed requesting LinkedIn API for profileId ${urn}`, error);
299
+ return;
300
+ }
301
+ (0, _loggerHelpers.loggerInfo)(logger, `Finished requesting LinkedIn API for profileId ${urn}`);
302
+ return profile;
303
+ }
304
+ async function getOrganization(urn, token, logger) {
305
+ let [,,, id] = urn.split(':');
306
+ let organization;
307
+ try {
308
+ organization = await _superagent.default.get(`${LINKEDIN_API}/organizations/${id}`).query({
309
+ projection: '(id,localizedName,vanityName,logoV2(original~:playableStreams))'
310
+ }).timeout(5000) // 5 seconds
311
+ .set({
312
+ Authorization: `Bearer ${token}`,
313
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
314
+ 'LinkedIn-Version': '202311'
315
+ }).then(result => result.body);
316
+ } catch (error) {
317
+ if (error.response.body.message.includes('ADMIN_ONLY')) {
318
+ return {
319
+ id: 'private'
320
+ };
321
+ }
322
+ (0, _loggerHelpers.loggerError)(logger, `Failed requesting LinkedIn API for organizationId ${urn}`, error);
323
+ }
324
+ (0, _loggerHelpers.loggerInfo)(logger, `Finished requesting LinkedIn API for organizationId ${urn}`);
325
+ return organization;
326
+ }
327
+ async function getAllStats(externalId, token, logger) {
328
+ let response = {};
329
+ try {
330
+ response = await _superagent.default.get(LINKEDIN_API + '/socialMetadata/' + fixedEncodeURIComponent(externalId)).timeout(5000) // 5 seconds
331
+ .set({
332
+ Authorization: `Bearer ${token}`,
333
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
334
+ 'LinkedIn-Version': '202311'
335
+ });
336
+ } catch (error) {
337
+ (0, _loggerHelpers.loggerError)(logger, 'Error in getAllStats', error);
338
+ }
339
+ let {
340
+ commentSummary,
341
+ reactionSummaries
342
+ } = response.body || {};
343
+ if (commentSummary && reactionSummaries) {
344
+ return {
345
+ commentSummary,
346
+ reactionSummaries
347
+ };
348
+ } else {
349
+ (0, _loggerHelpers.loggerError)(logger, `Invalid response getting all Linkedin Stats for externalId: ${externalId}`, {
350
+ response: JSON.stringify(response)
351
+ });
352
+ return {};
353
+ }
354
+ }
355
+ async function getSocialStats(externalId, token, logger) {
356
+ try {
357
+ // get all stats (likes are not to be trusted) seems to only work
358
+ // for og posts.
359
+ const {
360
+ commentSummary,
361
+ reactionSummaries
362
+ } = await getAllStats(externalId, token, logger);
363
+ return {
364
+ commentSummary,
365
+ reactionSummaries
366
+ };
367
+ } catch (error) {
368
+ (0, _loggerHelpers.loggerError)(logger, 'Error in getSocialStats', error);
369
+ }
370
+ }
371
+ async function likedByUser(token, externalId, authorExternalId, logger) {
372
+ let result;
373
+ try {
374
+ result = await _superagent.default.get(`${LINKEDIN_API}/reactions/(actor:${fixedEncodeURIComponent(authorExternalId)},entity:${fixedEncodeURIComponent(externalId)})`).set({
375
+ Authorization: `Bearer ${token}`,
376
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
377
+ 'LinkedIn-Version': '202311'
378
+ }).then(result => result.body);
379
+ } catch (error) {
380
+ (0, _loggerHelpers.loggerError)(logger, 'Error in getLikesByUser', error);
381
+ return false;
382
+ }
383
+ return true;
384
+ }
385
+
386
+ // query object is from AssetManager
387
+ async function uploadMedia(query, logger) {
388
+ const {
389
+ url,
390
+ token,
391
+ data
392
+ } = query;
393
+ return await _superagent.default.put(url).set({
394
+ Accept: '*/*',
395
+ 'Content-Type': 'application/octet-stream',
396
+ Authorization: `Bearer ${token}`,
397
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
398
+ 'LinkedIn-Version': '202311'
399
+ }).send(data).catch(err => {
400
+ (0, _loggerHelpers.loggerError)(logger, "Linkedin Error uploading Media", err);
401
+ throw err;
402
+ });
403
+ }
404
+ function fixedEncodeURIComponent(str) {
405
+ return encodeURIComponent(str).replace(/\(/g, '%28').replace(/\)/g, '%29');
406
+ }
@@ -65,7 +65,7 @@ Object.defineProperty(exports, "LinkedInApiClient", {
65
65
  return _linkedInApiClient.LinkedInApiClient;
66
66
  }
67
67
  });
68
- exports.LinkedInHelpers = void 0;
68
+ exports.LinkedinNative = exports.LinkedInHelpers = void 0;
69
69
  Object.defineProperty(exports, "MasfClient", {
70
70
  enumerable: true,
71
71
  get: function () {
@@ -119,6 +119,8 @@ var FacebookNative = _interopRequireWildcard(require("./http/facebook.native.js"
119
119
  exports.FacebookNative = FacebookNative;
120
120
  var TiktokNative = _interopRequireWildcard(require("./http/tiktok.native.js"));
121
121
  exports.TiktokNative = TiktokNative;
122
+ var LinkedinNative = _interopRequireWildcard(require("./http/linkedin.native.js"));
123
+ exports.LinkedinNative = LinkedinNative;
122
124
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
123
125
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
124
126
  const DocumentHelperFunctions = exports.DocumentHelperFunctions = {
@@ -0,0 +1,387 @@
1
+ import superagent from 'superagent';
2
+ import { loggerDebug, loggerError, loggerInfo } from '../../lib/logger.helpers.js';
3
+ const LINKEDIN_API = "https://api.linkedin.com/v2";
4
+ const LINKEDIN_API_VERSION = "2.0.0";
5
+ export async function like(token, externalId, socialAccountId, logger) {
6
+ let response;
7
+ let payload = {
8
+ actor: socialAccountId,
9
+ object: externalId
10
+ };
11
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(externalId)}/likes`;
12
+ try {
13
+ loggerDebug(logger, `Linkedin trying to Like `, {
14
+ query,
15
+ payload: JSON.stringify(payload)
16
+ });
17
+ response = await superagent.post(query).timeout(5000) // 5 seconds
18
+ .set({
19
+ Authorization: `Bearer ${token}`,
20
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
21
+ 'LinkedIn-Version': '202311'
22
+ }).send(payload);
23
+ loggerInfo(logger, `Native Linkedin API Like Mutation Response`, {
24
+ response: JSON.stringify(response)
25
+ });
26
+ } catch (err) {
27
+ loggerError(logger, `Linkedin Like or Unlike Exception`, err);
28
+ throw err;
29
+ }
30
+ return response.connection || response;
31
+ }
32
+ export async function unlike(token, externalId, socialAccountId, logger) {
33
+ let response;
34
+ let payload = {
35
+ actor: socialAccountId,
36
+ object: externalId
37
+ };
38
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(externalId)}/likes/${fixedEncodeURIComponent(payload.actor)}?actor=${fixedEncodeURIComponent(payload.actor)}`;
39
+ try {
40
+ loggerDebug(logger, `Linkedin trying Delete Previous Like `, {
41
+ query,
42
+ payload: JSON.stringify(payload)
43
+ });
44
+ response = await superagent.delete(query).timeout(5000).set({
45
+ Authorization: `Bearer ${token}`,
46
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
47
+ 'LinkedIn-Version': '202311'
48
+ });
49
+ loggerInfo(logger, `Native Linkedin API UNLike Mutation Response`, {
50
+ response: JSON.stringify(response)
51
+ });
52
+ } catch (err) {
53
+ loggerError(logger, `Linkedin Like or Unlike Exception`, err);
54
+ throw err;
55
+ }
56
+ return response.connection || response;
57
+ }
58
+ export async function deleteMessage(token, externalId, sourceId, inReplyToId, logger) {
59
+ const [, comment] = externalId.split(',');
60
+ const commentId = comment.split(')')[0];
61
+ let response;
62
+ const query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(inReplyToId)}/comments/${commentId}?actor=${fixedEncodeURIComponent(sourceId)}`;
63
+ try {
64
+ loggerDebug(logger, `Linkedin trying Delete `, {
65
+ query
66
+ });
67
+ response = await superagent.delete(query).timeout(5000).set({
68
+ Authorization: `Bearer ${token}`,
69
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
70
+ 'LinkedIn-Version': '202311'
71
+ });
72
+ loggerInfo(logger, `Native Linkedin API Delete Comment Response`, {
73
+ response: JSON.stringify(response)
74
+ });
75
+ } catch (err) {
76
+ loggerError(logger, `Linkedin Delete exception details`, {
77
+ err
78
+ });
79
+ throw err;
80
+ }
81
+ return response;
82
+ }
83
+ export async function comment(token, mediaId, inReplyToId, socialAccountId, messageText, logger) {
84
+ return publish(token, 'qt', mediaId, inReplyToId, socialAccountId, messageText, logger);
85
+ }
86
+ export async function reply(token, mediaId, inReplyToId, socialAccountId, messageText, logger) {
87
+ return publish(token, 're', mediaId, inReplyToId, socialAccountId, messageText, logger);
88
+ }
89
+ async function publish(token, discussionType, mediaId, inReplyToId, socialAccountId, messageText, logger) {
90
+ // 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.
91
+ if (mediaId) {
92
+ mediaId = mediaId.replace('urn:li:image:', 'urn:li:digitalmediaAsset:');
93
+ }
94
+ let body = {
95
+ actor: socialAccountId,
96
+ message: {
97
+ attributes: [],
98
+ text: messageText
99
+ }
100
+ };
101
+ if (discussionType === 're') {
102
+ body.parentComment = inReplyToId;
103
+ }
104
+ if (mediaId) {
105
+ body.content = [{
106
+ entity: {
107
+ digitalmediaAsset: mediaId
108
+ },
109
+ type: 'IMAGE'
110
+ }];
111
+ }
112
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(inReplyToId)}/comments`;
113
+
114
+ // data-listener -> linkedin_api.client sendComment
115
+ let response;
116
+ let downloadUrl = '';
117
+ try {
118
+ response = await superagent.post(query).timeout(5000).set({
119
+ Authorization: `Bearer ${token}`,
120
+ 'Content-Type': 'application/json',
121
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
122
+ 'LinkedIn-Version': '202311'
123
+ }).send(body);
124
+ loggerInfo(logger, `Native Linkedin API Publish Comment Response`, {
125
+ response: JSON.stringify(response)
126
+ });
127
+ response = {
128
+ status: 200,
129
+ message: JSON.parse(response.text)
130
+ };
131
+ if (mediaId) {
132
+ loggerInfo(logger, `mediaId`, {
133
+ mediaId
134
+ });
135
+ downloadUrl = await getImageMedia(mediaId, token, logger);
136
+ loggerInfo(logger, `downloadUrl`, {
137
+ downloadUrl
138
+ });
139
+ }
140
+ } catch (err) {
141
+ loggerError(logger`Exception in linkedin publish `, err);
142
+ throw err;
143
+ }
144
+ return {
145
+ response,
146
+ downloadUrl
147
+ };
148
+ }
149
+ async function getImageMedia(mediaId, token, logger) {
150
+ let response = {};
151
+ if (mediaId && mediaId.includes('digitalmediaAsset')) {
152
+ mediaId = mediaId.replace('urn:li:digitalmediaAsset:', 'urn:li:image:');
153
+ }
154
+ try {
155
+ response = await superagent.get(LINKEDIN_API + '/images/' + fixedEncodeURIComponent(mediaId)).timeout(5000) // 5 seconds
156
+ .set({
157
+ Authorization: `Bearer ${token}`,
158
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
159
+ 'LinkedIn-Version': '202311'
160
+ });
161
+ } catch (error) {
162
+ loggerError(logger, 'Error in getImageMedia', error);
163
+ }
164
+ let {
165
+ downloadUrl
166
+ } = response.body || {};
167
+ if (downloadUrl) {
168
+ return {
169
+ downloadUrl
170
+ };
171
+ } else {
172
+ loggerError(logger, `Invalid response getting Linkedin media for mediaId: ${mediaId} `, {
173
+ response: JSON.stringify(response)
174
+ });
175
+ return {};
176
+ }
177
+ }
178
+ export async function getVideoMedia(mediaId, token, logger) {
179
+ let response = {};
180
+ let tempValue;
181
+ if (mediaId && mediaId.includes('digitalmediaMediaArtifact')) {
182
+ tempValue = mediaId.split('digitalmediaMediaArtifact:(')[1];
183
+ tempValue = tempValue.split(',urn:li:digitalmediaMediaArtifactClass')[0];
184
+ mediaId = tempValue;
185
+ }
186
+ if (mediaId && mediaId.includes('digitalmediaAsset')) {
187
+ mediaId = mediaId.replace('urn:li:digitalmediaAsset:', 'urn:li:video:');
188
+ }
189
+ try {
190
+ response = await superagent.get(LINKEDIN_API + '/videos/' + fixedEncodeURIComponent(mediaId)).timeout(5000) // 5 seconds
191
+ .set({
192
+ Authorization: `Bearer ${token}`,
193
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
194
+ 'LinkedIn-Version': '202311'
195
+ });
196
+ } catch (error) {
197
+ loggerError(logger, 'Error in getVideoMedia', error);
198
+ }
199
+ let {
200
+ thumbnail,
201
+ downloadUrl
202
+ } = response.body || {};
203
+ if (downloadUrl) {
204
+ return {
205
+ thumbnail,
206
+ downloadUrl
207
+ };
208
+ } else {
209
+ loggerError(logger, `Invalid response getting Linkedin media for mediaId: ${mediaId} `, {
210
+ response: JSON.stringify(response)
211
+ });
212
+ return {};
213
+ }
214
+ }
215
+ export async function getMedia(token, mediaId, type, logger) {
216
+ // needed due to issue with polled data
217
+ if (type.includes('video') && mediaId.includes('image')) {
218
+ type = 'image/jpeg';
219
+ }
220
+ try {
221
+ if (type.includes('image')) {
222
+ const {
223
+ downloadUrl
224
+ } = await getImageMedia(mediaId, token, logger);
225
+ return {
226
+ downloadUrl
227
+ };
228
+ } else if (type.includes('video')) {
229
+ const {
230
+ thumbnail,
231
+ downloadUrl
232
+ } = await getVideoMedia(mediaId, token, logger);
233
+ return {
234
+ thumbnail,
235
+ downloadUrl
236
+ };
237
+ }
238
+ return {};
239
+ } catch (error) {
240
+ loggerError(logger, 'Error in getMedia', error);
241
+ }
242
+ }
243
+ export async function mediaUploadURL(token, socialAccountId, logger) {
244
+ let query = `${LINKEDIN_API}/images?action=initializeUpload`;
245
+ let body = {
246
+ initializeUploadRequest: {
247
+ owner: socialAccountId
248
+ }
249
+ };
250
+ let response;
251
+ try {
252
+ response = await superagent.post(query).timeout(5000).set({
253
+ Authorization: `Bearer ${token}`,
254
+ 'Content-type': 'application/json',
255
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
256
+ 'LinkedIn-Version': '202311'
257
+ }).send(body);
258
+ response = {
259
+ status: 200,
260
+ message: JSON.parse(response.text)
261
+ };
262
+ return response;
263
+ } catch (err) {
264
+ loggerError(logger, `Exception linkedin media register `, err);
265
+ }
266
+ }
267
+ export async function getProfile(urn, token, logger) {
268
+ let [,,, id] = urn.split(':');
269
+ let profile;
270
+ try {
271
+ profile = await superagent.get(`${LINKEDIN_API}/people/(id:${id})`).query({
272
+ projection: '(id,localizedFirstName,localizedLastName,vanityName,profilePicture(displayImage~:playableStreams))'
273
+ }).timeout(5000).set({
274
+ Authorization: `Bearer ${token}`,
275
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
276
+ 'LinkedIn-Version': '202311'
277
+ }).then(result => result.body);
278
+ } catch (error) {
279
+ loggerError(logger, `Failed requesting LinkedIn API for profileId ${urn}`, error);
280
+ return;
281
+ }
282
+ loggerInfo(logger, `Finished requesting LinkedIn API for profileId ${urn}`);
283
+ return profile;
284
+ }
285
+ export async function getOrganization(urn, token, logger) {
286
+ let [,,, id] = urn.split(':');
287
+ let organization;
288
+ try {
289
+ organization = await superagent.get(`${LINKEDIN_API}/organizations/${id}`).query({
290
+ projection: '(id,localizedName,vanityName,logoV2(original~:playableStreams))'
291
+ }).timeout(5000) // 5 seconds
292
+ .set({
293
+ Authorization: `Bearer ${token}`,
294
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
295
+ 'LinkedIn-Version': '202311'
296
+ }).then(result => result.body);
297
+ } catch (error) {
298
+ if (error.response.body.message.includes('ADMIN_ONLY')) {
299
+ return {
300
+ id: 'private'
301
+ };
302
+ }
303
+ loggerError(logger, `Failed requesting LinkedIn API for organizationId ${urn}`, error);
304
+ }
305
+ loggerInfo(logger, `Finished requesting LinkedIn API for organizationId ${urn}`);
306
+ return organization;
307
+ }
308
+ async function getAllStats(externalId, token, logger) {
309
+ let response = {};
310
+ try {
311
+ response = await superagent.get(LINKEDIN_API + '/socialMetadata/' + fixedEncodeURIComponent(externalId)).timeout(5000) // 5 seconds
312
+ .set({
313
+ Authorization: `Bearer ${token}`,
314
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
315
+ 'LinkedIn-Version': '202311'
316
+ });
317
+ } catch (error) {
318
+ loggerError(logger, 'Error in getAllStats', error);
319
+ }
320
+ let {
321
+ commentSummary,
322
+ reactionSummaries
323
+ } = response.body || {};
324
+ if (commentSummary && reactionSummaries) {
325
+ return {
326
+ commentSummary,
327
+ reactionSummaries
328
+ };
329
+ } else {
330
+ loggerError(logger, `Invalid response getting all Linkedin Stats for externalId: ${externalId}`, {
331
+ response: JSON.stringify(response)
332
+ });
333
+ return {};
334
+ }
335
+ }
336
+ export async function getSocialStats(externalId, token, logger) {
337
+ try {
338
+ // get all stats (likes are not to be trusted) seems to only work
339
+ // for og posts.
340
+ const {
341
+ commentSummary,
342
+ reactionSummaries
343
+ } = await getAllStats(externalId, token, logger);
344
+ return {
345
+ commentSummary,
346
+ reactionSummaries
347
+ };
348
+ } catch (error) {
349
+ loggerError(logger, 'Error in getSocialStats', error);
350
+ }
351
+ }
352
+ export async function likedByUser(token, externalId, authorExternalId, logger) {
353
+ let result;
354
+ try {
355
+ result = await superagent.get(`${LINKEDIN_API}/reactions/(actor:${fixedEncodeURIComponent(authorExternalId)},entity:${fixedEncodeURIComponent(externalId)})`).set({
356
+ Authorization: `Bearer ${token}`,
357
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
358
+ 'LinkedIn-Version': '202311'
359
+ }).then(result => result.body);
360
+ } catch (error) {
361
+ loggerError(logger, 'Error in getLikesByUser', error);
362
+ return false;
363
+ }
364
+ return true;
365
+ }
366
+
367
+ // query object is from AssetManager
368
+ export async function uploadMedia(query, logger) {
369
+ const {
370
+ url,
371
+ token,
372
+ data
373
+ } = query;
374
+ return await superagent.put(url).set({
375
+ Accept: '*/*',
376
+ 'Content-Type': 'application/octet-stream',
377
+ Authorization: `Bearer ${token}`,
378
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
379
+ 'LinkedIn-Version': '202311'
380
+ }).send(data).catch(err => {
381
+ loggerError(logger, "Linkedin Error uploading Media", err);
382
+ throw err;
383
+ });
384
+ }
385
+ function fixedEncodeURIComponent(str) {
386
+ return encodeURIComponent(str).replace(/\(/g, '%28').replace(/\)/g, '%29');
387
+ }
@@ -18,6 +18,7 @@ import * as applicationTagFunctions from '../lib/applicationTags.helpers.js';
18
18
  import * as hiddenHelpers from '../lib/hidden.helpers.js';
19
19
  import * as FacebookNative from './http/facebook.native.js';
20
20
  import * as TiktokNative from './http/tiktok.native.js';
21
+ import * as LinkedinNative from './http/linkedin.native.js';
21
22
  const DocumentHelperFunctions = {
22
23
  ...messageHelpers,
23
24
  ...applicationTagFunctions,
@@ -27,4 +28,4 @@ const LinkedInHelpers = {
27
28
  getOrganization,
28
29
  getProfile
29
30
  };
30
- export { FacebookNative, TiktokNative, awsS3Client, assetManagerTvmRepository, CompanyApiClient, CredentialsApiClient, EntitlementsApiClient, FacebookApiClient, FeatureToggleClient, IdentityServicesClient, InstagramApiClient, InstagramVideoClient, IRClient, LinkedInApiClient, TikTokApiClient, MasfClient, WarpZoneApiClient, DocumentHelperFunctions, LinkedInHelpers };
31
+ export { FacebookNative, TiktokNative, LinkedinNative, awsS3Client, assetManagerTvmRepository, CompanyApiClient, CredentialsApiClient, EntitlementsApiClient, FacebookApiClient, FeatureToggleClient, IdentityServicesClient, InstagramApiClient, InstagramVideoClient, IRClient, LinkedInApiClient, TikTokApiClient, MasfClient, WarpZoneApiClient, DocumentHelperFunctions, LinkedInHelpers };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltwater/conversations-api-services",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "description": "Repository to contain all conversations api services shared across our services",
5
5
  "main": "dist/cjs/data-access/index.js",
6
6
  "module": "dist/esm/data-access/index.js",
@@ -0,0 +1,530 @@
1
+
2
+ import superagent from 'superagent';
3
+ import { loggerDebug, loggerError, loggerInfo } from '../../lib/logger.helpers.js';
4
+
5
+
6
+ const LINKEDIN_API = "https://api.linkedin.com/v2";
7
+ const LINKEDIN_API_VERSION = "2.0.0";
8
+
9
+ export async function like(token, externalId, socialAccountId, logger) {
10
+ let response;
11
+ let payload = { actor: socialAccountId, object: externalId };
12
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(externalId)}/likes`
13
+
14
+ try {
15
+ loggerDebug(logger,`Linkedin trying to Like `, {
16
+ query,
17
+ payload: JSON.stringify(payload),
18
+ });
19
+ response = await superagent
20
+ .post(query)
21
+ .timeout(5000) // 5 seconds
22
+ .set({
23
+ Authorization: `Bearer ${token}`,
24
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
25
+ 'LinkedIn-Version': '202311',
26
+ })
27
+ .send(payload);
28
+
29
+
30
+ loggerInfo(logger,
31
+ `Native Linkedin API Like Mutation Response`,
32
+ { response: JSON.stringify(response) }
33
+ );
34
+ } catch (err) {
35
+ loggerError(logger,`Linkedin Like or Unlike Exception`, err);
36
+ throw err;
37
+ }
38
+ return response.connection || response;
39
+ }
40
+
41
+ export async function unlike(token, externalId, socialAccountId, logger) {
42
+ let response;
43
+ let payload = { actor: socialAccountId, object: externalId };
44
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(
45
+ externalId
46
+ )}/likes/${fixedEncodeURIComponent(
47
+ payload.actor
48
+ )}?actor=${fixedEncodeURIComponent(payload.actor)}`;
49
+
50
+ try {
51
+ loggerDebug(logger,`Linkedin trying Delete Previous Like `, {
52
+ query,
53
+ payload: JSON.stringify(payload),
54
+ });
55
+ response = await superagent
56
+ .delete(query)
57
+ .timeout(5000)
58
+ .set({
59
+ Authorization: `Bearer ${token}`,
60
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
61
+ 'LinkedIn-Version': '202311',
62
+ });
63
+
64
+ loggerInfo(logger,
65
+ `Native Linkedin API UNLike Mutation Response`,
66
+ { response: JSON.stringify(response) }
67
+ );
68
+ } catch (err) {
69
+ loggerError(logger,`Linkedin Like or Unlike Exception`, err);
70
+ throw err;
71
+ }
72
+ return response.connection || response;
73
+ }
74
+
75
+ export async function deleteMessage(token, externalId, sourceId, inReplyToId, logger) {
76
+ const [, comment] = externalId.split(',');
77
+ const commentId = comment.split(')')[0];
78
+
79
+ let response;
80
+ const query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(
81
+ inReplyToId
82
+ )}/comments/${commentId}?actor=${fixedEncodeURIComponent(sourceId)}`;
83
+
84
+ try {
85
+
86
+ loggerDebug(logger,`Linkedin trying Delete `, {
87
+ query,
88
+ });
89
+ response = await superagent
90
+ .delete(query)
91
+ .timeout(5000)
92
+ .set({
93
+ Authorization: `Bearer ${token}`,
94
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
95
+ 'LinkedIn-Version': '202311',
96
+ });
97
+
98
+ loggerInfo(logger,
99
+ `Native Linkedin API Delete Comment Response`,
100
+ { response: JSON.stringify(response) }
101
+ );
102
+ } catch (err) {
103
+ loggerError(logger,`Linkedin Delete exception details`, {
104
+ err,
105
+ });
106
+ throw err;
107
+ }
108
+ return response;
109
+ }
110
+
111
+ export async function comment(token, mediaId,inReplyToId,socialAccountId, messageText, logger){
112
+ return publish(token,'qt', mediaId,inReplyToId,socialAccountId, messageText, logger)
113
+ }
114
+ export async function reply(token, mediaId,inReplyToId,socialAccountId, messageText, logger){
115
+ return publish(token,'re', mediaId,inReplyToId,socialAccountId, messageText, logger)
116
+ }
117
+
118
+ async function publish(token,discussionType, mediaId,inReplyToId,socialAccountId, messageText, logger) {
119
+ // 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.
120
+ if (mediaId) {
121
+ mediaId = mediaId.replace(
122
+ 'urn:li:image:',
123
+ 'urn:li:digitalmediaAsset:'
124
+ );
125
+ }
126
+
127
+ let body = {
128
+ actor: socialAccountId,
129
+ message: {
130
+ attributes: [],
131
+ text: messageText,
132
+ },
133
+ };
134
+ if (discussionType === 're') {
135
+ body.parentComment = inReplyToId;
136
+ }
137
+ if (mediaId) {
138
+ body.content = [
139
+ {
140
+ entity: {
141
+ digitalmediaAsset: mediaId,
142
+ },
143
+ type: 'IMAGE',
144
+ },
145
+ ];
146
+ }
147
+
148
+ let query = `${LINKEDIN_API}/socialActions/${fixedEncodeURIComponent(inReplyToId)}/comments`;
149
+
150
+ // data-listener -> linkedin_api.client sendComment
151
+ let response;
152
+ let downloadUrl = '';
153
+ try {
154
+ response = await superagent
155
+ .post(query)
156
+ .timeout(5000)
157
+ .set({
158
+ Authorization: `Bearer ${token}`,
159
+ 'Content-Type': 'application/json',
160
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
161
+ 'LinkedIn-Version': '202311',
162
+ })
163
+ .send(body);
164
+
165
+ loggerInfo(logger,
166
+ `Native Linkedin API Publish Comment Response`,
167
+ { response: JSON.stringify(response) }
168
+ );
169
+ response = {
170
+ status: 200,
171
+ message: JSON.parse(response.text),
172
+ };
173
+
174
+
175
+ if (mediaId) {
176
+ loggerInfo(logger,
177
+ `mediaId`,
178
+ { mediaId }
179
+ );
180
+ downloadUrl = await getImageMedia(
181
+ mediaId,
182
+ token,
183
+ logger
184
+ );
185
+ loggerInfo(logger,
186
+ `downloadUrl`,
187
+ { downloadUrl }
188
+ );
189
+ }
190
+
191
+ } catch (err) {
192
+ loggerError(logger
193
+ `Exception in linkedin publish `,
194
+ err
195
+ );
196
+ throw err;
197
+ }
198
+
199
+ return {response, downloadUrl};
200
+ }
201
+
202
+ async function getImageMedia(mediaId, token, logger) {
203
+ let response = {};
204
+ if (mediaId && mediaId.includes('digitalmediaAsset')) {
205
+ mediaId = mediaId.replace(
206
+ 'urn:li:digitalmediaAsset:',
207
+ 'urn:li:image:'
208
+ );
209
+ }
210
+
211
+ try {
212
+ response = await superagent
213
+ .get(
214
+ LINKEDIN_API +
215
+ '/images/' +
216
+ fixedEncodeURIComponent(mediaId)
217
+ )
218
+ .timeout(5000) // 5 seconds
219
+ .set({
220
+ Authorization: `Bearer ${token}`,
221
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
222
+ 'LinkedIn-Version': '202311',
223
+ });
224
+ } catch (error) {
225
+ loggerError(logger,'Error in getImageMedia', error);
226
+ }
227
+
228
+ let { downloadUrl } = response.body || {};
229
+ if (downloadUrl) {
230
+ return { downloadUrl };
231
+ } else {
232
+ loggerError(logger,
233
+ `Invalid response getting Linkedin media for mediaId: ${mediaId} `,
234
+ { response: JSON.stringify(response) }
235
+ );
236
+ return {};
237
+ }
238
+ }
239
+
240
+ export async function getVideoMedia(mediaId, token, logger) {
241
+
242
+ let response = {};
243
+ let tempValue;
244
+
245
+ if (mediaId && mediaId.includes('digitalmediaMediaArtifact')) {
246
+ tempValue = mediaId.split('digitalmediaMediaArtifact:(')[1];
247
+ tempValue = tempValue.split(
248
+ ',urn:li:digitalmediaMediaArtifactClass'
249
+ )[0];
250
+ mediaId = tempValue;
251
+ }
252
+
253
+ if (mediaId && mediaId.includes('digitalmediaAsset')) {
254
+ mediaId = mediaId.replace(
255
+ 'urn:li:digitalmediaAsset:',
256
+ 'urn:li:video:'
257
+ );
258
+ }
259
+
260
+ try {
261
+ response = await superagent
262
+ .get(
263
+ LINKEDIN_API +
264
+ '/videos/' +
265
+ fixedEncodeURIComponent(mediaId)
266
+ )
267
+ .timeout(5000) // 5 seconds
268
+ .set({
269
+ Authorization: `Bearer ${token}`,
270
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
271
+ 'LinkedIn-Version': '202311',
272
+ });
273
+ } catch (error) {
274
+ loggerError(logger, 'Error in getVideoMedia', error);
275
+ }
276
+
277
+ let { thumbnail, downloadUrl } = response.body || {};
278
+ if (downloadUrl) {
279
+ return { thumbnail, downloadUrl };
280
+ } else {
281
+ loggerError(logger,
282
+ `Invalid response getting Linkedin media for mediaId: ${mediaId} `,
283
+ { response: JSON.stringify(response) }
284
+ );
285
+ return {};
286
+ }
287
+ }
288
+
289
+ export async function getMedia(token, mediaId, type, logger) {
290
+ // needed due to issue with polled data
291
+ if (type.includes('video') && mediaId.includes('image')) {
292
+ type = 'image/jpeg';
293
+ }
294
+
295
+ try {
296
+ if (type.includes('image')) {
297
+ const { downloadUrl } = await getImageMedia(
298
+ mediaId,
299
+ token,
300
+ logger
301
+ );
302
+
303
+ return {
304
+ downloadUrl,
305
+ };
306
+ } else if (type.includes('video')) {
307
+ const { thumbnail, downloadUrl } = await getVideoMedia(
308
+ mediaId,
309
+ token,
310
+ logger
311
+ );
312
+
313
+ return {
314
+ thumbnail,
315
+ downloadUrl,
316
+ };
317
+ }
318
+
319
+ return {};
320
+ } catch (error) {
321
+ loggerError(logger,'Error in getMedia', error);
322
+ }
323
+ }
324
+
325
+ export async function mediaUploadURL(
326
+ token,
327
+ socialAccountId,
328
+ logger
329
+ ) {
330
+ let query = `${LINKEDIN_API}/images?action=initializeUpload`;
331
+ let body = {
332
+ initializeUploadRequest: {
333
+ owner: socialAccountId,
334
+ },
335
+ };
336
+
337
+ let response;
338
+ try {
339
+ response = await superagent
340
+ .post(query)
341
+ .timeout(5000)
342
+ .set({
343
+ Authorization: `Bearer ${token}`,
344
+ 'Content-type': 'application/json',
345
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
346
+ 'LinkedIn-Version': '202311',
347
+ })
348
+ .send(body);
349
+
350
+ response = {
351
+ status: 200,
352
+ message: JSON.parse(response.text),
353
+ };
354
+ return response;
355
+ } catch (err) {
356
+ loggerError(logger,
357
+ `Exception linkedin media register `,
358
+ err
359
+ );
360
+ }
361
+ }
362
+
363
+ export async function getProfile(urn, token, logger) {
364
+ let [, , , id] = urn.split(':');
365
+
366
+ let profile;
367
+ try {
368
+ profile = await superagent
369
+ .get(`${LINKEDIN_API}/people/(id:${id})`)
370
+ .query({
371
+ projection:
372
+ '(id,localizedFirstName,localizedLastName,vanityName,profilePicture(displayImage~:playableStreams))',
373
+ })
374
+ .timeout(5000)
375
+ .set({
376
+ Authorization: `Bearer ${token}`,
377
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
378
+ 'LinkedIn-Version': '202311',
379
+ })
380
+ .then((result) => result.body);
381
+ } catch (error) {
382
+ loggerError(logger,
383
+ `Failed requesting LinkedIn API for profileId ${urn}`,
384
+ error
385
+ );
386
+ return;
387
+ }
388
+
389
+ loggerInfo( logger,
390
+ `Finished requesting LinkedIn API for profileId ${urn}`
391
+ );
392
+
393
+ return profile;
394
+ }
395
+
396
+ export async function getOrganization(urn, token, logger) {
397
+ let [, , , id] = urn.split(':');
398
+
399
+ let organization;
400
+ try {
401
+ organization = await superagent
402
+ .get(`${LINKEDIN_API}/organizations/${id}`)
403
+ .query({
404
+ projection:
405
+ '(id,localizedName,vanityName,logoV2(original~:playableStreams))',
406
+ })
407
+ .timeout(5000) // 5 seconds
408
+ .set({
409
+ Authorization: `Bearer ${token}`,
410
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
411
+ 'LinkedIn-Version': '202311',
412
+ })
413
+ .then((result) => result.body);
414
+ } catch (error) {
415
+ if (error.response.body.message.includes('ADMIN_ONLY')) {
416
+ return { id: 'private' };
417
+ }
418
+
419
+ loggerError(logger,
420
+ `Failed requesting LinkedIn API for organizationId ${urn}`,
421
+ error
422
+ );
423
+ }
424
+
425
+ loggerInfo(logger,
426
+ `Finished requesting LinkedIn API for organizationId ${urn}`
427
+ );
428
+
429
+ return organization;
430
+ }
431
+
432
+ async function getAllStats(externalId, token, logger) {
433
+ let response = {};
434
+ try {
435
+ response = await superagent
436
+ .get(
437
+ LINKEDIN_API +
438
+ '/socialMetadata/' +
439
+ fixedEncodeURIComponent(externalId)
440
+ )
441
+ .timeout(5000) // 5 seconds
442
+ .set({
443
+ Authorization: `Bearer ${token}`,
444
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
445
+ 'LinkedIn-Version': '202311',
446
+ });
447
+ } catch (error) {
448
+ loggerError(logger,'Error in getAllStats', error);
449
+ }
450
+
451
+ let { commentSummary, reactionSummaries } = response.body || {};
452
+ if (commentSummary && reactionSummaries) {
453
+ return { commentSummary, reactionSummaries };
454
+ } else {
455
+ loggerError( logger,
456
+ `Invalid response getting all Linkedin Stats for externalId: ${externalId}`,
457
+ { response: JSON.stringify(response) }
458
+ );
459
+ return {};
460
+ }
461
+ }
462
+
463
+ export async function getSocialStats(externalId, token, logger) {
464
+ try {
465
+ // get all stats (likes are not to be trusted) seems to only work
466
+ // for og posts.
467
+ const {
468
+ commentSummary,
469
+ reactionSummaries,
470
+ } = await getAllStats(externalId, token, logger);
471
+
472
+ return {
473
+ commentSummary,
474
+ reactionSummaries,
475
+ };
476
+ } catch (error) {
477
+ loggerError(logger,'Error in getSocialStats', error);
478
+ }
479
+ }
480
+
481
+ export async function likedByUser(
482
+ token,
483
+ externalId,
484
+ authorExternalId,
485
+ logger
486
+ ) {
487
+ let result;
488
+ try {
489
+ result = await superagent
490
+ .get(
491
+ `${LINKEDIN_API}/reactions/(actor:${fixedEncodeURIComponent(
492
+ authorExternalId
493
+ )},entity:${fixedEncodeURIComponent(externalId)})`
494
+ )
495
+ .set({
496
+ Authorization: `Bearer ${token}`,
497
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
498
+ 'LinkedIn-Version': '202311',
499
+ })
500
+ .then((result) => result.body);
501
+ } catch (error) {
502
+ loggerError(logger,'Error in getLikesByUser', error);
503
+ return false;
504
+ }
505
+ return true;
506
+ }
507
+
508
+ // query object is from AssetManager
509
+ export async function uploadMedia(query, logger) {
510
+ const { url, token, data } = query;
511
+ return await superagent
512
+ .put(url)
513
+ .set({
514
+ Accept: '*/*',
515
+ 'Content-Type': 'application/octet-stream',
516
+ Authorization: `Bearer ${token}`,
517
+ 'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
518
+ 'LinkedIn-Version': '202311',
519
+ })
520
+ .send(data)
521
+ .catch((err) => {
522
+ loggerError(logger,"Linkedin Error uploading Media",err);
523
+ throw err;
524
+ });
525
+ }
526
+
527
+
528
+ function fixedEncodeURIComponent(str) {
529
+ return encodeURIComponent(str).replace(/\(/g, '%28').replace(/\)/g, '%29');
530
+ }
@@ -18,6 +18,7 @@ import * as applicationTagFunctions from '../lib/applicationTags.helpers.js';
18
18
  import * as hiddenHelpers from '../lib/hidden.helpers.js';
19
19
  import * as FacebookNative from './http/facebook.native.js';
20
20
  import * as TiktokNative from './http/tiktok.native.js';
21
+ import * as LinkedinNative from './http/linkedin.native.js';
21
22
 
22
23
  const DocumentHelperFunctions = {
23
24
  ...messageHelpers,
@@ -32,6 +33,7 @@ const LinkedInHelpers = {
32
33
  export {
33
34
  FacebookNative,
34
35
  TiktokNative,
36
+ LinkedinNative,
35
37
  awsS3Client,
36
38
  assetManagerTvmRepository,
37
39
  CompanyApiClient,