@meltwater/conversations-api-services 1.3.2 → 1.4.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.
Files changed (28) hide show
  1. package/.github/workflows/release.yml +4 -6
  2. package/CLAUDE.md +70 -0
  3. package/dist/cjs/data-access/http/facebook.native.js +122 -106
  4. package/dist/cjs/data-access/http/instagram.native.js +92 -49
  5. package/dist/cjs/data-access/http/linkedin.native.js +135 -56
  6. package/dist/cjs/data-access/http/threads.native.js +53 -18
  7. package/dist/cjs/data-access/http/tiktok.native.js +18 -12
  8. package/dist/cjs/data-access/http/twitter.native.js +142 -40
  9. package/dist/cjs/data-access/http/youtube.native.js +19 -13
  10. package/dist/esm/data-access/http/facebook.native.js +123 -107
  11. package/dist/esm/data-access/http/instagram.native.js +93 -50
  12. package/dist/esm/data-access/http/linkedin.native.js +136 -57
  13. package/dist/esm/data-access/http/threads.native.js +54 -19
  14. package/dist/esm/data-access/http/tiktok.native.js +19 -13
  15. package/dist/esm/data-access/http/twitter.native.js +143 -41
  16. package/dist/esm/data-access/http/youtube.native.js +20 -14
  17. package/package.json +1 -3
  18. package/src/data-access/http/README.md +50 -0
  19. package/src/data-access/http/facebook.native.js +122 -144
  20. package/src/data-access/http/instagram.native.js +113 -93
  21. package/src/data-access/http/linkedin.native.js +144 -83
  22. package/src/data-access/http/threads.native.js +58 -27
  23. package/src/data-access/http/tiktok.native.js +19 -39
  24. package/src/data-access/http/twitter.native.js +145 -65
  25. package/src/data-access/http/youtube.native.js +21 -40
  26. package/dist/cjs/lib/hiddenComment.helper.js +0 -119
  27. package/dist/esm/lib/hiddenComment.helper.js +0 -112
  28. package/src/lib/hiddenComment.helper.js +0 -107
@@ -1,5 +1,5 @@
1
1
  import superagent from 'superagent';
2
- import { loggerDebug, loggerError, loggerInfo } from '../../lib/logger.helpers.js';
2
+ import { loggerDebug, loggerInfo, MeltwaterAttributes } from '../../lib/logger.helpers.js';
3
3
  const LINKEDIN_API = "https://api.linkedin.com/v2";
4
4
  const LINKEDIN_API_REST = "https://api.linkedin.com/rest";
5
5
  const LINKEDIN_API_VERSION = "2.0.0";
@@ -26,7 +26,13 @@ export async function like(token, externalId, socialAccountId, logger) {
26
26
  response: JSON.stringify(response)
27
27
  });
28
28
  } catch (err) {
29
- loggerError(logger, `Linkedin Like or Unlike Exception`, err);
29
+ loggerInfo(logger, `Failed to call linkedin api - like`, {
30
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
31
+ externalId,
32
+ socialAccountId,
33
+ error: err?.message
34
+ })
35
+ });
30
36
  throw err;
31
37
  }
32
38
  return response.connection || response;
@@ -52,7 +58,13 @@ export async function unlike(token, externalId, socialAccountId, logger) {
52
58
  response: JSON.stringify(response)
53
59
  });
54
60
  } catch (err) {
55
- loggerError(logger, `Linkedin Like or Unlike Exception`, err);
61
+ loggerInfo(logger, `Failed to call linkedin api - unlike`, {
62
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
63
+ externalId,
64
+ socialAccountId,
65
+ error: err?.message
66
+ })
67
+ });
56
68
  throw err;
57
69
  }
58
70
  return response.connection || response;
@@ -75,8 +87,14 @@ export async function deleteMessage(token, externalId, sourceId, inReplyToId, lo
75
87
  response: JSON.stringify(response)
76
88
  });
77
89
  } catch (err) {
78
- loggerError(logger, `Linkedin Delete exception details`, {
79
- err
90
+ loggerInfo(logger, `Failed to call linkedin api - deleteMessage`, {
91
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
92
+ externalId,
93
+ commentId,
94
+ inReplyToId,
95
+ sourceId,
96
+ error: err?.message
97
+ })
80
98
  });
81
99
  throw err;
82
100
  }
@@ -202,7 +220,14 @@ async function publish(token, discussionType, mediaId, inReplyToId, socialAccoun
202
220
  });
203
221
  }
