@meltwater/conversations-api-services 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.drone.yml +13 -0
- package/.github/workflows/release.yml +42 -0
- package/.nvmrc +1 -0
- package/README.md +91 -0
- package/catalog-info.yaml +12 -0
- package/docs/index.md +28 -0
- package/mkdocs.yml +8 -0
- package/package.json +45 -0
- package/src/data-access/http/InstagramVideoClient.js +37 -0
- package/src/data-access/http/WarpZoneApi.client.js +39 -0
- package/src/data-access/http/amazonS3.js +41 -0
- package/src/data-access/http/asset-manager-tvm.client.js +32 -0
- package/src/data-access/http/companiesApi.client.js +35 -0
- package/src/data-access/http/credentialsApi.client.js +137 -0
- package/src/data-access/http/entitlementsApi.client.js +41 -0
- package/src/data-access/http/facebookApi.client.js +701 -0
- package/src/data-access/http/featureToggleApi.client.js +35 -0
- package/src/data-access/http/identityServices.client.js +117 -0
- package/src/data-access/http/instagramApi.client.js +496 -0
- package/src/data-access/http/ir.client.js +309 -0
- package/src/data-access/http/linkedInApi.client.js +624 -0
- package/src/data-access/http/masf.client.js +93 -0
- package/src/data-access/http/tiktokApi.client.js +477 -0
- package/src/data-access/index.js +33 -0
- package/src/lib/applicationTags.helpers.js +22 -0
- package/src/lib/configuration.js +10 -0
- package/src/lib/document-action-events.js +6 -0
- package/src/lib/externalId.helpers.js +15 -0
- package/src/lib/hiddenComment.helper.js +100 -0
- package/src/lib/logger.js +14 -0
- package/src/lib/metrics.helper.js +107 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import configuration from '../../lib/configuration.js';
|
|
4
|
+
|
|
5
|
+
export class FeatureToggleClient {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.featureToggleUrl = configuration.get('FEATURE_TOGGLE_URL');
|
|
8
|
+
|
|
9
|
+
this.featureToggleNames = [
|
|
10
|
+
'CONNECTIONS-SOCIAL-GOVERNANCE',
|
|
11
|
+
'conversations-googlebusiness-display',
|
|
12
|
+
'CONVERSATIONS_FACEBOOK_MENTIONS',
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async get(companyId, jwt) {
|
|
17
|
+
try {
|
|
18
|
+
const result = await superagent
|
|
19
|
+
.post(`${this.featureToggleUrl}/treatments/${companyId}`)
|
|
20
|
+
.set('Authorization', jwt)
|
|
21
|
+
.send({
|
|
22
|
+
features: this.featureToggleNames,
|
|
23
|
+
attributes: {},
|
|
24
|
+
})
|
|
25
|
+
.timeout(5000);
|
|
26
|
+
|
|
27
|
+
return result.body.treatments;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error(
|
|
30
|
+
`Failed requesting Feature Toggle service for feature toggle get `,
|
|
31
|
+
{ error, companyId }
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import configuration from '../../lib/configuration.js';
|
|
4
|
+
|
|
5
|
+
export class IdentityServicesClient {
|
|
6
|
+
constructor({ companyId, services }) {
|
|
7
|
+
this.companyId = companyId;
|
|
8
|
+
this.identityServicesUrl = configuration.get('IDENTITY_SERVICES_URL');
|
|
9
|
+
|
|
10
|
+
this.authService = services.auth;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async getById(userId, jwt, retries = 0) {
|
|
14
|
+
let user;
|
|
15
|
+
try {
|
|
16
|
+
let resignedToken = await this.authService.getResignedToken(jwt);
|
|
17
|
+
user = await superagent
|
|
18
|
+
.get(`${this.identityServicesUrl}/users/${userId}`)
|
|
19
|
+
.set('Authorization', resignedToken)
|
|
20
|
+
.set('x-client-name', configuration.get('CLIENT_NAME_HEADER'))
|
|
21
|
+
.then((result) => result.body.user);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (retries <= 3) {
|
|
24
|
+
logger.error(
|
|
25
|
+
`getById Failed requesting Identity Services for userId ${userId} retry ${retries}`,
|
|
26
|
+
error
|
|
27
|
+
);
|
|
28
|
+
return this.getById(userId, jwt, ++retries);
|
|
29
|
+
} else {
|
|
30
|
+
logger.error(
|
|
31
|
+
`getById Failed requesting Identity Services app settings for userId ${userId} complete failure`,
|
|
32
|
+
error
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return user;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getAllUsers(jwt, retries = 0) {
|
|
42
|
+
let users;
|
|
43
|
+
try {
|
|
44
|
+
let resignedToken = await this.authService.getResignedToken(jwt);
|
|
45
|
+
users = await superagent
|
|
46
|
+
.get(
|
|
47
|
+
`${this.identityServicesUrl}/companies/${this.companyId}/users`
|
|
48
|
+
)
|
|
49
|
+
.set('Authorization', resignedToken)
|
|
50
|
+
.set('x-client-name', configuration.get('CLIENT_NAME_HEADER'))
|
|
51
|
+
.then((result) => result.body.map((record) => record.user));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (retries <= 3) {
|
|
54
|
+
logger.error(
|
|
55
|
+
`getAllUsers Failed requesting Identity Services for ${this.companyId} retrying ${retries}`,
|
|
56
|
+
error
|
|
57
|
+
);
|
|
58
|
+
return await this.getAllUsers(jwt, ++retries);
|
|
59
|
+
} else {
|
|
60
|
+
logger.error(
|
|
61
|
+
`getAllUsers Failed requesting Identity Services app settings for companyId ${companyId} complete failure`,
|
|
62
|
+
error
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return users;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getUsersAppSettings({ userIds, settingNames, jwt }, retries = 0) {
|
|
72
|
+
if (!settingNames) {
|
|
73
|
+
return {};
|
|
74
|
+
} else {
|
|
75
|
+
let appSettings;
|
|
76
|
+
try {
|
|
77
|
+
let resignedToken = await this.authService.getResignedToken(
|
|
78
|
+
jwt
|
|
79
|
+
);
|
|
80
|
+
appSettings = await superagent
|
|
81
|
+
.post(
|
|
82
|
+
`${this.identityServicesUrl}/users/app_settings/batch_get`
|
|
83
|
+
)
|
|
84
|
+
.set('Authorization', resignedToken)
|
|
85
|
+
.set(
|
|
86
|
+
'x-client-name',
|
|
87
|
+
configuration.get('CLIENT_NAME_HEADER')
|
|
88
|
+
)
|
|
89
|
+
.send({
|
|
90
|
+
userIds,
|
|
91
|
+
settingNames: settingNames,
|
|
92
|
+
})
|
|
93
|
+
.then((result) => result.body);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (retries <= 3) {
|
|
96
|
+
logger.error(
|
|
97
|
+
`getUsersAppSettings Failed requesting Identity Services app settings for userIds ${userIds} retrying ${retries}`,
|
|
98
|
+
error
|
|
99
|
+
);
|
|
100
|
+
return await this.getUsersAppSettings(
|
|
101
|
+
{ userIds, settingNames, jwt },
|
|
102
|
+
++retries
|
|
103
|
+
);
|
|
104
|
+
} else {
|
|
105
|
+
logger.error(
|
|
106
|
+
`getUsersAppSettings Failed requesting Identity Services app settings for userIds ${userIds} complete failure`,
|
|
107
|
+
error
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return appSettings;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import { removePrefix } from '../../lib/externalId.helpers.js';
|
|
4
|
+
import assert from 'assert';
|
|
5
|
+
import { CredentialsApiClient } from '../http/credentialsApi.client.js';
|
|
6
|
+
|
|
7
|
+
const INSTAGRAM_URL = 'https://graph.facebook.com';
|
|
8
|
+
export class InstagramApiClient {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.credentialsAPI = new CredentialsApiClient();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async validate(accessToken, payload) {
|
|
14
|
+
const { inReplyToId, text } = payload;
|
|
15
|
+
assert(accessToken, 'AccessToken is required.');
|
|
16
|
+
assert(inReplyToId, 'inReplyToId is required.');
|
|
17
|
+
assert(text, 'text is required.');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async requestApi(
|
|
21
|
+
apiUrl,
|
|
22
|
+
accessToken,
|
|
23
|
+
text,
|
|
24
|
+
documentId,
|
|
25
|
+
data,
|
|
26
|
+
discussionType = 're',
|
|
27
|
+
adCampaign = undefined
|
|
28
|
+
) {
|
|
29
|
+
let response = {};
|
|
30
|
+
try {
|
|
31
|
+
let queryStringArgs = { access_token: accessToken };
|
|
32
|
+
if (adCampaign) {
|
|
33
|
+
queryStringArgs.ad_id = adCampaign.adAccountId;
|
|
34
|
+
}
|
|
35
|
+
if (text) {
|
|
36
|
+
queryStringArgs[discussionType === 'dm' ? 'text' : 'message'] =
|
|
37
|
+
text;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
response = await superagent
|
|
41
|
+
.post(apiUrl)
|
|
42
|
+
.set('Accept', 'application/json')
|
|
43
|
+
.set('Content-Type', 'application/json')
|
|
44
|
+
.query(queryStringArgs)
|
|
45
|
+
.send(data);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
let errorText = '';
|
|
48
|
+
if (
|
|
49
|
+
err &&
|
|
50
|
+
err.response &&
|
|
51
|
+
err.response.body &&
|
|
52
|
+
err.response.body.error
|
|
53
|
+
) {
|
|
54
|
+
errorText =
|
|
55
|
+
'Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}';
|
|
56
|
+
logger.error(errorText);
|
|
57
|
+
} else {
|
|
58
|
+
errorText =
|
|
59
|
+
'Failed to call instagram api for documentId ${documentId}';
|
|
60
|
+
logger.error(errorText, err);
|
|
61
|
+
}
|
|
62
|
+
throw new Error(errorText);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (response.status !== 200) {
|
|
66
|
+
logger.error(
|
|
67
|
+
`Failed to call instagram api for documentId ${documentId}`,
|
|
68
|
+
response.body
|
|
69
|
+
);
|
|
70
|
+
let error = new Error(
|
|
71
|
+
`Failed to call instagram api for documentId ${documentId}`
|
|
72
|
+
);
|
|
73
|
+
error.code = response.status;
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
logger.debug('Instagram Response status', response.status);
|
|
77
|
+
logger.debug('Instagram Response body', response.body);
|
|
78
|
+
logger.debug('Instagram Response body.id', response.body.id);
|
|
79
|
+
return response;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getLikes(accessToken, payload) {
|
|
83
|
+
let response;
|
|
84
|
+
const { externalId, documentId } = payload;
|
|
85
|
+
|
|
86
|
+
logger.debug(
|
|
87
|
+
`Starting to call instagram getPost api for documentId ${documentId}`,
|
|
88
|
+
{ payload }
|
|
89
|
+
);
|
|
90
|
+
try {
|
|
91
|
+
response = await this.getPost(
|
|
92
|
+
`${INSTAGRAM_URL}/${removePrefix(externalId)}`,
|
|
93
|
+
accessToken,
|
|
94
|
+
'like_count',
|
|
95
|
+
documentId
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
logger.info(
|
|
99
|
+
`Native Instagram API getPost Response for documentId ${documentId}`,
|
|
100
|
+
{ status: response.status, ok: response.ok }
|
|
101
|
+
);
|
|
102
|
+
return response.body;
|
|
103
|
+
} catch (err) {
|
|
104
|
+
logger.error('Error getLikes Instagram', { payload });
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async mentionRequestApi(
|
|
110
|
+
apiUrl,
|
|
111
|
+
accessToken,
|
|
112
|
+
replayObject,
|
|
113
|
+
text,
|
|
114
|
+
documentId
|
|
115
|
+
) {
|
|
116
|
+
let response = {};
|
|
117
|
+
try {
|
|
118
|
+
response = await superagent
|
|
119
|
+
.post(apiUrl)
|
|
120
|
+
.set('Accept', 'application/json')
|
|
121
|
+
.set('Content-Type', 'application/json')
|
|
122
|
+
.query({ access_token: accessToken })
|
|
123
|
+
.query(replayObject)
|
|
124
|
+
.query({ message: text })
|
|
125
|
+
.send();
|
|
126
|
+
} catch (err) {
|
|
127
|
+
let errorText = '';
|
|
128
|
+
if (
|
|
129
|
+
err &&
|
|
130
|
+
err.response &&
|
|
131
|
+
err.response.body &&
|
|
132
|
+
err.response.body.error
|
|
133
|
+
) {
|
|
134
|
+
errorText = `Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}`;
|
|
135
|
+
logger.error(errorText);
|
|
136
|
+
} else {
|
|
137
|
+
errorText = `Failed to call instagram api for documentId ${documentId}`;
|
|
138
|
+
logger.error(errorText, err);
|
|
139
|
+
}
|
|
140
|
+
throw new Error(errorText);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (response.status !== 200) {
|
|
144
|
+
logger.error(
|
|
145
|
+
`Failed to call instagram api for documentId ${documentId}`,
|
|
146
|
+
response.body
|
|
147
|
+
);
|
|
148
|
+
let error = new Error(
|
|
149
|
+
`Failed to call instagram api for documentId ${documentId}`
|
|
150
|
+
);
|
|
151
|
+
error.code = response.status;
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
logger.debug('Instagram Response status', response.status);
|
|
155
|
+
logger.debug('Instagram Response body', response.body);
|
|
156
|
+
logger.debug('Instagram Response body.id', response.body.id);
|
|
157
|
+
return response;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async hideApi(apiUrl, accessToken, hideStatus, documentId) {
|
|
161
|
+
let response = {};
|
|
162
|
+
try {
|
|
163
|
+
response = await superagent
|
|
164
|
+
.post(apiUrl)
|
|
165
|
+
.set('Accept', 'application/json')
|
|
166
|
+
.set('Content-Type', 'application/json')
|
|
167
|
+
.query({ access_token: accessToken })
|
|
168
|
+
.query({ hide: hideStatus })
|
|
169
|
+
.send();
|
|
170
|
+
} catch (err) {
|
|
171
|
+
let errorText = '';
|
|
172
|
+
if (err && err.response && err.response.body) {
|
|
173
|
+
errorText = `Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}`;
|
|
174
|
+
logger.error(errorText, err.response.body.error);
|
|
175
|
+
} else {
|
|
176
|
+
errorText = `Failed to call instagram api for documentId ${documentId}`;
|
|
177
|
+
logger.error(errorText, err);
|
|
178
|
+
}
|
|
179
|
+
throw new Error(errorText);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (response.status !== 200) {
|
|
183
|
+
logger.error(
|
|
184
|
+
`Failed to call instagram api for documentId ${documentId}`,
|
|
185
|
+
response.body
|
|
186
|
+
);
|
|
187
|
+
let error = new Error(
|
|
188
|
+
`Failed to call instagram api for documentId ${documentId}`
|
|
189
|
+
);
|
|
190
|
+
error.code = response.status;
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
logger.debug('Instagram Response status', response.status);
|
|
194
|
+
logger.debug('Instagram Response body', response.body);
|
|
195
|
+
logger.debug('Instagram Response body.id', response.body.id);
|
|
196
|
+
return response;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async getPost(apiUrl, accessToken, fields, documentId) {
|
|
200
|
+
let response = {};
|
|
201
|
+
try {
|
|
202
|
+
response = await superagent
|
|
203
|
+
.get(apiUrl)
|
|
204
|
+
.set('Accept', 'application/json')
|
|
205
|
+
.set('Content-Type', 'application/json')
|
|
206
|
+
.query({ access_token: accessToken })
|
|
207
|
+
.query({ fields: fields })
|
|
208
|
+
.send();
|
|
209
|
+
} catch (err) {
|
|
210
|
+
let errorText = '';
|
|
211
|
+
if (err && err.response && err.response.body) {
|
|
212
|
+
errorText = `Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}`;
|
|
213
|
+
logger.error(errorText, err.response.body.error);
|
|
214
|
+
} else {
|
|
215
|
+
errorText = `Failed to call instagram api for documentId ${documentId}`;
|
|
216
|
+
logger.error(errorText, err);
|
|
217
|
+
}
|
|
218
|
+
throw new Error(errorText);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (response.status !== 200) {
|
|
222
|
+
logger.error(
|
|
223
|
+
`Failed to call instagram api for documentId ${documentId}`,
|
|
224
|
+
response.body
|
|
225
|
+
);
|
|
226
|
+
let error = new Error(
|
|
227
|
+
`Failed to call instagram api for documentId ${documentId}`
|
|
228
|
+
);
|
|
229
|
+
error.code = response.status;
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
logger.debug('Instagram Response status', response.status);
|
|
233
|
+
logger.debug('Instagram Response body', response.body);
|
|
234
|
+
logger.debug('Instagram Response body.id', response.body.id);
|
|
235
|
+
return response;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async comment(accessToken, payload) {
|
|
239
|
+
let response;
|
|
240
|
+
const { inReplyToId, text, documentId, isMention, sourceId } = payload;
|
|
241
|
+
|
|
242
|
+
logger.debug(
|
|
243
|
+
`Starting to call instagram comment api for documentId ${documentId}`,
|
|
244
|
+
payload
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
this.validate(accessToken, payload);
|
|
248
|
+
|
|
249
|
+
if (isMention) {
|
|
250
|
+
response = await this.mentionRequestApi(
|
|
251
|
+
`${INSTAGRAM_URL}/${sourceId}/mentions`,
|
|
252
|
+
accessToken,
|
|
253
|
+
{ media_id: removePrefix(inReplyToId) },
|
|
254
|
+
text,
|
|
255
|
+
documentId
|
|
256
|
+
);
|
|
257
|
+
} else {
|
|
258
|
+
response = await this.requestApi(
|
|
259
|
+
`${INSTAGRAM_URL}/${removePrefix(inReplyToId)}/comments`,
|
|
260
|
+
accessToken,
|
|
261
|
+
text,
|
|
262
|
+
documentId,
|
|
263
|
+
undefined,
|
|
264
|
+
'qt'
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
logger.info(
|
|
269
|
+
`Native Intagram API Publish Comment Response for documentId ${documentId}`,
|
|
270
|
+
{ status: response.status, ok: response.ok }
|
|
271
|
+
);
|
|
272
|
+
return response.body;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async reply(accessToken, payload) {
|
|
276
|
+
let response;
|
|
277
|
+
const {
|
|
278
|
+
inReplyToId,
|
|
279
|
+
text,
|
|
280
|
+
documentId,
|
|
281
|
+
isMention,
|
|
282
|
+
sourceId,
|
|
283
|
+
threadId,
|
|
284
|
+
adCampaign,
|
|
285
|
+
} = payload;
|
|
286
|
+
|
|
287
|
+
logger.debug(
|
|
288
|
+
`Starting to call instagram comment api for documentId ${documentId}`,
|
|
289
|
+
payload
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
this.validate(accessToken, payload);
|
|
293
|
+
|
|
294
|
+
if (isMention) {
|
|
295
|
+
response = await this.mentionRequestApi(
|
|
296
|
+
`${INSTAGRAM_URL}/${sourceId}/mentions`,
|
|
297
|
+
accessToken,
|
|
298
|
+
{
|
|
299
|
+
media_id: removePrefix(threadId),
|
|
300
|
+
comment_id: removePrefix(inReplyToId),
|
|
301
|
+
},
|
|
302
|
+
text,
|
|
303
|
+
documentId
|
|
304
|
+
);
|
|
305
|
+
} else {
|
|
306
|
+
response = await this.requestApi(
|
|
307
|
+
`${INSTAGRAM_URL}/${removePrefix(inReplyToId)}/replies`,
|
|
308
|
+
accessToken,
|
|
309
|
+
text,
|
|
310
|
+
documentId,
|
|
311
|
+
undefined,
|
|
312
|
+
're',
|
|
313
|
+
adCampaign
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
logger.info(
|
|
318
|
+
`Native Instagram API Publish Reply Response for documentId ${documentId}`,
|
|
319
|
+
{ status: response.status, ok: response.ok }
|
|
320
|
+
);
|
|
321
|
+
return response.body;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async publish(document) {
|
|
325
|
+
const {
|
|
326
|
+
documentId,
|
|
327
|
+
body: {
|
|
328
|
+
content: { text },
|
|
329
|
+
},
|
|
330
|
+
appData: { isMention },
|
|
331
|
+
metaData: {
|
|
332
|
+
discussionType,
|
|
333
|
+
source: { id: sourceId },
|
|
334
|
+
inReplyTo: { id: inReplyToId, profileId, threadId },
|
|
335
|
+
adCampaign,
|
|
336
|
+
},
|
|
337
|
+
systemData: {
|
|
338
|
+
connectionsCredential: credentialId,
|
|
339
|
+
policies: { storage: { privateTo: companyId } = {} } = {},
|
|
340
|
+
} = {},
|
|
341
|
+
} = document;
|
|
342
|
+
|
|
343
|
+
let payload = {
|
|
344
|
+
documentId,
|
|
345
|
+
credentialId,
|
|
346
|
+
text,
|
|
347
|
+
companyId,
|
|
348
|
+
inReplyToId,
|
|
349
|
+
profileId,
|
|
350
|
+
isMention,
|
|
351
|
+
sourceId,
|
|
352
|
+
threadId,
|
|
353
|
+
adCampaign,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
let updatedDocument;
|
|
357
|
+
let apiResponse;
|
|
358
|
+
try {
|
|
359
|
+
const token = await this.credentialsAPI.getToken(
|
|
360
|
+
payload.credentialId,
|
|
361
|
+
payload.companyId
|
|
362
|
+
);
|
|
363
|
+
logger.debug(
|
|
364
|
+
`finished fetching token for instagram for ${documentId} on company ${companyId} `,
|
|
365
|
+
payload
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
switch (discussionType) {
|
|
369
|
+
case 'qt':
|
|
370
|
+
apiResponse = await this.comment(token.token, payload);
|
|
371
|
+
break;
|
|
372
|
+
case 're':
|
|
373
|
+
apiResponse = await this.reply(token.token, payload);
|
|
374
|
+
break;
|
|
375
|
+
case 'dm':
|
|
376
|
+
// get parent account
|
|
377
|
+
const parentCredentialId =
|
|
378
|
+
await this.credentialsAPI.getCredentialIdByAccountId(
|
|
379
|
+
token.parentAccountId,
|
|
380
|
+
'facebook'
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
if (!parentCredentialId) {
|
|
384
|
+
throw 'Parent account id not found';
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// get parent
|
|
388
|
+
const parentCredential = await this.credentialsAPI.getToken(
|
|
389
|
+
parentCredentialId,
|
|
390
|
+
payload.companyId
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
if (!parentCredential) {
|
|
394
|
+
throw 'Parent account not found';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
apiResponse = await this.privateMessage(
|
|
398
|
+
parentCredential.token,
|
|
399
|
+
payload
|
|
400
|
+
);
|
|
401
|
+
break;
|
|
402
|
+
default:
|
|
403
|
+
throw new Error('Unsupported discussion type');
|
|
404
|
+
}
|
|
405
|
+
} catch (err) {
|
|
406
|
+
logger.error(documentId + ' - exception details ' + err, err);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return apiResponse;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async hide(accessToken, payload) {
|
|
413
|
+
let response;
|
|
414
|
+
const { externalId, documentId } = payload;
|
|
415
|
+
|
|
416
|
+
logger.debug(
|
|
417
|
+
`Starting to call instagram hide api for documentId ${documentId}`,
|
|
418
|
+
{ payload }
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
response = await this.hideApi(
|
|
422
|
+
`${INSTAGRAM_URL}/${removePrefix(externalId)}`,
|
|
423
|
+
accessToken,
|
|
424
|
+
true,
|
|
425
|
+
documentId
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
logger.info(
|
|
429
|
+
`Native Instagram API Hide Response for documentId ${documentId}`,
|
|
430
|
+
{ status: response.status, ok: response.ok }
|
|
431
|
+
);
|
|
432
|
+
return response.body;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async unhide(accessToken, payload) {
|
|
436
|
+
let response;
|
|
437
|
+
const { externalId, documentId } = payload;
|
|
438
|
+
|
|
439
|
+
logger.debug(
|
|
440
|
+
`Starting to call instagram unhide api for documentId ${documentId}`,
|
|
441
|
+
payload
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
response = await this.hideApi(
|
|
445
|
+
`${INSTAGRAM_URL}/${removePrefix(externalId)}`,
|
|
446
|
+
accessToken,
|
|
447
|
+
false,
|
|
448
|
+
documentId
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
logger.info(
|
|
452
|
+
`Native Instagram API Unhide Response for documentId ${documentId}`,
|
|
453
|
+
{ status: response.status, ok: response.ok }
|
|
454
|
+
);
|
|
455
|
+
return response.body;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async privateMessage(accessToken, payload) {
|
|
459
|
+
let response;
|
|
460
|
+
const { profileId, text, documentId, isMention, sourceId, threadId } =
|
|
461
|
+
payload;
|
|
462
|
+
|
|
463
|
+
logger.debug(
|
|
464
|
+
`Starting to call instagram private message api for documentId ${documentId}`,
|
|
465
|
+
payload
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
this.validate(accessToken, payload);
|
|
469
|
+
|
|
470
|
+
response = await this.requestApi(
|
|
471
|
+
`${INSTAGRAM_URL}/v10.0/me/messages`,
|
|
472
|
+
accessToken,
|
|
473
|
+
// this sends in query string leaving out :shrug:
|
|
474
|
+
undefined,
|
|
475
|
+
documentId,
|
|
476
|
+
{
|
|
477
|
+
recipient: {
|
|
478
|
+
// not :100: this is right
|
|
479
|
+
id: removePrefix(profileId),
|
|
480
|
+
},
|
|
481
|
+
message: {
|
|
482
|
+
text,
|
|
483
|
+
},
|
|
484
|
+
// this apparently allows up to 7 days after message 🤔
|
|
485
|
+
tag: 'HUMAN_AGENT',
|
|
486
|
+
},
|
|
487
|
+
'dm'
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
logger.info(
|
|
491
|
+
`Native Instagram API Publish Private Message Response for documentId ${documentId}`,
|
|
492
|
+
{ status: response.status, ok: response.ok }
|
|
493
|
+
);
|
|
494
|
+
return response.body;
|
|
495
|
+
}
|
|
496
|
+
}
|