@meltwater/conversations-api-services 1.1.5 → 1.1.6
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.
|
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.getInsights = getInsights;
|
|
7
7
|
exports.getProfile = getProfile;
|
|
8
|
+
exports.manageReply = manageReply;
|
|
9
|
+
exports.quote = quote;
|
|
8
10
|
exports.reply = reply;
|
|
9
11
|
exports.repost = repost;
|
|
10
12
|
var _superagent = _interopRequireDefault(require("superagent"));
|
|
@@ -17,25 +19,68 @@ const MAX_RETRY_COUNT = 11;
|
|
|
17
19
|
const SHORT_WAIT_TIME_MS = 10000; // 10 seconds
|
|
18
20
|
const LONG_WAIT_TIME_MS = 60000; // 60 seconds
|
|
19
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Utility function to retry an asynchronous operation with exponential backoff.
|
|
24
|
+
* @param {Function} operation - The async operation to retry.
|
|
25
|
+
* @param {number} maxRetries - Maximum number of retries.
|
|
26
|
+
* @param {number} initialWaitTime - Initial wait time in milliseconds.
|
|
27
|
+
* @param {Function} logger - Logger instance for logging retries.
|
|
28
|
+
* @returns {Promise<any>} The result of the operation.
|
|
29
|
+
* @throws {Error} If the operation fails after all retries.
|
|
30
|
+
*/
|
|
31
|
+
async function retryWithBackoff(operation, maxRetries, initialWaitTime, logger) {
|
|
32
|
+
let waitTime = initialWaitTime;
|
|
33
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
34
|
+
try {
|
|
35
|
+
return await operation();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (attempt === maxRetries) {
|
|
38
|
+
throw error; // Rethrow error if max retries are reached
|
|
39
|
+
}
|
|
40
|
+
(0, _loggerHelpers.loggerInfo)(logger, `Retry attempt ${attempt + 1} failed. Retrying in ${waitTime / 1000} seconds...`);
|
|
41
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
42
|
+
waitTime *= 2; // Exponential backoff
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Confirms the creation ID by polling the Threads API until the status is no longer "IN_PROGRESS".
|
|
49
|
+
* @param {string} accessToken - The access token for authentication.
|
|
50
|
+
* @param {string} creationId - The ID of the creation process to confirm.
|
|
51
|
+
* @param {Object} logger - Logger instance for logging.
|
|
52
|
+
* @returns {Promise<Object>} The API response when the status is no longer "IN_PROGRESS".
|
|
53
|
+
* @throws {Error} If the status remains "IN_PROGRESS" after all retries.
|
|
54
|
+
*/
|
|
20
55
|
async function confirmCreationId(accessToken, creationId, logger) {
|
|
21
|
-
|
|
56
|
+
const operation = async () => {
|
|
22
57
|
const response = await _superagent.default.get(`${THREADS_URL}/${creationId}`).set('Accept', 'application/json').set('Content-Type', 'application/json').query({
|
|
23
58
|
access_token: accessToken,
|
|
24
59
|
fields: 'id,status,error_message'
|
|
25
60
|
}).send();
|
|
26
61
|
if (response.body.status === 'IN_PROGRESS') {
|
|
27
|
-
|
|
28
|
-
(0, _loggerHelpers.loggerInfo)(logger, `Creation ID ${creationId} in progress. Retry ${retry}. Waiting ${waitTime / 1000} seconds.`);
|
|
29
|
-
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
30
|
-
} else {
|
|
31
|
-
(0, _loggerHelpers.loggerDebug)(logger, 'Threads Response status', response.status);
|
|
32
|
-
return response;
|
|
62
|
+
throw new Error('Status is still IN_PROGRESS');
|
|
33
63
|
}
|
|
64
|
+
(0, _loggerHelpers.loggerDebug)(logger, 'Threads Response status', response.status);
|
|
65
|
+
return response;
|
|
66
|
+
};
|
|
67
|
+
try {
|
|
68
|
+
return await retryWithBackoff(operation, MAX_RETRY_COUNT, SHORT_WAIT_TIME_MS, logger);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
(0, _loggerHelpers.loggerError)(logger, `Creation ID ${creationId} confirmation failed: ${error.message}`, error);
|
|
71
|
+
error.code = 408; // Request Timeout
|
|
72
|
+
throw error;
|
|
34
73
|
}
|
|
35
|
-
const error = new Error(`Creation ID ${creationId} is taking too long.`);
|
|
36
|
-
error.code = 408; // Request Timeout
|
|
37
|
-
throw error;
|
|
38
74
|
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Sends a GET request to the Threads API.
|
|
78
|
+
* @param {string} apiUrl - The API URL to send the request to.
|
|
79
|
+
* @param {Object} params - Query parameters for the request.
|
|
80
|
+
* @param {Object} logger - Logger instance for logging.
|
|
81
|
+
* @returns {Promise<Object>} The API response.
|
|
82
|
+
* @throws {Error} If the request fails.
|
|
83
|
+
*/
|
|
39
84
|
async function sendRequest(apiUrl, params, logger) {
|
|
40
85
|
try {
|
|
41
86
|
const response = await _superagent.default.get(apiUrl).set('Accept', 'application/json').set('Content-Type', 'application/json').query(params).send();
|
|
@@ -50,6 +95,15 @@ async function sendRequest(apiUrl, params, logger) {
|
|
|
50
95
|
throw err;
|
|
51
96
|
}
|
|
52
97
|
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Sends a POST request to the Threads API.
|
|
101
|
+
* @param {string} apiUrl - The API URL to send the request to.
|
|
102
|
+
* @param {Object} params - Query parameters for the request.
|
|
103
|
+
* @param {Object} logger - Logger instance for logging.
|
|
104
|
+
* @returns {Promise<Object>} The API response.
|
|
105
|
+
* @throws {Error} If the request fails.
|
|
106
|
+
*/
|
|
53
107
|
async function requestApi(apiUrl, params, logger) {
|
|
54
108
|
try {
|
|
55
109
|
const response = await _superagent.default.post(apiUrl).set('Accept', 'application/json').set('Content-Type', 'application/json').query(params).send();
|
|
@@ -64,6 +118,15 @@ async function requestApi(apiUrl, params, logger) {
|
|
|
64
118
|
throw err;
|
|
65
119
|
}
|
|
66
120
|
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Publishes a post to Threads.
|
|
124
|
+
* @param {string} accessToken - The access token for authentication.
|
|
125
|
+
* @param {string} text - The text content of the post.
|
|
126
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
127
|
+
* @param {Object} logger - Logger instance for logging.
|
|
128
|
+
* @returns {Promise<Object>} The API response.
|
|
129
|
+
*/
|
|
67
130
|
async function reply(accessToken, inReplyToId, text, asset, logger) {
|
|
68
131
|
const query = await constructReplyQuery(accessToken, inReplyToId, text, asset, logger);
|
|
69
132
|
let response = await requestApi(THREADS_PUBLISH_URL, query, logger);
|
|
@@ -72,6 +135,33 @@ async function reply(accessToken, inReplyToId, text, asset, logger) {
|
|
|
72
135
|
});
|
|
73
136
|
return response.body;
|
|
74
137
|
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Publishes a quote post to Threads.
|
|
141
|
+
* @param {string} accessToken - The access token for authentication.
|
|
142
|
+
* @param {string} inReplyToId - The ID of the post to quote.
|
|
143
|
+
* @param {string} text - The text content of the quote.
|
|
144
|
+
* @param {Object} asset - The media asset to attach to the quote.
|
|
145
|
+
* @param {Object} logger - Logger instance for logging.
|
|
146
|
+
* @returns {Promise<Object>} The API response.
|
|
147
|
+
*/
|
|
148
|
+
async function quote(accessToken, inReplyToId, text, asset, logger) {
|
|
149
|
+
const query = await constructReplyQuery(accessToken, inReplyToId, text, asset, logger, true);
|
|
150
|
+
let response = await requestApi(THREADS_PUBLISH_URL, query, logger);
|
|
151
|
+
(0, _loggerHelpers.loggerInfo)(logger, `Native Threads API Publish quote Response`, {
|
|
152
|
+
responseBody: JSON.stringify(response.body)
|
|
153
|
+
});
|
|
154
|
+
return response.body;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Publishes a post to Threads.
|
|
159
|
+
* @param {string} accessToken - The access token for authentication.
|
|
160
|
+
* @param {string} text - The text content of the post.
|
|
161
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
162
|
+
* @param {Object} logger - Logger instance for logging.
|
|
163
|
+
* @returns {Promise<Object>} The API response.
|
|
164
|
+
*/
|
|
75
165
|
async function repost(token, externalId, logger) {
|
|
76
166
|
const postId = (0, _externalIdHelpers.removePrefix)(externalId);
|
|
77
167
|
let response = await requestApi(`${THREADS_URL}/${postId}/repost`, {
|
|
@@ -82,6 +172,15 @@ async function repost(token, externalId, logger) {
|
|
|
82
172
|
});
|
|
83
173
|
return response.body;
|
|
84
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Retrieves the profile information of a user.
|
|
178
|
+
* @param {string} token - The access token for authentication.
|
|
179
|
+
* @param {string} externalId - The external ID of the user.
|
|
180
|
+
* @param {string} fields - The fields to retrieve from the profile.
|
|
181
|
+
* @param {Object} logger - Logger instance for logging.
|
|
182
|
+
* @returns {Promise<Object>} The API response containing the profile information.
|
|
183
|
+
*/
|
|
85
184
|
async function getProfile(token, externalId, fields, logger) {
|
|
86
185
|
const userId = (0, _externalIdHelpers.removePrefix)(externalId);
|
|
87
186
|
let response = await sendRequest(`${THREADS_URL}/${userId}?fields=${fields}`, {
|
|
@@ -92,6 +191,15 @@ async function getProfile(token, externalId, fields, logger) {
|
|
|
92
191
|
});
|
|
93
192
|
return response.body;
|
|
94
193
|
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Retrieves the insights of a post.
|
|
197
|
+
* @param {string} token - The access token for authentication.
|
|
198
|
+
* @param {string} externalId - The external ID of the post.
|
|
199
|
+
* @param {string} metric - The metric to retrieve insights for.
|
|
200
|
+
* @param {Object} logger - Logger instance for logging.
|
|
201
|
+
* @returns {Promise<Object>} The API response containing the insights.
|
|
202
|
+
*/
|
|
95
203
|
async function getInsights(token, externalId, metric, logger) {
|
|
96
204
|
const mediaId = (0, _externalIdHelpers.removePrefix)(externalId);
|
|
97
205
|
let response = await sendRequest(`${THREADS_URL}/${mediaId}/insights`, {
|
|
@@ -103,11 +211,45 @@ async function getInsights(token, externalId, metric, logger) {
|
|
|
103
211
|
});
|
|
104
212
|
return response.body;
|
|
105
213
|
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Manages the visibility of a reply.
|
|
217
|
+
* @param {string} token - The access token for authentication.
|
|
218
|
+
* @param {string} externalId - The external ID of the reply.
|
|
219
|
+
* @param {boolean} hide - Whether to hide the reply.
|
|
220
|
+
* @param {Object} logger - Logger instance for logging.
|
|
221
|
+
* @returns {Promise<Object>} The API response.
|
|
222
|
+
*/
|
|
223
|
+
async function manageReply(token, externalId) {
|
|
224
|
+
let hide = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
225
|
+
let logger = arguments.length > 3 ? arguments[3] : undefined;
|
|
226
|
+
const replyId = (0, _externalIdHelpers.removePrefix)(externalId);
|
|
227
|
+
let response = await requestApi(`${THREADS_URL}/${replyId}/manage_reply`, {
|
|
228
|
+
access_token: token,
|
|
229
|
+
hide
|
|
230
|
+
}, logger);
|
|
231
|
+
(0, _loggerHelpers.loggerInfo)(logger, `Native Threads API hideReply Response`, {
|
|
232
|
+
responseBody: JSON.stringify(response.body)
|
|
233
|
+
});
|
|
234
|
+
return response.body;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Constructs the query parameters for replying to a post.
|
|
239
|
+
* @param {string} accessToken - The access token for authentication.
|
|
240
|
+
* @param {string} inReplyToId - The ID of the post to reply to.
|
|
241
|
+
* @param {string} text - The text content of the reply.
|
|
242
|
+
* @param {Object} asset - The media asset to attach to the reply.
|
|
243
|
+
* @param {Object} logger - Logger instance for logging.
|
|
244
|
+
* @param {boolean} isQuote - Whether the reply is a quote.
|
|
245
|
+
* @returns {Promise<Object>} The constructed query parameters.
|
|
246
|
+
*/
|
|
106
247
|
async function constructReplyQuery(accessToken, inReplyToId, text, asset, logger) {
|
|
248
|
+
let isQuote = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
|
|
107
249
|
const baseQuery = {
|
|
108
250
|
access_token: accessToken,
|
|
109
|
-
|
|
110
|
-
|
|
251
|
+
text,
|
|
252
|
+
[isQuote ? 'quote_post_id' : 'in_reply_to_id']: inReplyToId
|
|
111
253
|
};
|
|
112
254
|
const mediaQuery = getMediaQuery(asset);
|
|
113
255
|
const query = {
|
|
@@ -126,6 +268,12 @@ async function constructReplyQuery(accessToken, inReplyToId, text, asset, logger
|
|
|
126
268
|
throw error;
|
|
127
269
|
}
|
|
128
270
|
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Constructs the media query parameters based on the asset type.
|
|
274
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
275
|
+
* @returns {Object} The constructed media query parameters.
|
|
276
|
+
*/
|
|
129
277
|
function getMediaQuery(asset) {
|
|
130
278
|
if (!asset) {
|
|
131
279
|
return {
|
|
@@ -7,25 +7,68 @@ const MAX_RETRY_COUNT = 11;
|
|
|
7
7
|
const SHORT_WAIT_TIME_MS = 10000; // 10 seconds
|
|
8
8
|
const LONG_WAIT_TIME_MS = 60000; // 60 seconds
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Utility function to retry an asynchronous operation with exponential backoff.
|
|
12
|
+
* @param {Function} operation - The async operation to retry.
|
|
13
|
+
* @param {number} maxRetries - Maximum number of retries.
|
|
14
|
+
* @param {number} initialWaitTime - Initial wait time in milliseconds.
|
|
15
|
+
* @param {Function} logger - Logger instance for logging retries.
|
|
16
|
+
* @returns {Promise<any>} The result of the operation.
|
|
17
|
+
* @throws {Error} If the operation fails after all retries.
|
|
18
|
+
*/
|
|
19
|
+
async function retryWithBackoff(operation, maxRetries, initialWaitTime, logger) {
|
|
20
|
+
let waitTime = initialWaitTime;
|
|
21
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
22
|
+
try {
|
|
23
|
+
return await operation();
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (attempt === maxRetries) {
|
|
26
|
+
throw error; // Rethrow error if max retries are reached
|
|
27
|
+
}
|
|
28
|
+
loggerInfo(logger, `Retry attempt ${attempt + 1} failed. Retrying in ${waitTime / 1000} seconds...`);
|
|
29
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
30
|
+
waitTime *= 2; // Exponential backoff
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Confirms the creation ID by polling the Threads API until the status is no longer "IN_PROGRESS".
|
|
37
|
+
* @param {string} accessToken - The access token for authentication.
|
|
38
|
+
* @param {string} creationId - The ID of the creation process to confirm.
|
|
39
|
+
* @param {Object} logger - Logger instance for logging.
|
|
40
|
+
* @returns {Promise<Object>} The API response when the status is no longer "IN_PROGRESS".
|
|
41
|
+
* @throws {Error} If the status remains "IN_PROGRESS" after all retries.
|
|
42
|
+
*/
|
|
10
43
|
async function confirmCreationId(accessToken, creationId, logger) {
|
|
11
|
-
|
|
44
|
+
const operation = async () => {
|
|
12
45
|
const response = await superagent.get(`${THREADS_URL}/${creationId}`).set('Accept', 'application/json').set('Content-Type', 'application/json').query({
|
|
13
46
|
access_token: accessToken,
|
|
14
47
|
fields: 'id,status,error_message'
|
|
15
48
|
}).send();
|
|
16
49
|
if (response.body.status === 'IN_PROGRESS') {
|
|
17
|
-
|
|
18
|
-
loggerInfo(logger, `Creation ID ${creationId} in progress. Retry ${retry}. Waiting ${waitTime / 1000} seconds.`);
|
|
19
|
-
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
20
|
-
} else {
|
|
21
|
-
loggerDebug(logger, 'Threads Response status', response.status);
|
|
22
|
-
return response;
|
|
50
|
+
throw new Error('Status is still IN_PROGRESS');
|
|
23
51
|
}
|
|
52
|
+
loggerDebug(logger, 'Threads Response status', response.status);
|
|
53
|
+
return response;
|
|
54
|
+
};
|
|
55
|
+
try {
|
|
56
|
+
return await retryWithBackoff(operation, MAX_RETRY_COUNT, SHORT_WAIT_TIME_MS, logger);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
loggerError(logger, `Creation ID ${creationId} confirmation failed: ${error.message}`, error);
|
|
59
|
+
error.code = 408; // Request Timeout
|
|
60
|
+
throw error;
|
|
24
61
|
}
|
|
25
|
-
const error = new Error(`Creation ID ${creationId} is taking too long.`);
|
|
26
|
-
error.code = 408; // Request Timeout
|
|
27
|
-
throw error;
|
|
28
62
|
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sends a GET request to the Threads API.
|
|
66
|
+
* @param {string} apiUrl - The API URL to send the request to.
|
|
67
|
+
* @param {Object} params - Query parameters for the request.
|
|
68
|
+
* @param {Object} logger - Logger instance for logging.
|
|
69
|
+
* @returns {Promise<Object>} The API response.
|
|
70
|
+
* @throws {Error} If the request fails.
|
|
71
|
+
*/
|
|
29
72
|
async function sendRequest(apiUrl, params, logger) {
|
|
30
73
|
try {
|
|
31
74
|
const response = await superagent.get(apiUrl).set('Accept', 'application/json').set('Content-Type', 'application/json').query(params).send();
|
|
@@ -40,6 +83,15 @@ async function sendRequest(apiUrl, params, logger) {
|
|
|
40
83
|
throw err;
|
|
41
84
|
}
|
|
42
85
|
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Sends a POST request to the Threads API.
|
|
89
|
+
* @param {string} apiUrl - The API URL to send the request to.
|
|
90
|
+
* @param {Object} params - Query parameters for the request.
|
|
91
|
+
* @param {Object} logger - Logger instance for logging.
|
|
92
|
+
* @returns {Promise<Object>} The API response.
|
|
93
|
+
* @throws {Error} If the request fails.
|
|
94
|
+
*/
|
|
43
95
|
async function requestApi(apiUrl, params, logger) {
|
|
44
96
|
try {
|
|
45
97
|
const response = await superagent.post(apiUrl).set('Accept', 'application/json').set('Content-Type', 'application/json').query(params).send();
|
|
@@ -54,6 +106,15 @@ async function requestApi(apiUrl, params, logger) {
|
|
|
54
106
|
throw err;
|
|
55
107
|
}
|
|
56
108
|
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Publishes a post to Threads.
|
|
112
|
+
* @param {string} accessToken - The access token for authentication.
|
|
113
|
+
* @param {string} text - The text content of the post.
|
|
114
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
115
|
+
* @param {Object} logger - Logger instance for logging.
|
|
116
|
+
* @returns {Promise<Object>} The API response.
|
|
117
|
+
*/
|
|
57
118
|
export async function reply(accessToken, inReplyToId, text, asset, logger) {
|
|
58
119
|
const query = await constructReplyQuery(accessToken, inReplyToId, text, asset, logger);
|
|
59
120
|
let response = await requestApi(THREADS_PUBLISH_URL, query, logger);
|
|
@@ -62,6 +123,33 @@ export async function reply(accessToken, inReplyToId, text, asset, logger) {
|
|
|
62
123
|
});
|
|
63
124
|
return response.body;
|
|
64
125
|
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Publishes a quote post to Threads.
|
|
129
|
+
* @param {string} accessToken - The access token for authentication.
|
|
130
|
+
* @param {string} inReplyToId - The ID of the post to quote.
|
|
131
|
+
* @param {string} text - The text content of the quote.
|
|
132
|
+
* @param {Object} asset - The media asset to attach to the quote.
|
|
133
|
+
* @param {Object} logger - Logger instance for logging.
|
|
134
|
+
* @returns {Promise<Object>} The API response.
|
|
135
|
+
*/
|
|
136
|
+
export async function quote(accessToken, inReplyToId, text, asset, logger) {
|
|
137
|
+
const query = await constructReplyQuery(accessToken, inReplyToId, text, asset, logger, true);
|
|
138
|
+
let response = await requestApi(THREADS_PUBLISH_URL, query, logger);
|
|
139
|
+
loggerInfo(logger, `Native Threads API Publish quote Response`, {
|
|
140
|
+
responseBody: JSON.stringify(response.body)
|
|
141
|
+
});
|
|
142
|
+
return response.body;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Publishes a post to Threads.
|
|
147
|
+
* @param {string} accessToken - The access token for authentication.
|
|
148
|
+
* @param {string} text - The text content of the post.
|
|
149
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
150
|
+
* @param {Object} logger - Logger instance for logging.
|
|
151
|
+
* @returns {Promise<Object>} The API response.
|
|
152
|
+
*/
|
|
65
153
|
export async function repost(token, externalId, logger) {
|
|
66
154
|
const postId = removePrefix(externalId);
|
|
67
155
|
let response = await requestApi(`${THREADS_URL}/${postId}/repost`, {
|
|
@@ -72,6 +160,15 @@ export async function repost(token, externalId, logger) {
|
|
|
72
160
|
});
|
|
73
161
|
return response.body;
|
|
74
162
|
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Retrieves the profile information of a user.
|
|
166
|
+
* @param {string} token - The access token for authentication.
|
|
167
|
+
* @param {string} externalId - The external ID of the user.
|
|
168
|
+
* @param {string} fields - The fields to retrieve from the profile.
|
|
169
|
+
* @param {Object} logger - Logger instance for logging.
|
|
170
|
+
* @returns {Promise<Object>} The API response containing the profile information.
|
|
171
|
+
*/
|
|
75
172
|
export async function getProfile(token, externalId, fields, logger) {
|
|
76
173
|
const userId = removePrefix(externalId);
|
|
77
174
|
let response = await sendRequest(`${THREADS_URL}/${userId}?fields=${fields}`, {
|
|
@@ -82,6 +179,15 @@ export async function getProfile(token, externalId, fields, logger) {
|
|
|
82
179
|
});
|
|
83
180
|
return response.body;
|
|
84
181
|
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Retrieves the insights of a post.
|
|
185
|
+
* @param {string} token - The access token for authentication.
|
|
186
|
+
* @param {string} externalId - The external ID of the post.
|
|
187
|
+
* @param {string} metric - The metric to retrieve insights for.
|
|
188
|
+
* @param {Object} logger - Logger instance for logging.
|
|
189
|
+
* @returns {Promise<Object>} The API response containing the insights.
|
|
190
|
+
*/
|
|
85
191
|
export async function getInsights(token, externalId, metric, logger) {
|
|
86
192
|
const mediaId = removePrefix(externalId);
|
|
87
193
|
let response = await sendRequest(`${THREADS_URL}/${mediaId}/insights`, {
|
|
@@ -93,11 +199,45 @@ export async function getInsights(token, externalId, metric, logger) {
|
|
|
93
199
|
});
|
|
94
200
|
return response.body;
|
|
95
201
|
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Manages the visibility of a reply.
|
|
205
|
+
* @param {string} token - The access token for authentication.
|
|
206
|
+
* @param {string} externalId - The external ID of the reply.
|
|
207
|
+
* @param {boolean} hide - Whether to hide the reply.
|
|
208
|
+
* @param {Object} logger - Logger instance for logging.
|
|
209
|
+
* @returns {Promise<Object>} The API response.
|
|
210
|
+
*/
|
|
211
|
+
export async function manageReply(token, externalId) {
|
|
212
|
+
let hide = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
213
|
+
let logger = arguments.length > 3 ? arguments[3] : undefined;
|
|
214
|
+
const replyId = removePrefix(externalId);
|
|
215
|
+
let response = await requestApi(`${THREADS_URL}/${replyId}/manage_reply`, {
|
|
216
|
+
access_token: token,
|
|
217
|
+
hide
|
|
218
|
+
}, logger);
|
|
219
|
+
loggerInfo(logger, `Native Threads API hideReply Response`, {
|
|
220
|
+
responseBody: JSON.stringify(response.body)
|
|
221
|
+
});
|
|
222
|
+
return response.body;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Constructs the query parameters for replying to a post.
|
|
227
|
+
* @param {string} accessToken - The access token for authentication.
|
|
228
|
+
* @param {string} inReplyToId - The ID of the post to reply to.
|
|
229
|
+
* @param {string} text - The text content of the reply.
|
|
230
|
+
* @param {Object} asset - The media asset to attach to the reply.
|
|
231
|
+
* @param {Object} logger - Logger instance for logging.
|
|
232
|
+
* @param {boolean} isQuote - Whether the reply is a quote.
|
|
233
|
+
* @returns {Promise<Object>} The constructed query parameters.
|
|
234
|
+
*/
|
|
96
235
|
async function constructReplyQuery(accessToken, inReplyToId, text, asset, logger) {
|
|
236
|
+
let isQuote = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
|
|
97
237
|
const baseQuery = {
|
|
98
238
|
access_token: accessToken,
|
|
99
|
-
|
|
100
|
-
|
|
239
|
+
text,
|
|
240
|
+
[isQuote ? 'quote_post_id' : 'in_reply_to_id']: inReplyToId
|
|
101
241
|
};
|
|
102
242
|
const mediaQuery = getMediaQuery(asset);
|
|
103
243
|
const query = {
|
|
@@ -116,6 +256,12 @@ async function constructReplyQuery(accessToken, inReplyToId, text, asset, logger
|
|
|
116
256
|
throw error;
|
|
117
257
|
}
|
|
118
258
|
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Constructs the media query parameters based on the asset type.
|
|
262
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
263
|
+
* @returns {Object} The constructed media query parameters.
|
|
264
|
+
*/
|
|
119
265
|
function getMediaQuery(asset) {
|
|
120
266
|
if (!asset) {
|
|
121
267
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meltwater/conversations-api-services",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
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",
|
|
@@ -13,8 +13,46 @@ const MAX_RETRY_COUNT = 11;
|
|
|
13
13
|
const SHORT_WAIT_TIME_MS = 10000; // 10 seconds
|
|
14
14
|
const LONG_WAIT_TIME_MS = 60000; // 60 seconds
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Utility function to retry an asynchronous operation with exponential backoff.
|
|
18
|
+
* @param {Function} operation - The async operation to retry.
|
|
19
|
+
* @param {number} maxRetries - Maximum number of retries.
|
|
20
|
+
* @param {number} initialWaitTime - Initial wait time in milliseconds.
|
|
21
|
+
* @param {Function} logger - Logger instance for logging retries.
|
|
22
|
+
* @returns {Promise<any>} The result of the operation.
|
|
23
|
+
* @throws {Error} If the operation fails after all retries.
|
|
24
|
+
*/
|
|
25
|
+
async function retryWithBackoff(operation, maxRetries, initialWaitTime, logger) {
|
|
26
|
+
let waitTime = initialWaitTime;
|
|
27
|
+
|
|
28
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
29
|
+
try {
|
|
30
|
+
return await operation();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (attempt === maxRetries) {
|
|
33
|
+
throw error; // Rethrow error if max retries are reached
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
loggerInfo(
|
|
37
|
+
logger,
|
|
38
|
+
`Retry attempt ${attempt + 1} failed. Retrying in ${waitTime / 1000} seconds...`
|
|
39
|
+
);
|
|
40
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
41
|
+
waitTime *= 2; // Exponential backoff
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Confirms the creation ID by polling the Threads API until the status is no longer "IN_PROGRESS".
|
|
48
|
+
* @param {string} accessToken - The access token for authentication.
|
|
49
|
+
* @param {string} creationId - The ID of the creation process to confirm.
|
|
50
|
+
* @param {Object} logger - Logger instance for logging.
|
|
51
|
+
* @returns {Promise<Object>} The API response when the status is no longer "IN_PROGRESS".
|
|
52
|
+
* @throws {Error} If the status remains "IN_PROGRESS" after all retries.
|
|
53
|
+
*/
|
|
16
54
|
async function confirmCreationId(accessToken, creationId, logger) {
|
|
17
|
-
|
|
55
|
+
const operation = async () => {
|
|
18
56
|
const response = await superagent
|
|
19
57
|
.get(`${THREADS_URL}/${creationId}`)
|
|
20
58
|
.set('Accept', 'application/json')
|
|
@@ -26,26 +64,30 @@ async function confirmCreationId(accessToken, creationId, logger) {
|
|
|
26
64
|
.send();
|
|
27
65
|
|
|
28
66
|
if (response.body.status === 'IN_PROGRESS') {
|
|
29
|
-
|
|
30
|
-
retry === 0 ? SHORT_WAIT_TIME_MS : LONG_WAIT_TIME_MS;
|
|
31
|
-
loggerInfo(
|
|
32
|
-
logger,
|
|
33
|
-
`Creation ID ${creationId} in progress. Retry ${retry}. Waiting ${
|
|
34
|
-
waitTime / 1000
|
|
35
|
-
} seconds.`
|
|
36
|
-
);
|
|
37
|
-
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
38
|
-
} else {
|
|
39
|
-
loggerDebug(logger, 'Threads Response status', response.status);
|
|
40
|
-
return response;
|
|
67
|
+
throw new Error('Status is still IN_PROGRESS');
|
|
41
68
|
}
|
|
42
|
-
}
|
|
43
69
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
70
|
+
loggerDebug(logger, 'Threads Response status', response.status);
|
|
71
|
+
return response;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
return await retryWithBackoff(operation, MAX_RETRY_COUNT, SHORT_WAIT_TIME_MS, logger);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
loggerError(logger, `Creation ID ${creationId} confirmation failed: ${error.message}`, error);
|
|
78
|
+
error.code = 408; // Request Timeout
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
47
81
|
}
|
|
48
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Sends a GET request to the Threads API.
|
|
85
|
+
* @param {string} apiUrl - The API URL to send the request to.
|
|
86
|
+
* @param {Object} params - Query parameters for the request.
|
|
87
|
+
* @param {Object} logger - Logger instance for logging.
|
|
88
|
+
* @returns {Promise<Object>} The API response.
|
|
89
|
+
* @throws {Error} If the request fails.
|
|
90
|
+
*/
|
|
49
91
|
async function sendRequest(apiUrl, params, logger) {
|
|
50
92
|
try {
|
|
51
93
|
const response = await superagent
|
|
@@ -68,6 +110,14 @@ async function sendRequest(apiUrl, params, logger) {
|
|
|
68
110
|
}
|
|
69
111
|
}
|
|
70
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Sends a POST request to the Threads API.
|
|
115
|
+
* @param {string} apiUrl - The API URL to send the request to.
|
|
116
|
+
* @param {Object} params - Query parameters for the request.
|
|
117
|
+
* @param {Object} logger - Logger instance for logging.
|
|
118
|
+
* @returns {Promise<Object>} The API response.
|
|
119
|
+
* @throws {Error} If the request fails.
|
|
120
|
+
*/
|
|
71
121
|
async function requestApi(apiUrl, params, logger) {
|
|
72
122
|
try {
|
|
73
123
|
const response = await superagent
|
|
@@ -90,6 +140,14 @@ async function requestApi(apiUrl, params, logger) {
|
|
|
90
140
|
}
|
|
91
141
|
}
|
|
92
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Publishes a post to Threads.
|
|
145
|
+
* @param {string} accessToken - The access token for authentication.
|
|
146
|
+
* @param {string} text - The text content of the post.
|
|
147
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
148
|
+
* @param {Object} logger - Logger instance for logging.
|
|
149
|
+
* @returns {Promise<Object>} The API response.
|
|
150
|
+
*/
|
|
93
151
|
export async function reply(accessToken, inReplyToId, text, asset, logger) {
|
|
94
152
|
const query = await constructReplyQuery(
|
|
95
153
|
accessToken,
|
|
@@ -107,6 +165,39 @@ export async function reply(accessToken, inReplyToId, text, asset, logger) {
|
|
|
107
165
|
return response.body;
|
|
108
166
|
}
|
|
109
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Publishes a quote post to Threads.
|
|
170
|
+
* @param {string} accessToken - The access token for authentication.
|
|
171
|
+
* @param {string} inReplyToId - The ID of the post to quote.
|
|
172
|
+
* @param {string} text - The text content of the quote.
|
|
173
|
+
* @param {Object} asset - The media asset to attach to the quote.
|
|
174
|
+
* @param {Object} logger - Logger instance for logging.
|
|
175
|
+
* @returns {Promise<Object>} The API response.
|
|
176
|
+
*/
|
|
177
|
+
export async function quote(accessToken, inReplyToId, text, asset, logger) {
|
|
178
|
+
const query = await constructReplyQuery(
|
|
179
|
+
accessToken,
|
|
180
|
+
inReplyToId,
|
|
181
|
+
text,
|
|
182
|
+
asset,
|
|
183
|
+
logger,
|
|
184
|
+
true
|
|
185
|
+
);
|
|
186
|
+
let response = await requestApi(THREADS_PUBLISH_URL, query, logger);
|
|
187
|
+
loggerInfo(logger, `Native Threads API Publish quote Response`, {
|
|
188
|
+
responseBody: JSON.stringify(response.body),
|
|
189
|
+
});
|
|
190
|
+
return response.body;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Publishes a post to Threads.
|
|
195
|
+
* @param {string} accessToken - The access token for authentication.
|
|
196
|
+
* @param {string} text - The text content of the post.
|
|
197
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
198
|
+
* @param {Object} logger - Logger instance for logging.
|
|
199
|
+
* @returns {Promise<Object>} The API response.
|
|
200
|
+
*/
|
|
110
201
|
export async function repost(token, externalId, logger) {
|
|
111
202
|
const postId = removePrefix(externalId);
|
|
112
203
|
let response = await requestApi(
|
|
@@ -121,6 +212,14 @@ export async function repost(token, externalId, logger) {
|
|
|
121
212
|
return response.body;
|
|
122
213
|
}
|
|
123
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Retrieves the profile information of a user.
|
|
217
|
+
* @param {string} token - The access token for authentication.
|
|
218
|
+
* @param {string} externalId - The external ID of the user.
|
|
219
|
+
* @param {string} fields - The fields to retrieve from the profile.
|
|
220
|
+
* @param {Object} logger - Logger instance for logging.
|
|
221
|
+
* @returns {Promise<Object>} The API response containing the profile information.
|
|
222
|
+
*/
|
|
124
223
|
export async function getProfile(token, externalId, fields, logger) {
|
|
125
224
|
const userId = removePrefix(externalId);
|
|
126
225
|
let response = await sendRequest(
|
|
@@ -135,6 +234,14 @@ export async function getProfile(token, externalId, fields, logger) {
|
|
|
135
234
|
return response.body;
|
|
136
235
|
}
|
|
137
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Retrieves the insights of a post.
|
|
239
|
+
* @param {string} token - The access token for authentication.
|
|
240
|
+
* @param {string} externalId - The external ID of the post.
|
|
241
|
+
* @param {string} metric - The metric to retrieve insights for.
|
|
242
|
+
* @param {Object} logger - Logger instance for logging.
|
|
243
|
+
* @returns {Promise<Object>} The API response containing the insights.
|
|
244
|
+
*/
|
|
138
245
|
export async function getInsights(token, externalId, metric, logger) {
|
|
139
246
|
const mediaId = removePrefix(externalId);
|
|
140
247
|
let response = await sendRequest(
|
|
@@ -149,17 +256,50 @@ export async function getInsights(token, externalId, metric, logger) {
|
|
|
149
256
|
return response.body;
|
|
150
257
|
}
|
|
151
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Manages the visibility of a reply.
|
|
261
|
+
* @param {string} token - The access token for authentication.
|
|
262
|
+
* @param {string} externalId - The external ID of the reply.
|
|
263
|
+
* @param {boolean} hide - Whether to hide the reply.
|
|
264
|
+
* @param {Object} logger - Logger instance for logging.
|
|
265
|
+
* @returns {Promise<Object>} The API response.
|
|
266
|
+
*/
|
|
267
|
+
export async function manageReply(token, externalId, hide = false, logger) {
|
|
268
|
+
const replyId = removePrefix(externalId);
|
|
269
|
+
let response = await requestApi(
|
|
270
|
+
`${THREADS_URL}/${replyId}/manage_reply`,
|
|
271
|
+
{ access_token: token, hide },
|
|
272
|
+
logger
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
loggerInfo(logger, `Native Threads API hideReply Response`, {
|
|
276
|
+
responseBody: JSON.stringify(response.body),
|
|
277
|
+
});
|
|
278
|
+
return response.body;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Constructs the query parameters for replying to a post.
|
|
283
|
+
* @param {string} accessToken - The access token for authentication.
|
|
284
|
+
* @param {string} inReplyToId - The ID of the post to reply to.
|
|
285
|
+
* @param {string} text - The text content of the reply.
|
|
286
|
+
* @param {Object} asset - The media asset to attach to the reply.
|
|
287
|
+
* @param {Object} logger - Logger instance for logging.
|
|
288
|
+
* @param {boolean} isQuote - Whether the reply is a quote.
|
|
289
|
+
* @returns {Promise<Object>} The constructed query parameters.
|
|
290
|
+
*/
|
|
152
291
|
async function constructReplyQuery(
|
|
153
292
|
accessToken,
|
|
154
293
|
inReplyToId,
|
|
155
294
|
text,
|
|
156
295
|
asset,
|
|
157
|
-
logger
|
|
296
|
+
logger,
|
|
297
|
+
isQuote = false
|
|
158
298
|
) {
|
|
159
299
|
const baseQuery = {
|
|
160
300
|
access_token: accessToken,
|
|
161
|
-
reply_to_id: inReplyToId,
|
|
162
301
|
text,
|
|
302
|
+
[isQuote ? 'quote_post_id' : 'in_reply_to_id']: inReplyToId,
|
|
163
303
|
};
|
|
164
304
|
|
|
165
305
|
const mediaQuery = getMediaQuery(asset);
|
|
@@ -182,6 +322,11 @@ async function constructReplyQuery(
|
|
|
182
322
|
}
|
|
183
323
|
}
|
|
184
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Constructs the media query parameters based on the asset type.
|
|
327
|
+
* @param {Object} asset - The media asset to attach to the post.
|
|
328
|
+
* @returns {Object} The constructed media query parameters.
|
|
329
|
+
*/
|
|
185
330
|
function getMediaQuery(asset) {
|
|
186
331
|
if (!asset) {
|
|
187
332
|
return { media_type: 'TEXT' };
|