204
222
  } catch (err) {
205
- loggerError(logger, `Exception in linkedin publish `, err);
223
+ loggerInfo(logger, `Failed to call linkedin api - publish`, {
224
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
225
+ inReplyToId,
226
+ discussionType,
227
+ socialAccountId,
228
+ error: err?.response?.body?.message ?? err?.message
229
+ })
230
+ });
206
231
  throw err;
207
232
  }
208
233
  return {
@@ -223,7 +248,13 @@ async function getImageMedia(mediaId, token, logger) {
223
248
  'LinkedIn-Version': LINKEDIN_VERSION
224
249
  });
225
250
  } catch (error) {
226
- loggerError(logger, 'Error in getImageMedia', error);
251
+ loggerInfo(logger, `Failed to call linkedin api - getImageMedia`, {
252
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
253
+ mediaId,
254
+ error: error?.message
255
+ })
256
+ });
257
+ throw error;
227
258
  }
228
259
  let {
229
260
  downloadUrl
@@ -233,8 +264,11 @@ async function getImageMedia(mediaId, token, logger) {
233
264
  downloadUrl
234
265
  };
235
266
  } else {
236
- loggerError(logger, `Invalid response getting Linkedin media for mediaId: ${mediaId} `, {
237
- response: JSON.stringify(response)
267
+ loggerInfo(logger, `Failed to call linkedin api - getImageMedia bad status`, {
268
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
269
+ mediaId,
270
+ responseBody: response.body
271
+ })
238
272
  });
239
273
  return {};
240
274
  }
@@ -258,7 +292,13 @@ export async function getVideoMedia(mediaId, token, logger) {
258
292
  'LinkedIn-Version': LINKEDIN_VERSION
259
293
  });
260
294
  } catch (error) {
261
- loggerError(logger, 'Error in getVideoMedia', error);
295
+ loggerInfo(logger, `Failed to call linkedin api - getVideoMedia`, {
296
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
297
+ mediaId,
298
+ error: error?.message
299
+ })
300
+ });
301
+ throw error;
262
302
  }
263
303
  let {
264
304
  thumbnail,
@@ -270,8 +310,11 @@ export async function getVideoMedia(mediaId, token, logger) {
270
310
  downloadUrl
271
311
  };
272
312
  } else {
273
- loggerError(logger, `Invalid response getting Linkedin media for mediaId: ${mediaId} `, {
274
- response: JSON.stringify(response)
313
+ loggerInfo(logger, `Failed to call linkedin api - getVideoMedia bad status`, {
314
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
315
+ mediaId,
316
+ responseBody: response.body
317
+ })
275
318
  });
276
319
  return {};
277
320
  }
@@ -281,28 +324,24 @@ export async function getMedia(token, mediaId, type, logger) {
281
324
  if (type.includes('video') && mediaId.includes('image')) {
282
325
  type = 'image/jpeg';
283
326
  }
284
- try {
285
- if (type.includes('image')) {
286
- const {
287
- downloadUrl
288
- } = await getImageMedia(mediaId, token, logger);
289
- return {
290
- downloadUrl
291
- };
292
- } else if (type.includes('video')) {
293
- const {
294
- thumbnail,
295
- downloadUrl
296
- } = await getVideoMedia(mediaId, token, logger);
297
- return {
298
- thumbnail,
299
- downloadUrl
300
- };
301
- }
302
- return {};
303
- } catch (error) {
304
- loggerError(logger, 'Error in getMedia', error);
327
+ if (type.includes('image')) {
328
+ const {
329
+ downloadUrl
330
+ } = await getImageMedia(mediaId, token, logger);
331
+ return {
332
+ downloadUrl
333
+ };
334
+ } else if (type.includes('video')) {
335
+ const {
336
+ thumbnail,
337
+ downloadUrl
338
+ } = await getVideoMedia(mediaId, token, logger);
339
+ return {
340
+ thumbnail,
341
+ downloadUrl
342
+ };
305
343
  }
344
+ return {};
306
345
  }
307
346
  export async function mediaUploadURL(token, socialAccountId, logger) {
308
347
  let query = `${LINKEDIN_API}/images?action=initializeUpload`;
@@ -325,7 +364,13 @@ export async function mediaUploadURL(token, socialAccountId, logger) {
325
364
  };
326
365
  return response;
327
366
  } catch (err) {
328
- loggerError(logger, `Exception linkedin media register `, err);
367
+ loggerInfo(logger, `Failed to call linkedin api - mediaUploadURL`, {
368
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
369
+ socialAccountId,
370
+ error: err?.response?.body?.message ?? err?.message
371
+ })
372
+ });
373
+ throw err;
329
374
  }
