@meltwater/conversations-api-services 1.0.24 → 1.0.26
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/dist/cjs/data-access/http/instagram.native.js +286 -0
- package/dist/cjs/data-access/http/twitter.native.js +584 -0
- package/dist/cjs/data-access/index.js +6 -1
- package/dist/esm/data-access/http/instagram.native.js +271 -0
- package/dist/esm/data-access/http/twitter.native.js +561 -0
- package/dist/esm/data-access/index.js +3 -1
- package/package.json +4 -1
- package/src/data-access/http/instagram.native.js +477 -0
- package/src/data-access/http/twitter.native.js +663 -0
- package/src/data-access/index.js +4 -0
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import { removePrefix } from '../../lib/externalId.helpers.js';
|
|
3
|
+
import { loggerDebug, loggerError, loggerInfo } from '../../lib/logger.helpers.js';
|
|
4
|
+
import OAuth from 'oauth-1.0a';
|
|
5
|
+
import crypto from 'crypto';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import Twit from '@meltwater/social-twit';
|
|
9
|
+
export function addOAuthToToken(token, TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET) {
|
|
10
|
+
if (!token.oauth) {
|
|
11
|
+
token.oauth = OAuth({
|
|
12
|
+
consumer: {
|
|
13
|
+
key: TWITTER_CONSUMER_KEY,
|
|
14
|
+
secret: TWITTER_CONSUMER_SECRET
|
|
15
|
+
},
|
|
16
|
+
signature_method: 'HMAC-SHA1',
|
|
17
|
+
hash_function(base_string, key) {
|
|
18
|
+
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
token.consumer_key = TWITTER_CONSUMER_KEY;
|
|
22
|
+
token.consumer_secret = TWITTER_CONSUMER_SECRET;
|
|
23
|
+
}
|
|
24
|
+
return token;
|
|
25
|
+
}
|
|
26
|
+
export async function getUserInfoFromHandles(token, handles, logger) {
|
|
27
|
+
try {
|
|
28
|
+
let query = `users/lookup.json?screen_name=${handles.join(',')}`;
|
|
29
|
+
let result = await postRequest({
|
|
30
|
+
token,
|
|
31
|
+
uri: query,
|
|
32
|
+
logger
|
|
33
|
+
});
|
|
34
|
+
return result.data;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
loggerError(logger, `Error getting UserInfo`, e);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function getUserInfoFromHandle(token, handleId, logger) {
|
|
40
|
+
try {
|
|
41
|
+
let query = `users/show.json?user_id=${removePrefix(handleId)}`;
|
|
42
|
+
let result = await getRequest({
|
|
43
|
+
token,
|
|
44
|
+
uri: query,
|
|
45
|
+
logger
|
|
46
|
+
});
|
|
47
|
+
return result.data;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
loggerError(logger, `Error getting UserInfo`, e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function getDirectMessageImage(token, imageUrl, logger) {
|
|
53
|
+
try {
|
|
54
|
+
let result = await getRequest({
|
|
55
|
+
token,
|
|
56
|
+
uri: imageUrl,
|
|
57
|
+
attachUrlPrefix: false,
|
|
58
|
+
logger
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
image: result.data,
|
|
62
|
+
contentType: result.contentType
|
|
63
|
+
};
|
|
64
|
+
} catch (e) {
|
|
65
|
+
loggerError(logger, `Error getting image`, e);
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function getCurrentInfo(token, externalId, logger) {
|
|
70
|
+
try {
|
|
71
|
+
const uri = `https://api.twitter.com/2/tweets?ids=${removePrefix(externalId)}&tweet.fields=public_metrics&expansions=author_id`;
|
|
72
|
+
let result = await getRequest({
|
|
73
|
+
token,
|
|
74
|
+
uri,
|
|
75
|
+
attachUrlPrefix: false,
|
|
76
|
+
convertPayloadToUri: false,
|
|
77
|
+
logger
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// this makes it so 'retweeted' is only true on a retweet that isn't a comment
|
|
81
|
+
// sometimes 'retweeted' is true if user retweeted with comment, sometimes not :sadness:
|
|
82
|
+
result.data.retweeted = !!result.data.current_user_retweet;
|
|
83
|
+
return result.data.data[0].public_metrics;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
loggerDebug(logger, 'Error in twitter getCurrentInfo', error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function getMentionHandleInfo(token, externalId, logger) {
|
|
89
|
+
try {
|
|
90
|
+
const uri = `https://api.twitter.com/2/tweets/${removePrefix(externalId)}?tweet.fields=entities&expansions=entities.mentions.username,author_id&user.fields=id,name,username,profile_image_url`;
|
|
91
|
+
let result = await getRequest({
|
|
92
|
+
token,
|
|
93
|
+
uri,
|
|
94
|
+
attachUrlPrefix: false,
|
|
95
|
+
convertPayloadToUri: false
|
|
96
|
+
});
|
|
97
|
+
return result.data;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
loggerDebug(logger, 'Error in twitter getMentionHandleInfo', error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export async function retweet(token, sourceId, externalId, logger) {
|
|
103
|
+
try {
|
|
104
|
+
let response = await postRequest({
|
|
105
|
+
token,
|
|
106
|
+
uri: `https://api.twitter.com/2/users/${removePrefix(sourceId)}/retweets`,
|
|
107
|
+
payload: {
|
|
108
|
+
tweet_id: removePrefix(externalId)
|
|
109
|
+
},
|
|
110
|
+
attachUrlPrefix: false,
|
|
111
|
+
convertPayloadToUri: false,
|
|
112
|
+
logger
|
|
113
|
+
});
|
|
114
|
+
if (response.data.data.retweeted !== undefined && response.resp.statusCode === 200) {
|
|
115
|
+
return true;
|
|
116
|
+
} else {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
loggerDebug(logger, `Error in retweet user: ${sourceId}`, error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export async function unRetweet(token, sourceId, externalId, logger) {
|
|
124
|
+
try {
|
|
125
|
+
let response = await deleteRequest({
|
|
126
|
+
token,
|
|
127
|
+
uri: `https://api.twitter.com/2/users/${sourceId}/retweets/${removePrefix(externalId)}`,
|
|
128
|
+
logger
|
|
129
|
+
});
|
|
130
|
+
if (response.data.data.retweeted !== undefined && response.resp.statusCode === 200) {
|
|
131
|
+
return true;
|
|
132
|
+
} else {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
loggerDebug(logger, `Error in unretweet user: ${sourceId}`, error, {
|
|
137
|
+
[MeltwaterAttributes.SOCIALEXTERNALID]: externalId
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export async function like(token, externalId, logger) {
|
|
142
|
+
try {
|
|
143
|
+
let response = await postRequest({
|
|
144
|
+
token,
|
|
145
|
+
uri: 'favorites/create.json?id=' + removePrefix(externalId),
|
|
146
|
+
logger
|
|
147
|
+
});
|
|
148
|
+
if (response.data.favorited !== undefined && response.resp.statusCode === 200) {
|
|
149
|
+
return true;
|
|
150
|
+
} else {
|
|
151
|
+
loggerInfo(logger, 'Twitter Error in like user statusCode non 200 ', {
|
|
152
|
+
response: JSON.stringify(response)
|
|
153
|
+
});
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
loggerDebug(logger, `Twitter Error in like user: ${externalId}`, error);
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export async function unLike(token, externalId, logger) {
|
|
162
|
+
try {
|
|
163
|
+
let response = await postRequest({
|
|
164
|
+
token,
|
|
165
|
+
uri: 'favorites/destroy.json?id=' + removePrefix(externalId),
|
|
166
|
+
logger
|
|
167
|
+
});
|
|
168
|
+
if (response.data.favorited !== undefined && response.resp.statusCode === 200) {
|
|
169
|
+
return true;
|
|
170
|
+
} else {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
loggerDebug(logger, `Error in unlike user: ${externalId}`, error);
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export async function followUser(token, profileId, logger) {
|
|
179
|
+
try {
|
|
180
|
+
let response = await postRequest({
|
|
181
|
+
token,
|
|
182
|
+
uri: 'friendships/create.json',
|
|
183
|
+
payload: {
|
|
184
|
+
user_id: removePrefix(profileId),
|
|
185
|
+
follow: true
|
|
186
|
+
},
|
|
187
|
+
logger
|
|
188
|
+
});
|
|
189
|
+
if (response.data.following !== undefined) {
|
|
190
|
+
return true;
|
|
191
|
+
} else {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
loggerDebug(logger, `Error in following user: ${profileId}`, error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
export async function unFollowUser(token, profileId, logger) {
|
|
199
|
+
try {
|
|
200
|
+
let response = await postRequest({
|
|
201
|
+
token,
|
|
202
|
+
uri: 'friendships/destroy.json',
|
|
203
|
+
payload: {
|
|
204
|
+
user_id: removePrefix(profileId)
|
|
205
|
+
},
|
|
206
|
+
logger
|
|
207
|
+
});
|
|
208
|
+
if (response.data.following !== undefined) {
|
|
209
|
+
return true;
|
|
210
|
+
} else {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
} catch (error) {
|
|
214
|
+
loggerDebug(logger, `Error in unfollowing user: ${profileId}`, error);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
export async function userFollowStatus(token, profileId, logger) {
|
|
218
|
+
try {
|
|
219
|
+
let response = await getRequest({
|
|
220
|
+
token,
|
|
221
|
+
uri: 'friendships/lookup.json',
|
|
222
|
+
payload: {
|
|
223
|
+
user_id: removePrefix(profileId)
|
|
224
|
+
},
|
|
225
|
+
logger
|
|
226
|
+
});
|
|
227
|
+
if (response.data.length > 0) {
|
|
228
|
+
if (response.data[0].connections.length > 0) {
|
|
229
|
+
for (var i = 0; i < response.data[0].connections.length; i++) {
|
|
230
|
+
if (response.data[0].connections[i] === 'following') {
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
} else {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
loggerDebug(logger, `Error in getting user follow status: ${profileId}`, error);
|
|
241
|
+
if (error.message != 'Bad Request') {
|
|
242
|
+
// if we hit a minor rate limit here, we wait for 5 seconds before attempting again, usually long enough to continue
|
|
243
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
244
|
+
return userFollowStatus(token, profileId, logger);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
export async function getBrandUserRelationship(token, profileId, brandProfileId, logger) {
|
|
249
|
+
try {
|
|
250
|
+
let {
|
|
251
|
+
data: {
|
|
252
|
+
relationship
|
|
253
|
+
} = {}
|
|
254
|
+
} = await getRequest({
|
|
255
|
+
token,
|
|
256
|
+
uri: `friendships/show.json?source_id=${brandProfileId}&target_id=${removePrefix(profileId)}`,
|
|
257
|
+
logger
|
|
258
|
+
});
|
|
259
|
+
return relationship;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
loggerDebug(logger, `Error in getting user brand friendship info: ${profileId} and ${brandProfileId}`, error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
export async function reply(token, text, attachment, inReplyToId, removeInReplyToId, logger) {
|
|
265
|
+
let mediaId;
|
|
266
|
+
if (attachment) {
|
|
267
|
+
// discussionType only matters if DM or Not
|
|
268
|
+
mediaId = await uploadMedia(attachment, token, 're', logger);
|
|
269
|
+
}
|
|
270
|
+
const payload = {
|
|
271
|
+
text,
|
|
272
|
+
...(attachment && {
|
|
273
|
+
media: {
|
|
274
|
+
media_ids: [mediaId]
|
|
275
|
+
}
|
|
276
|
+
}),
|
|
277
|
+
reply: {
|
|
278
|
+
in_reply_to_tweet_id: removePrefix(inReplyToId)
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
if (removeInReplyToId.length) {
|
|
282
|
+
payload.reply.exclude_reply_user_ids = removeInReplyToId.map(removePrefix);
|
|
283
|
+
}
|
|
284
|
+
let query = 'https://api.twitter.com/2/tweets';
|
|
285
|
+
return publishTweet(token, payload, query, false, logger);
|
|
286
|
+
}
|
|
287
|
+
export async function privateMessage(token, text, attachment, profileId, logger) {
|
|
288
|
+
let query = 'direct_messages/events/new.json';
|
|
289
|
+
let mediaId;
|
|
290
|
+
if (attachment) {
|
|
291
|
+
// discussionType only matters if DM or Not
|
|
292
|
+
mediaId = await uploadMedia(attachment, token, 'dm', logger);
|
|
293
|
+
}
|
|
294
|
+
const payload = {
|
|
295
|
+
event: {
|
|
296
|
+
type: 'message_create',
|
|
297
|
+
message_create: {
|
|
298
|
+
target: {
|
|
299
|
+
recipient_id: removePrefix(profileId)
|
|
300
|
+
},
|
|
301
|
+
message_data: {
|
|
302
|
+
text: text || ' ',
|
|
303
|
+
...(attachment && {
|
|
304
|
+
attachment: {
|
|
305
|
+
type: 'media',
|
|
306
|
+
media: {
|
|
307
|
+
id: mediaId
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
return publishTweet(token, payload, query, true, logger);
|
|
316
|
+
}
|
|
317
|
+
export async function retweetWithComment(token, text, attachment, logger) {
|
|
318
|
+
let mediaId;
|
|
319
|
+
if (attachment) {
|
|
320
|
+
// discussionType only matters if DM or Not
|
|
321
|
+
mediaId = await uploadMedia(attachment, token, 're', logger);
|
|
322
|
+
}
|
|
323
|
+
const payload = {
|
|
324
|
+
text,
|
|
325
|
+
...(attachment && {
|
|
326
|
+
media: {
|
|
327
|
+
media_ids: [mediaId]
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
};
|
|
331
|
+
let query = 'https://api.twitter.com/2/tweets';
|
|
332
|
+
return publishTweet(token, payload, query, false, logger);
|
|
333
|
+
}
|
|
334
|
+
async function publishTweet(token, payload, query, isDirectMessage, logger) {
|
|
335
|
+
try {
|
|
336
|
+
let nativeResponse = await postRequest({
|
|
337
|
+
token,
|
|
338
|
+
uri: query,
|
|
339
|
+
payload,
|
|
340
|
+
convertPayloadToUri: false,
|
|
341
|
+
attachUrlPrefix: isDirectMessage
|
|
342
|
+
});
|
|
343
|
+
if (!isDirectMessage && nativeResponse.resp.statusCode === 201) {
|
|
344
|
+
const uri = `https://api.twitter.com/2/tweets?ids=${nativeResponse.data.data.id}&tweet.fields=public_metrics&expansions=author_id`;
|
|
345
|
+
nativeResponse = await getRequest({
|
|
346
|
+
token,
|
|
347
|
+
uri,
|
|
348
|
+
payload,
|
|
349
|
+
attachUrlPrefix: false,
|
|
350
|
+
convertPayloadToUri: false
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
loggerInfo(logger, `finished fetching twitterAPI nativeResponse `, {
|
|
354
|
+
data: JSON.stringify(nativeResponse.data)
|
|
355
|
+
});
|
|
356
|
+
const response = nativeResponse.resp ? {
|
|
357
|
+
statusCode: nativeResponse.resp.statusCode,
|
|
358
|
+
statusMessage: nativeResponse.resp.statusCode === 200 ? 'Success' : 'Unknown Failure',
|
|
359
|
+
data: isDirectMessage ? nativeResponse.data.event : nativeResponse.data.data[0]
|
|
360
|
+
} : {
|
|
361
|
+
statusCode: nativeResponse.statusCode,
|
|
362
|
+
statusMessage: nativeResponse.message
|
|
363
|
+
};
|
|
364
|
+
loggerDebug(logger, `Twitter the data response is`, {
|
|
365
|
+
response: JSON.stringify(response)
|
|
366
|
+
});
|
|
367
|
+
return response;
|
|
368
|
+
} catch (err) {
|
|
369
|
+
loggerError(`Twitter publish exception details`, err);
|
|
370
|
+
throw err;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
async function postRequest(_ref) {
|
|
374
|
+
let {
|
|
375
|
+
token,
|
|
376
|
+
uri,
|
|
377
|
+
payload,
|
|
378
|
+
attachUrlPrefix = true,
|
|
379
|
+
convertPayloadToUri = true,
|
|
380
|
+
logger
|
|
381
|
+
} = _ref;
|
|
382
|
+
return doRequest({
|
|
383
|
+
requestMethod: 'post',
|
|
384
|
+
token,
|
|
385
|
+
convertPayloadToUri,
|
|
386
|
+
payload,
|
|
387
|
+
uri,
|
|
388
|
+
attachUrlPrefix,
|
|
389
|
+
logger
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
async function getRequest(_ref2) {
|
|
393
|
+
let {
|
|
394
|
+
token,
|
|
395
|
+
uri,
|
|
396
|
+
payload,
|
|
397
|
+
attachUrlPrefix = true,
|
|
398
|
+
convertPayloadToUri = true,
|
|
399
|
+
logger
|
|
400
|
+
} = _ref2;
|
|
401
|
+
return doRequest({
|
|
402
|
+
requestMethod: 'get',
|
|
403
|
+
payload,
|
|
404
|
+
token,
|
|
405
|
+
uri,
|
|
406
|
+
attachUrlPrefix,
|
|
407
|
+
convertPayloadToUri,
|
|
408
|
+
logger
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
async function deleteRequest(_ref3) {
|
|
412
|
+
let {
|
|
413
|
+
token,
|
|
414
|
+
uri,
|
|
415
|
+
payload,
|
|
416
|
+
attachUrlPrefix = false,
|
|
417
|
+
convertPayloadToUri = false,
|
|
418
|
+
logger
|
|
419
|
+
} = _ref3;
|
|
420
|
+
return doRequest({
|
|
421
|
+
requestMethod: 'delete',
|
|
422
|
+
token,
|
|
423
|
+
convertPayloadToUri,
|
|
424
|
+
payload,
|
|
425
|
+
uri,
|
|
426
|
+
attachUrlPrefix,
|
|
427
|
+
logger
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
function fixedEncodeURIComponent(str) {
|
|
431
|
+
return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
|
|
432
|
+
return '%' + c.charCodeAt(0).toString(16);
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
async function doRequest(_ref4) {
|
|
436
|
+
let {
|
|
437
|
+
requestMethod,
|
|
438
|
+
token,
|
|
439
|
+
uri,
|
|
440
|
+
payload,
|
|
441
|
+
attachUrlPrefix = true,
|
|
442
|
+
convertPayloadToUri = true,
|
|
443
|
+
logger
|
|
444
|
+
} = _ref4;
|
|
445
|
+
if (payload && convertPayloadToUri) {
|
|
446
|
+
uri = uri + (uri.endsWith('?') ? '' : '?') + Object.keys(payload).map(key => {
|
|
447
|
+
return fixedEncodeURIComponent(key) + '=' + fixedEncodeURIComponent(payload[key]);
|
|
448
|
+
}).join('&');
|
|
449
|
+
}
|
|
450
|
+
let url = attachUrlPrefix ? `https://api.twitter.com/1.1/${uri}` : uri;
|
|
451
|
+
return new Promise(async (resolve, reject) => {
|
|
452
|
+
try {
|
|
453
|
+
const {
|
|
454
|
+
Authorization
|
|
455
|
+
} = token.oauth.toHeader(token.oauth.authorize({
|
|
456
|
+
url,
|
|
457
|
+
method: requestMethod,
|
|
458
|
+
data: {}
|
|
459
|
+
}, {
|
|
460
|
+
key: token.token,
|
|
461
|
+
secret: token.tokenSecret
|
|
462
|
+
}));
|
|
463
|
+
const result = await (requestMethod === 'get' ? superagent.get(url).set('Authorization', Authorization) : requestMethod === 'post' ? superagent.post(url).set('Authorization', Authorization).send(payload && !convertPayloadToUri ? payload : undefined) : superagent.del(url).set('Authorization', Authorization));
|
|
464
|
+
loggerDebug(logger, 'Twitter Native Response ', {
|
|
465
|
+
native: {
|
|
466
|
+
//data: result.body,
|
|
467
|
+
contentType: result.headers['content-type']
|
|
468
|
+
},
|
|
469
|
+
methodParams: {
|
|
470
|
+
uri,
|
|
471
|
+
payload: JSON.stringify(payload)
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
resolve({
|
|
475
|
+
data: result.body,
|
|
476
|
+
contentType: result.headers['content-type'],
|
|
477
|
+
resp: {
|
|
478
|
+
...result
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
} catch (e) {
|
|
482
|
+
loggerError(logger, `Error in twitter doRequest`, e, {
|
|
483
|
+
url,
|
|
484
|
+
convertPayloadToUri,
|
|
485
|
+
payload: JSON.stringify(payload)
|
|
486
|
+
});
|
|
487
|
+
reject(e);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// local
|
|
493
|
+
async function downloadImage(url, fileName) {
|
|
494
|
+
const writer = fs.createWriteStream(fileName);
|
|
495
|
+
const response = await axios({
|
|
496
|
+
url,
|
|
497
|
+
method: 'GET',
|
|
498
|
+
responseType: 'stream'
|
|
499
|
+
});
|
|
500
|
+
response.data = response.data;
|
|
501
|
+
response.data.pipe(writer);
|
|
502
|
+
return new Promise((resolve, reject) => {
|
|
503
|
+
writer.on('finish', resolve);
|
|
504
|
+
writer.on('error', reject);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// local
|
|
509
|
+
function generateFilePath(mimeType) {
|
|
510
|
+
let dir = `${__dirname}/temporaryTwitterMedia`;
|
|
511
|
+
if (!fs.existsSync(dir)) {
|
|
512
|
+
fs.mkdirSync(dir);
|
|
513
|
+
}
|
|
514
|
+
let randomFileName = Math.floor(Math.random() * 100);
|
|
515
|
+
let fileExtension = mimeType.split('/')[1];
|
|
516
|
+
if (mimeType === 'video/quicktime') {
|
|
517
|
+
fileExtension = 'mov';
|
|
518
|
+
}
|
|
519
|
+
return `${dir}/${randomFileName}.${fileExtension}`;
|
|
520
|
+
}
|
|
521
|
+
// local
|
|
522
|
+
async function uploadMedia(attachment, token, discussionType, logger) {
|
|
523
|
+
return new Promise(async (resolve, reject) => {
|
|
524
|
+
let filePath = generateFilePath(attachment.mimeType);
|
|
525
|
+
await downloadImage(attachment.url, filePath);
|
|
526
|
+
const T = new Twit({
|
|
527
|
+
consumer_key: token.consumer_key,
|
|
528
|
+
consumer_secret: token.consumer_secret,
|
|
529
|
+
access_token: token.token,
|
|
530
|
+
access_token_secret: token.tokenSecret
|
|
531
|
+
});
|
|
532
|
+
let mediaCategory = discussionType === 'dm' ? 'dm' : 'tweet';
|
|
533
|
+
try {
|
|
534
|
+
T.postMediaChunked({
|
|
535
|
+
file_path: filePath
|
|
536
|
+
}, mediaCategory, function (err, data, response) {
|
|
537
|
+
if (err) {
|
|
538
|
+
reject(err);
|
|
539
|
+
}
|
|
540
|
+
resolve(data.media_id_string);
|
|
541
|
+
removeMedia(filePath, logger);
|
|
542
|
+
});
|
|
543
|
+
} catch (e) {
|
|
544
|
+
loggerError(logger, `Failed posting media`, e);
|
|
545
|
+
// this is just a safety precaution
|
|
546
|
+
removeMedia(filePath, logger);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
function removeMedia(file, logger) {
|
|
551
|
+
try {
|
|
552
|
+
fs.unlink(file, function (err) {
|
|
553
|
+
if (err) {
|
|
554
|
+
loggerError(logger, `Failed removing ${file}`, err);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
} catch (e) {
|
|
558
|
+
loggerError(logger, `failed trying to remove media ${file} it may have already been removed`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
;
|
|
@@ -20,6 +20,8 @@ import * as FacebookNative from './http/facebook.native.js';
|
|
|
20
20
|
import * as TiktokNative from './http/tiktok.native.js';
|
|
21
21
|
import * as YoutubeNative from './http/youtube.native.js';
|
|
22
22
|
import * as LinkedinNative from './http/linkedin.native.js';
|
|
23
|
+
import * as TwitterNative from './http/twitter.native.js';
|
|
24
|
+
import * as InstagramNative from './http/instagram.native.js';
|
|
23
25
|
const DocumentHelperFunctions = {
|
|
24
26
|
...messageHelpers,
|
|
25
27
|
...applicationTagFunctions,
|
|
@@ -29,4 +31,4 @@ const LinkedInHelpers = {
|
|
|
29
31
|
getOrganization,
|
|
30
32
|
getProfile
|
|
31
33
|
};
|
|
32
|
-
export { FacebookNative, TiktokNative, YoutubeNative, LinkedinNative, awsS3Client, assetManagerTvmRepository, CompanyApiClient, CredentialsApiClient, EntitlementsApiClient, FacebookApiClient, FeatureToggleClient, IdentityServicesClient, InstagramApiClient, InstagramVideoClient, IRClient, LinkedInApiClient, TikTokApiClient, MasfClient, WarpZoneApiClient, DocumentHelperFunctions, LinkedInHelpers };
|
|
34
|
+
export { InstagramNative, FacebookNative, TiktokNative, YoutubeNative, LinkedinNative, TwitterNative, 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.
|
|
3
|
+
"version": "1.0.26",
|
|
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",
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/meltwater/conversations-api-services#readme",
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"axios": "^1.1.3",
|
|
34
|
+
"oauth-1.0a": "^2.2.6",
|
|
35
|
+
"@meltwater/social-twit": "^2.2.12",
|
|
33
36
|
"@elastic/ecs-winston-format": "^1.5.3",
|
|
34
37
|
"@hapi/joi": "^17.1.1",
|
|
35
38
|
"@meltwater/date-range": "^3.6.0",
|