330
375
  }
331
376
  export async function getProfile(urn, token, logger) {
@@ -340,8 +385,13 @@ export async function getProfile(urn, token, logger) {
340
385
  'LinkedIn-Version': LINKEDIN_VERSION
341
386
  }).then(result => result.body);
342
387
  } catch (error) {
343
- loggerError(logger, `Failed requesting LinkedIn API for profileId ${urn}`, error);
344
- return;
388
+ loggerInfo(logger, `Failed to call linkedin api - getProfile`, {
389
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
390
+ urn,
391
+ error: error?.message
392
+ })
393
+ });
394
+ throw error;
345
395
  }
346
396
  loggerInfo(logger, `Finished requesting LinkedIn API for profileId ${urn}`);
347
397
  return profile;
@@ -368,7 +418,13 @@ export async function getOrganization(urn, token, logger) {
368
418
  id: 'private'
369
419
  };
370
420
  }
371
- loggerError(logger, `Failed requesting LinkedIn API for organizationId ${urn}`, error);
421
+ loggerInfo(logger, `Failed to call linkedin api - getOrganization`, {
422
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
423
+ urn,
424
+ error: error?.message
425
+ })
426
+ });
427
+ throw error;
372
428
  }
373
429
  loggerInfo(logger, `Finished requesting LinkedIn API for organizationId ${urn}`);
374
430
  return organization;
@@ -382,7 +438,13 @@ export async function getMediaFromUrl(token, url, logger) {
382
438
  });
383
439
  return response;
384
440
  } catch (error) {
385
- loggerError(logger, 'Error in getMediaFromUrl', error);
441
+ loggerInfo(logger, `Failed to call linkedin api - getMediaFromUrl`, {
442
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
443
+ url,
444
+ error: error?.message
445
+ })
446
+ });
447
+ throw error;
386
448
  }
387
449
  }
388
450
  export async function getOrganizations(urn, token, logger) {
@@ -399,7 +461,14 @@ export async function getOrganizations(urn, token, logger) {
399
461
  return res.results[id];
400
462
  });
401
463
  } catch (error) {
402
- loggerError(logger, `Failed requesting LinkedIn API for organization id ${id}`, error);
464
+ loggerInfo(logger, `Failed to call linkedin api - getOrganizations`, {
465
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
466
+ urn,
467
+ id,
468
+ error: error?.message
469
+ })
470
+ });
471
+ throw error;
403
472
  }
404
473
  loggerInfo(logger, `Finished requesting LinkedIn API for organization id ${id}`);
405
474
  return organizations;
@@ -414,7 +483,13 @@ async function getAllStats(externalId, token, logger) {
414
483
  'LinkedIn-Version': LINKEDIN_VERSION
415
484
  });
416
485
  } catch (error) {
417
- loggerError(logger, 'Error in getAllStats', error);
486
+ loggerInfo(logger, `Failed to call linkedin api - getAllStats`, {
487
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
488
+ externalId,
489
+ error: error?.message
490
+ })
491
+ });
492
+ throw error;
418
493
  }
419
494
  let {
420
495
  commentSummary,
@@ -426,27 +501,26 @@ async function getAllStats(externalId, token, logger) {
426
501
  reactionSummaries
427
502
  };
428
503
  } else {
429
- loggerError(logger, `Invalid response getting all Linkedin Stats for externalId: ${externalId}`, {
430
- response: JSON.stringify(response)
504
+ loggerInfo(logger, `Failed to call linkedin api - getAllStats bad status`, {
505
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
506
+ externalId,
507
+ responseBody: response.body
508
+ })
431
509
  });
432
510
  return {};
433
511
  }
434
512
  }
435
513
  export async function getSocialStats(externalId, token, logger) {
436
- try {
437
- // get all stats (likes are not to be trusted) seems to only work
438
- // for og posts.
439
- const {
440
- commentSummary,
441
- reactionSummaries
442
- } = await getAllStats(externalId, token, logger);
443
- return {
444
- commentSummary,
445
- reactionSummaries
446
- };
447
- } catch (error) {
448
- loggerError(logger, 'Error in getSocialStats', error);
449
- }
514
+ // get all stats (likes are not to be trusted) seems to only work
515
+ // for og posts.
516
+ const {
517
+ commentSummary,
518
+ reactionSummaries
519
+ } = await getAllStats(externalId, token, logger);
520
+ return {
521
+ commentSummary,
522
+ reactionSummaries
523
+ };
450
524
  }
451
525
  export async function likedByUser(token, externalId, authorExternalId, logger) {
452
526
  let result;
@@ -477,7 +551,12 @@ export async function uploadMedia(query, logger) {
477
551
  'X-RestLi-Protocol-Version': LINKEDIN_API_VERSION,
478
552
  'LinkedIn-Version': LINKEDIN_VERSION
479
553
  }).send(data).catch(err => {
480
- loggerError(logger, "Linkedin Error uploading Media", err);
554
+ loggerInfo(logger, `Failed to call linkedin api - uploadMedia`, {
555
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
556
+ url,
557
+ error: err?.message
558
+ })
559
+ });
481
560
  throw err;
482
561
  });
483
562
  }
@@ -1,6 +1,6 @@
1
1
  import superagent from 'superagent';
2
2
  import { removePrefix } from '../../lib/externalId.helpers.js';
3
- import { loggerDebug, loggerError, loggerInfo } from '../../lib/logger.helpers.js';
3
+ import { loggerDebug, loggerInfo, MeltwaterAttributes } from '../../lib/logger.helpers.js';
4
4
  const THREADS_URL = 'https://graph.threads.net/v1.0';
5
5
  const THREADS_PUBLISH_URL = `${THREADS_URL}/me/threads_publish`;
6
6
  const MAX_RETRY_COUNT = 11;
@@ -55,7 +55,12 @@ async function confirmCreationId(accessToken, creationId, logger) {
55
55
  try {
56
56
  return await retryWithBackoff(operation, MAX_RETRY_COUNT, SHORT_WAIT_TIME_MS, logger);
57
57
  } catch (error) {
58
- loggerError(logger, `Creation ID ${creationId} confirmation failed: ${error.message}`, error);
58
+ loggerInfo(logger, `Failed to call threads api - confirmCreationId`, {
59
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
60
+ creationId,
61
+ error: error?.message
62
+ })
63
+ });
59
64
  error.code = 408; // Request Timeout
60
65
  throw error;
61
66
  }
@@ -70,18 +75,30 @@ async function confirmCreationId(accessToken, creationId, logger) {
70
75
  * @throws {Error} If the request fails.
71
76
  */
72
77
  async function sendRequest(apiUrl, params, logger) {
78
+ let response;
73
79
  try {
74
- const response = await superagent.get(apiUrl).set('Accept', 'application/json').set('Content-Type', 'application/json').query(params).send();
75
- if (response.status !== 200) {
76
- throw new Error(`Unexpected response status: ${response.status}`);
77
- }
78
- loggerDebug(logger, 'Threads Response status', response.status);
79
- return response;
80
+ response = await superagent.get(apiUrl).set('Accept', 'application/json').set('Content-Type', 'application/json').query(params).send();
80
81
  } catch (err) {
81
- const errorMessage = err?.response?.body?.error?.message || err.message;
82
- loggerError(logger, `Failed to call Threads API: ${errorMessage}`, err);
82
+ loggerInfo(logger, `Failed to call threads api - sendRequest`, {
83
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
84
+ apiUrl,
85
+ error: err?.response?.body?.error?.message ?? err?.message
86
+ })
87
+ });
83
88
  throw err;
84
89
  }
90
+ if (response.status !== 200) {
91
+ loggerInfo(logger, `Failed to call threads api - sendRequest bad status`, {
92
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
93
+ apiUrl,
94
+ status: response.status,
95
+ responseBody: response.body
96
+ })
97
+ });
98
+ throw new Error(`Unexpected response status: ${response.status}`);
99
+ }
100
+ loggerDebug(logger, 'Threads Response status', response.status);
101
+ return response;
85
102
  }
86
103
 
87
104
  /**
@@ -93,18 +110,30 @@ async function sendRequest(apiUrl, params, logger) {
93
110
  * @throws {Error} If the request fails.
94
111
  */
95
112
  async function requestApi(apiUrl, params, logger) {
113
+ let response;
96
114
  try {
97
- const response = await superagent.post(apiUrl).set('Accept', 'application/json').set('Content-Type', 'application/json').query(params).send();
98
- if (response.status !== 200) {
99
- throw new Error(`Unexpected response status: ${response.status}`);
100
- }
101
- loggerDebug(logger, 'Threads Response status', response.status);
102
- return response;
115
+ response = await superagent.post(apiUrl).set('Accept', 'application/json').set('Content-Type', 'application/json').query(params).send();
103
116
  } catch (err) {
104
- const errorMessage = err?.response?.body?.error?.message || err.message;
105
- loggerError(logger, `Failed to call Threads API: ${errorMessage}`, err);
117
+ loggerInfo(logger, `Failed to call threads api - requestApi`, {
118
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
119
+ apiUrl,
120
+ error: err?.response?.body?.error?.message ?? err?.message
121
+ })
122
+ });
106
123
  throw err;
107
124
  }
125
+ if (response.status !== 200) {
126
+ loggerInfo(logger, `Failed to call threads api - requestApi bad status`, {
127
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
128
+ apiUrl,
129
+ status: response.status,
130
+ responseBody: response.body
131
+ })
132
+ });
133
+ throw new Error(`Unexpected response status: ${response.status}`);
134
+ }
135
+ loggerDebug(logger, 'Threads Response status', response.status);
136
+ return response;
108
137
  }
109
138
 
110
139
  /**
@@ -272,7 +301,13 @@ async function constructReplyQuery(accessToken, inReplyToId, text, asset, logger
272
301
  creation_id: containerResponse.body.id
273
302
  };
274
303
  } catch (error) {
275
- loggerError(logger, 'Error constructing reply query', error);
304
+ loggerInfo(logger, `Failed to call threads api - constructReplyQuery`, {
305
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
306
+ inReplyToId,
307
+ isQuote,
308
+ error: error?.message
309
+ })
310
+ });
276
311
  throw error;
277
312
  }
278
313
  }
@@ -1,5 +1,5 @@
1
1
  import superagent from 'superagent';
2
- import { loggerError, MeltwaterAttributes } from '../../lib/logger.helpers.js';
2
+ import { loggerInfo, MeltwaterAttributes } from '../../lib/logger.helpers.js';
3
3
  import { removePrefix } from '../../lib/externalId.helpers.js';
4
4
  const TIKTOK_API_URL = 'https://business-api.tiktok.com/open_api/v1.3/business/';
5
5
  export async function comment(token, business_id, video_id, text, logger) {
@@ -168,16 +168,21 @@ async function sendRequest(token) {
168
168
  try {
169
169
  response = await superagent.get(TIKTOK_API_URL + paramString).set('Accept', 'application/json').set('Content-Type', 'application/json').set('Access-Token', token).send();
170
170
  } catch (err) {
171
- if (err && err.response && err.response.body && err.response.body.error) {
172
- loggerError(logger, `Failed to call tiktok api for paramString ${paramString}: ${err.response.body.error.message}`);
173
- } else {
174
- loggerError(logger, `Failed to call tiktok api for paramString ${paramString}`, err);
175
- }
171
+ loggerInfo(logger, 'Failed to call tiktok api - sendRequest', {
172
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
173
+ paramString,
174
+ error: err?.response?.body?.error?.message ?? err?.message
175
+ })
176
+ });
176
177
  throw err;
177
178
  }
178
179
  if (response.status !== 200) {
179
- loggerError(logger, `Failed to call tiktok api`, {
180
- [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify(response.body)
180
+ loggerInfo(logger, 'Failed to call tiktok api - sendRequest', {
181
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
182
+ paramString,
183
+ status: response.status,
184
+ body: response.body
185
+ })
181
186
  });
182
187
  let error = new Error(`Failed to call tiktok api`);
183
188
  error.code = response.status;
@@ -193,11 +198,12 @@ async function sendPost(token) {
193
198
  try {
194
199
  response = await superagent.post(TIKTOK_API_URL + paramString).set('Accept', 'application/json').set('Content-Type', 'application/json').set('Access-Token', token).send(postData);
195
200
  } catch (err) {
196
- if (err && err.response && err.response.body && err.response.body.error) {
197
- loggerError(logger, `Failed to call tiktok api for paramString ${paramString}: ${err.response.body.error.message}`);
198
- } else {
199
- loggerError(logger, `Failed to call tiktok api for paramString ${paramString}`, err);
200
- }
201
+ loggerInfo(logger, 'Failed to call tiktok api - sendPost', {
202
+ [MeltwaterAttributes.PAYLOADDATA]: JSON.stringify({
203
+ paramString,
204
+ error: err?.response?.body?.error?.message ?? err?.message
205
+ })
206
+ });
201
207
  throw err;
202
208
  }
203
209
  return response;