@meltwater/conversations-api-services 1.0.19 → 1.0.21
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/.github/workflows/release.yml +2 -0
- package/babel.config.js +18 -0
- package/dist/cjs/data-access/http/InstagramVideoClient.js +42 -0
- package/dist/cjs/data-access/http/WarpZoneApi.client.js +32 -0
- package/dist/cjs/data-access/http/amazonS3.js +44 -0
- package/dist/cjs/data-access/http/asset-manager-tvm.client.js +35 -0
- package/dist/cjs/data-access/http/companiesApi.client.js +38 -0
- package/dist/cjs/data-access/http/credentialsApi.client.js +102 -0
- package/dist/cjs/data-access/http/entitlementsApi.client.js +40 -0
- package/dist/cjs/data-access/http/facebook.native.js +344 -0
- package/dist/cjs/data-access/http/facebookApi.client.js +631 -0
- package/dist/cjs/data-access/http/featureToggleApi.client.js +31 -0
- package/dist/cjs/data-access/http/identityServices.client.js +97 -0
- package/dist/cjs/data-access/http/instagramApi.client.js +428 -0
- package/dist/cjs/data-access/http/ir.client.js +242 -0
- package/dist/cjs/data-access/http/linkedInApi.client.js +491 -0
- package/dist/cjs/data-access/http/masf.client.js +101 -0
- package/dist/cjs/data-access/http/tiktok.native.js +162 -0
- package/dist/cjs/data-access/http/tiktokApi.client.js +441 -0
- package/dist/cjs/data-access/index.js +132 -0
- package/dist/cjs/errors/engage-error.js +16 -0
- package/dist/cjs/errors/http-error.js +23 -0
- package/dist/cjs/lib/applicationTags.helpers.js +30 -0
- package/dist/cjs/lib/configuration.js +14 -0
- package/dist/cjs/lib/document-action-events.js +12 -0
- package/dist/cjs/lib/externalId.helpers.js +19 -0
- package/dist/cjs/lib/hidden.helpers.js +13 -0
- package/dist/cjs/lib/hiddenComment.helper.js +119 -0
- package/dist/cjs/lib/logger.helpers.js +68 -0
- package/dist/cjs/lib/logger.js +23 -0
- package/dist/cjs/lib/message.helpers.js +58 -0
- package/dist/cjs/lib/metrics.helper.js +97 -0
- package/dist/esm/data-access/http/InstagramVideoClient.js +34 -0
- package/dist/esm/data-access/http/WarpZoneApi.client.js +24 -0
- package/dist/esm/data-access/http/amazonS3.js +37 -0
- package/dist/esm/data-access/http/asset-manager-tvm.client.js +28 -0
- package/dist/esm/data-access/http/companiesApi.client.js +30 -0
- package/dist/esm/data-access/http/credentialsApi.client.js +92 -0
- package/dist/esm/data-access/http/entitlementsApi.client.js +32 -0
- package/dist/esm/data-access/http/facebook.native.js +325 -0
- package/dist/esm/data-access/http/facebookApi.client.js +621 -0
- package/dist/esm/data-access/http/featureToggleApi.client.js +23 -0
- package/dist/esm/data-access/http/identityServices.client.js +89 -0
- package/dist/esm/data-access/http/instagramApi.client.js +420 -0
- package/dist/esm/data-access/http/ir.client.js +234 -0
- package/dist/esm/data-access/http/linkedInApi.client.js +481 -0
- package/dist/esm/data-access/http/masf.client.js +93 -0
- package/dist/esm/data-access/http/tiktok.native.js +146 -0
- package/dist/esm/data-access/http/tiktokApi.client.js +433 -0
- package/dist/esm/data-access/index.js +30 -0
- package/dist/esm/errors/engage-error.js +9 -0
- package/dist/esm/errors/http-error.js +16 -0
- package/dist/esm/lib/applicationTags.helpers.js +22 -0
- package/dist/esm/lib/configuration.js +8 -0
- package/dist/esm/lib/document-action-events.js +6 -0
- package/dist/esm/lib/externalId.helpers.js +12 -0
- package/dist/esm/lib/hidden.helpers.js +6 -0
- package/dist/esm/lib/hiddenComment.helper.js +112 -0
- package/dist/esm/lib/logger.helpers.js +60 -0
- package/dist/esm/lib/logger.js +16 -0
- package/dist/esm/lib/message.helpers.js +52 -0
- package/dist/esm/lib/metrics.helper.js +90 -0
- package/package.json +14 -4
- package/src/data-access/http/facebook.native.js +542 -0
- package/src/data-access/http/tiktok.native.js +248 -0
- package/src/data-access/index.js +4 -0
- package/src/errors/engage-error.js +11 -0
- package/src/errors/http-error.js +19 -0
- package/src/lib/logger.helpers.js +15 -0
- package/src/lib/message.helpers.js +7 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import { loggerError } from '../../lib/logger.helpers.js';
|
|
3
|
+
import { removePrefix } from '../../lib/externalId.helpers.js';
|
|
4
|
+
const TIKTOK_API_URL = "https://business-api.tiktok.com/open_api/v1.3/business/";
|
|
5
|
+
export async function comment(token, business_id, video_id, text, logger) {
|
|
6
|
+
const {
|
|
7
|
+
body: publishedMessage
|
|
8
|
+
} = (await sendPost(token, 'comment/create/', {
|
|
9
|
+
business_id: business_id,
|
|
10
|
+
video_id: video_id.startsWith('tt_') ? video_id.split('_')[2] : video_id,
|
|
11
|
+
text
|
|
12
|
+
}, logger)) || {};
|
|
13
|
+
return publishedMessage;
|
|
14
|
+
}
|
|
15
|
+
export async function reply(token, videoId, sourceId, inReplyToId, text, logger) {
|
|
16
|
+
const {
|
|
17
|
+
body: publishedMessage
|
|
18
|
+
} = (await sendPost(token, 'comment/reply/create/', {
|
|
19
|
+
business_id: sourceId,
|
|
20
|
+
comment_id: inReplyToId,
|
|
21
|
+
video_id: removePrefix(videoId),
|
|
22
|
+
text
|
|
23
|
+
}, logger)) || {};
|
|
24
|
+
return publishedMessage;
|
|
25
|
+
}
|
|
26
|
+
export async function like(token, externalId, sourceId, logger) {
|
|
27
|
+
const {
|
|
28
|
+
body: publishedMessage
|
|
29
|
+
} = (await sendPost(token, 'comment/like/', {
|
|
30
|
+
business_id: removePrefix(sourceId),
|
|
31
|
+
comment_id: removePrefix(externalId),
|
|
32
|
+
action: 'LIKE'
|
|
33
|
+
}, logger)) || {};
|
|
34
|
+
return publishedMessage;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Conversations says this is only allowed on Native...
|
|
38
|
+
export async function unlike(token, inReplyToId, sourceId, logger) {
|
|
39
|
+
const {
|
|
40
|
+
body: publishedMessage
|
|
41
|
+
} = (await sendPost(token, 'comment/like/', {
|
|
42
|
+
business_id: removePrefix(sourceId),
|
|
43
|
+
comment_id: removePrefix(inReplyToId),
|
|
44
|
+
action: 'unlike'
|
|
45
|
+
}, logger)) || {};
|
|
46
|
+
return publishedMessage;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Conversations says this is only allowed on Native...
|
|
50
|
+
export async function hide(token, externalId, sourceId, video_id, logger) {
|
|
51
|
+
const {
|
|
52
|
+
body: publishedMessage
|
|
53
|
+
} = (await sendPost(token, 'comment/hide/', {
|
|
54
|
+
business_id: removePrefix(sourceId),
|
|
55
|
+
comment_id: removePrefix(externalId),
|
|
56
|
+
video_id: removePrefix(video_id),
|
|
57
|
+
action: 'HIDE'
|
|
58
|
+
}, logger)) || {};
|
|
59
|
+
return publishedMessage;
|
|
60
|
+
}
|
|
61
|
+
export async function unhide(token, externalId, sourceId, video_id, logger) {
|
|
62
|
+
const {
|
|
63
|
+
body: publishedMessage
|
|
64
|
+
} = (await sendPost(token, 'comment/hide/', {
|
|
65
|
+
business_id: removePrefix(sourceId),
|
|
66
|
+
comment_id: removePrefix(externalId),
|
|
67
|
+
video_id: removePrefix(video_id),
|
|
68
|
+
action: 'UNHIDE'
|
|
69
|
+
}, logger)) || {};
|
|
70
|
+
return publishedMessage;
|
|
71
|
+
}
|
|
72
|
+
export async function deleteComment(token, inReplyToId, profileId, logger) {
|
|
73
|
+
const {
|
|
74
|
+
body: publishedMessage
|
|
75
|
+
} = (await sendPost(token, 'comment/delete/', {
|
|
76
|
+
business_id: profileId,
|
|
77
|
+
comment_id: inReplyToId
|
|
78
|
+
}, logger)) || {};
|
|
79
|
+
return publishedMessage;
|
|
80
|
+
}
|
|
81
|
+
export async function getProfile(token, business_id) {
|
|
82
|
+
let fields = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '["username", "display_name", "profile_image"]';
|
|
83
|
+
let logger = arguments.length > 3 ? arguments[3] : undefined;
|
|
84
|
+
const profile = (await sendRequest(token, `get/?business_id=${business_id}&fields=${fields}`, logger)) || {};
|
|
85
|
+
return profile;
|
|
86
|
+
}
|
|
87
|
+
export async function getStats(token, business_id, externalIds) {
|
|
88
|
+
let paramString = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'video/list/';
|
|
89
|
+
let logger = arguments.length > 4 ? arguments[4] : undefined;
|
|
90
|
+
const profile = (await sendRequest(token, `${paramString}?business_id=${business_id}&fields=["item_id","create_time","thumbnail_url","share_url","embed_url","caption","video_views","likes","comments","shares"]&filters=${JSON.stringify({
|
|
91
|
+
video_ids: externalIds
|
|
92
|
+
})}`, logger)) || {};
|
|
93
|
+
return profile;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// This doesn't appear to be used in Conversations either
|
|
97
|
+
export async function getPostData(token, business_id, video_id) {
|
|
98
|
+
let fields = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '["item_id", "thumbnail_url", "create_time", "username", "display_name", "profile_image"]';
|
|
99
|
+
let logger = arguments.length > 4 ? arguments[4] : undefined;
|
|
100
|
+
const video_ids = `["${video_id}"]`;
|
|
101
|
+
const profile = (await sendRequest(token, `video/list?business_id=${business_id}&fields=${fields}&filters={"video_ids":${video_ids}}`, logger)) || {};
|
|
102
|
+
return profile;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// assumes batch calls are using the same credential
|
|
106
|
+
async function sendRequest(token) {
|
|
107
|
+
let paramString = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
|
108
|
+
let logger = arguments.length > 2 ? arguments[2] : undefined;
|
|
109
|
+
let response = {};
|
|
110
|
+
try {
|
|
111
|
+
response = await superagent.get(TIKTOK_API_URL + paramString).set('Accept', 'application/json').set('Content-Type', 'application/json').set('Access-Token', token).send();
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (err && err.response && err.response.body && err.response.body.error) {
|
|
114
|
+
loggerError(logger, `Failed to call tiktok api for paramString ${paramString}: ${err.response.body.error.message}`);
|
|
115
|
+
} else {
|
|
116
|
+
loggerError(logger, `Failed to call tiktok api for paramString ${paramString}`, err);
|
|
117
|
+
}
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
120
|
+
if (response.status !== 200) {
|
|
121
|
+
loggerError(logger, `Failed to call tiktok api`, {
|
|
122
|
+
responseBody: JSON.stringify(response.body)
|
|
123
|
+
});
|
|
124
|
+
let error = new Error(`Failed to call tiktok api`);
|
|
125
|
+
error.code = response.status;
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
return response.body;
|
|
129
|
+
}
|
|
130
|
+
async function sendPost(token) {
|
|
131
|
+
let paramString = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
|
132
|
+
let postData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
|
|
133
|
+
let logger = arguments.length > 3 ? arguments[3] : undefined;
|
|
134
|
+
let response = {};
|
|
135
|
+
try {
|
|
136
|
+
response = await superagent.post(TIKTOK_API_URL + paramString).set('Accept', 'application/json').set('Content-Type', 'application/json').set('Access-Token', token).send(postData);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (err && err.response && err.response.body && err.response.body.error) {
|
|
139
|
+
loggerError(logger, `Failed to call tiktok api for paramString ${paramString}: ${err.response.body.error.message}`);
|
|
140
|
+
} else {
|
|
141
|
+
loggerError(logger, `Failed to call tiktok api for paramString ${paramString}`, err);
|
|
142
|
+
}
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
return response;
|
|
146
|
+
}
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import configuration from '../../lib/configuration.js';
|
|
3
|
+
import logger from '../../lib/logger.js';
|
|
4
|
+
import { removePrefix } from '../../lib/externalId.helpers.js';
|
|
5
|
+
import { CredentialsApiClient } from '../http/credentialsApi.client.js';
|
|
6
|
+
import { MeltwaterAttributes } from '../../lib/logger.helpers.js';
|
|
7
|
+
export class TikTokApiClient {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.tiktokURL = configuration.get('TIKTOK_API_URL');
|
|
10
|
+
this.credentialsAPI = new CredentialsApiClient();
|
|
11
|
+
}
|
|
12
|
+
async getAuthorization(document) {
|
|
13
|
+
const {
|
|
14
|
+
documentId,
|
|
15
|
+
systemData: {
|
|
16
|
+
connectionsCredential: credentialId,
|
|
17
|
+
policies: {
|
|
18
|
+
storage: {
|
|
19
|
+
privateTo: companyId
|
|
20
|
+
} = {}
|
|
21
|
+
} = {}
|
|
22
|
+
} = {}
|
|
23
|
+
} = document;
|
|
24
|
+
try {
|
|
25
|
+
const credential = await this.credentialsAPI.getToken(credentialId, companyId);
|
|
26
|
+
return `${credential.token}`;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
logger.error(`${documentId} - error getting tiktok token - ${error.code}`, error, {
|
|
29
|
+
[MeltwaterAttributes.DOCUMENTID]: documentId,
|
|
30
|
+
[MeltwaterAttributes.COMPANYID]: companyId,
|
|
31
|
+
[MeltwaterAttributes.CREDENTIALID]: credentialId
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async sendPost(_ref) {
|
|
36
|
+
let {
|
|
37
|
+
paramString = '',
|
|
38
|
+
headers = {},
|
|
39
|
+
document,
|
|
40
|
+
postData = undefined
|
|
41
|
+
} = _ref;
|
|
42
|
+
let response = {};
|
|
43
|
+
try {
|
|
44
|
+
response = await superagent.post(this.tiktokURL + paramString).set('Accept', 'application/json').set('Content-Type', 'application/json').set('Access-Token', await this.getAuthorization(document)).send(postData);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
let errorText = '';
|
|
47
|
+
if (err && err.response && err.response.body && err.response.body.error) {
|
|
48
|
+
errorText = `Failed to call tiktok api for paramString ${paramString}: ${err.response.body.error.message}`;
|
|
49
|
+
logger.error(errorText);
|
|
50
|
+
} else {
|
|
51
|
+
errorText = `Failed to call tiktok api for paramString ${paramString}`;
|
|
52
|
+
logger.error(errorText, err);
|
|
53
|
+
}
|
|
54
|
+
throw new Error(errorText);
|
|
55
|
+
}
|
|
56
|
+
return response;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// assumes batch calls are using the same credential
|
|
60
|
+
async sendRequest(_ref2) {
|
|
61
|
+
let {
|
|
62
|
+
paramString = '',
|
|
63
|
+
headers = {},
|
|
64
|
+
document
|
|
65
|
+
} = _ref2;
|
|
66
|
+
let response = {};
|
|
67
|
+
try {
|
|
68
|
+
response = await superagent.get(this.tiktokURL + paramString).set('Accept', 'application/json').set('Content-Type', 'application/json').set('Access-Token', await this.getAuthorization(document)).send();
|
|
69
|
+
} catch (err) {
|
|
70
|
+
let errorText = '';
|
|
71
|
+
if (err && err.response && err.response.body && err.response.body.error) {
|
|
72
|
+
errorText = `Failed to call tiktok api for paramString ${paramString}: ${err.response.body.error.message}`;
|
|
73
|
+
logger.error(errorText);
|
|
74
|
+
} else {
|
|
75
|
+
errorText = `Failed to call tiktok api for paramString ${paramString}`;
|
|
76
|
+
logger.error(errorText, err);
|
|
77
|
+
}
|
|
78
|
+
throw new Error(errorText);
|
|
79
|
+
}
|
|
80
|
+
if (response.status !== 200) {
|
|
81
|
+
logger.error(`Failed to call tiktok api for documentId ${document.documentId}`, response.body, {
|
|
82
|
+
[MeltwaterAttributes.DOCUMENTID]: document.documentId
|
|
83
|
+
});
|
|
84
|
+
let error = new Error(`Failed to call tiktok api for documentId ${document.documentId}`);
|
|
85
|
+
error.code = response.status;
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
return response.body;
|
|
89
|
+
}
|
|
90
|
+
async getChannelInfoForDocuments(documents) {
|
|
91
|
+
const channelInfo = await this.getStatsForDocuments(documents, 'metaData.authors.0.authorInfo.externalId', {
|
|
92
|
+
og: ['video/list/']
|
|
93
|
+
});
|
|
94
|
+
return channelInfo;
|
|
95
|
+
}
|
|
96
|
+
async getStatsForDocuments(documents) {
|
|
97
|
+
let externalIdFrom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'metaData.externalId';
|
|
98
|
+
let paramsByType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
|
|
99
|
+
og: ['video/list/']
|
|
100
|
+
};
|
|
101
|
+
// for batching separate og and comment/reply requests per credential
|
|
102
|
+
const documentsByTypeAndCredentials = documents.reduce((retObj, document, index) => {
|
|
103
|
+
const paramArray = paramsByType[document.metaData.discussionType];
|
|
104
|
+
if (paramArray) {
|
|
105
|
+
paramArray.forEach(paramString => {
|
|
106
|
+
let discussionTypeBucket = retObj[`${paramString}${document.systemData.connectionsCredential}`];
|
|
107
|
+
if (!discussionTypeBucket) {
|
|
108
|
+
discussionTypeBucket = {
|
|
109
|
+
paramString,
|
|
110
|
+
document,
|
|
111
|
+
externalIds: [],
|
|
112
|
+
externalIdToDocumentIndex: {}
|
|
113
|
+
};
|
|
114
|
+
retObj[`${paramString}${document.systemData.connectionsCredential}`] = discussionTypeBucket;
|
|
115
|
+
}
|
|
116
|
+
const externalIdTrimmed = removePrefix(
|
|
117
|
+
// javascript weirdness to get around '.' notation
|
|
118
|
+
externalIdFrom.split('.').reduce((a, b) => a[b], document));
|
|
119
|
+
discussionTypeBucket.externalIds.push(externalIdTrimmed);
|
|
120
|
+
discussionTypeBucket.externalIdToDocumentIndex[externalIdTrimmed] = index;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return retObj;
|
|
124
|
+
}, {});
|
|
125
|
+
const sortedResults = Array(documents.length).fill();
|
|
126
|
+
await Promise.all(Object.values(documentsByTypeAndCredentials).map(async documentsByTypeAndCredential => {
|
|
127
|
+
if (documentsByTypeAndCredential.externalIds.length) {
|
|
128
|
+
const {
|
|
129
|
+
metaData: {
|
|
130
|
+
inReplyTo: {
|
|
131
|
+
profileId: business_id
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} = documentsByTypeAndCredential.document;
|
|
135
|
+
const trimmedBusinessId = removePrefix(business_id);
|
|
136
|
+
const requestResult = await this.sendRequest({
|
|
137
|
+
paramString: `${documentsByTypeAndCredential.paramString}?business_id=${trimmedBusinessId}&fields=["item_id","create_time","thumbnail_url","share_url","embed_url","caption","video_views","likes","comments","shares"]&filters=${JSON.stringify({
|
|
138
|
+
video_ids: documentsByTypeAndCredential.externalIds
|
|
139
|
+
})}`,
|
|
140
|
+
document: documentsByTypeAndCredential.document
|
|
141
|
+
});
|
|
142
|
+
requestResult.data.videos.forEach(_ref3 => {
|
|
143
|
+
let {
|
|
144
|
+
item_id,
|
|
145
|
+
create_time,
|
|
146
|
+
thumbnail_url,
|
|
147
|
+
share_url,
|
|
148
|
+
embed_url,
|
|
149
|
+
caption,
|
|
150
|
+
video_views,
|
|
151
|
+
likes,
|
|
152
|
+
comments,
|
|
153
|
+
shares,
|
|
154
|
+
...args
|
|
155
|
+
} = _ref3;
|
|
156
|
+
const documentIndex = documentsByTypeAndCredential.externalIdToDocumentIndex[item_id];
|
|
157
|
+
sortedResults[documentIndex] = {
|
|
158
|
+
...sortedResults[documentIndex],
|
|
159
|
+
item_id,
|
|
160
|
+
create_time,
|
|
161
|
+
thumbnail_url,
|
|
162
|
+
externalId: documents[documentIndex].metaData.externalId,
|
|
163
|
+
share_url,
|
|
164
|
+
embed_url,
|
|
165
|
+
caption,
|
|
166
|
+
video_views,
|
|
167
|
+
likes,
|
|
168
|
+
comments,
|
|
169
|
+
shares
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}));
|
|
174
|
+
return sortedResults;
|
|
175
|
+
}
|
|
176
|
+
async publish(message, videoId, markMessageAsCompleteDocumentId) {
|
|
177
|
+
const {
|
|
178
|
+
metaData: {
|
|
179
|
+
discussionType
|
|
180
|
+
}
|
|
181
|
+
} = message;
|
|
182
|
+
let publishedMessage;
|
|
183
|
+
switch (discussionType) {
|
|
184
|
+
case 'qt':
|
|
185
|
+
publishedMessage = await this.insertComment(message);
|
|
186
|
+
break;
|
|
187
|
+
case 're':
|
|
188
|
+
publishedMessage = await this.insertReply(videoId, message, markMessageAsCompleteDocumentId);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
return publishedMessage;
|
|
192
|
+
}
|
|
193
|
+
async insertComment(document) {
|
|
194
|
+
const {
|
|
195
|
+
body: {
|
|
196
|
+
content: {
|
|
197
|
+
text: text
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
metaData: {
|
|
201
|
+
source: {
|
|
202
|
+
id: business_id
|
|
203
|
+
},
|
|
204
|
+
inReplyTo: {
|
|
205
|
+
id: video_id
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} = document;
|
|
209
|
+
const {
|
|
210
|
+
body: publishedMessage
|
|
211
|
+
} = (await this.sendPost({
|
|
212
|
+
paramString: 'comment/create/',
|
|
213
|
+
postData: {
|
|
214
|
+
business_id,
|
|
215
|
+
video_id,
|
|
216
|
+
text
|
|
217
|
+
},
|
|
218
|
+
document
|
|
219
|
+
})) || {};
|
|
220
|
+
return publishedMessage;
|
|
221
|
+
}
|
|
222
|
+
async insertReply(videoId, document, markMessageAsCompleteDocumentId) {
|
|
223
|
+
const {
|
|
224
|
+
body: {
|
|
225
|
+
content: {
|
|
226
|
+
text: text
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
metaData: {
|
|
230
|
+
inReplyTo: {
|
|
231
|
+
id: comment_id,
|
|
232
|
+
profileId: business_id
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} = document;
|
|
236
|
+
const {
|
|
237
|
+
body: publishedMessage
|
|
238
|
+
} = (await this.sendPost({
|
|
239
|
+
paramString: 'comment/reply/create/',
|
|
240
|
+
postData: {
|
|
241
|
+
business_id: document.metaData.source.id,
|
|
242
|
+
comment_id: markMessageAsCompleteDocumentId || comment_id,
|
|
243
|
+
video_id: removePrefix(videoId),
|
|
244
|
+
text
|
|
245
|
+
},
|
|
246
|
+
document
|
|
247
|
+
})) || {};
|
|
248
|
+
return publishedMessage;
|
|
249
|
+
}
|
|
250
|
+
async toggleLike(document) {
|
|
251
|
+
const {
|
|
252
|
+
enrichments: {
|
|
253
|
+
socialScores: {
|
|
254
|
+
tt_liked_by_user: likedByUser
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} = document;
|
|
258
|
+
try {
|
|
259
|
+
let likeResponse = await (!likedByUser ? this.like(document) : this.unlike(document));
|
|
260
|
+
return likeResponse;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
logger.error(`${document} - error recieved - ${error.code}`, error);
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async toggleHide(document) {
|
|
267
|
+
let parentDoc = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
|
|
268
|
+
const {
|
|
269
|
+
documentId,
|
|
270
|
+
appData: {
|
|
271
|
+
hidden: hiddenOnNative
|
|
272
|
+
}
|
|
273
|
+
} = document;
|
|
274
|
+
let response;
|
|
275
|
+
try {
|
|
276
|
+
if (hiddenOnNative) {
|
|
277
|
+
response = await this.hide(document, parentDoc);
|
|
278
|
+
} else {
|
|
279
|
+
response = await this.unhide(document, parentDoc);
|
|
280
|
+
}
|
|
281
|
+
logger.info(`Native Tiktok API Hide Response for documentId ${documentId}`, {
|
|
282
|
+
status: response.status,
|
|
283
|
+
ok: response.ok,
|
|
284
|
+
[MeltwaterAttributes.DOCUMENTID]: documentId
|
|
285
|
+
});
|
|
286
|
+
if (response.message === 'OK') {
|
|
287
|
+
return response;
|
|
288
|
+
}
|
|
289
|
+
} catch (error) {
|
|
290
|
+
logger.error(`${documentId} - error recieved - ${error.code}`, error, {
|
|
291
|
+
[MeltwaterAttributes.DOCUMENTID]: documentId
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async like(document) {
|
|
296
|
+
const {
|
|
297
|
+
metaData: {
|
|
298
|
+
externalId: comment_id,
|
|
299
|
+
source: {
|
|
300
|
+
id: sourceId
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} = document;
|
|
304
|
+
const {
|
|
305
|
+
body: publishedMessage
|
|
306
|
+
} = (await this.sendPost({
|
|
307
|
+
paramString: 'comment/like/',
|
|
308
|
+
postData: {
|
|
309
|
+
business_id: removePrefix(sourceId),
|
|
310
|
+
comment_id: removePrefix(comment_id),
|
|
311
|
+
action: 'LIKE'
|
|
312
|
+
},
|
|
313
|
+
document
|
|
314
|
+
})) || {};
|
|
315
|
+
return publishedMessage;
|
|
316
|
+
}
|
|
317
|
+
async unlike(document) {
|
|
318
|
+
const {
|
|
319
|
+
metaData: {
|
|
320
|
+
inReplyTo: {
|
|
321
|
+
id: comment_id
|
|
322
|
+
},
|
|
323
|
+
source: {
|
|
324
|
+
id: sourceId
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} = document;
|
|
328
|
+
const {
|
|
329
|
+
body: publishedMessage
|
|
330
|
+
} = (await this.sendPost({
|
|
331
|
+
paramString: 'comment/like/',
|
|
332
|
+
postData: {
|
|
333
|
+
business_id: removePrefix(sourceId),
|
|
334
|
+
comment_id: removePrefix(comment_id),
|
|
335
|
+
action: 'unlike'
|
|
336
|
+
},
|
|
337
|
+
document
|
|
338
|
+
})) || {};
|
|
339
|
+
return publishedMessage;
|
|
340
|
+
}
|
|
341
|
+
async hide(document) {
|
|
342
|
+
let parentDoc = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
|
|
343
|
+
const {
|
|
344
|
+
metaData: {
|
|
345
|
+
externalId: comment_id,
|
|
346
|
+
source: {
|
|
347
|
+
id: sourceId
|
|
348
|
+
},
|
|
349
|
+
discussionType
|
|
350
|
+
}
|
|
351
|
+
} = document;
|
|
352
|
+
|
|
353
|
+
//very small chance we get externalid instead of id
|
|
354
|
+
const video_id = discussionType === 're' ? parentDoc?.metaData?.inReplyTo?.id || parentDoc?.metaData?.inReplyTo?.externalId : document?.metaData?.inReplyTo?.id || document?.metaData?.inReplyTo?.externalId;
|
|
355
|
+
const {
|
|
356
|
+
body: publishedMessage
|
|
357
|
+
} = (await this.sendPost({
|
|
358
|
+
paramString: 'comment/hide/',
|
|
359
|
+
postData: {
|
|
360
|
+
business_id: removePrefix(sourceId),
|
|
361
|
+
comment_id: removePrefix(comment_id),
|
|
362
|
+
video_id: removePrefix(video_id),
|
|
363
|
+
action: 'HIDE'
|
|
364
|
+
},
|
|
365
|
+
document
|
|
366
|
+
})) || {};
|
|
367
|
+
return publishedMessage;
|
|
368
|
+
}
|
|
369
|
+
async unhide(document) {
|
|
370
|
+
let parentDoc = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
|
|
371
|
+
const {
|
|
372
|
+
metaData: {
|
|
373
|
+
externalId: comment_id,
|
|
374
|
+
source: {
|
|
375
|
+
id: sourceId
|
|
376
|
+
},
|
|
377
|
+
discussionType
|
|
378
|
+
}
|
|
379
|
+
} = document;
|
|
380
|
+
const video_id = discussionType === 're' ? parentDoc?.metaData?.inReplyTo?.id : document?.metaData?.inReplyTo?.id;
|
|
381
|
+
const {
|
|
382
|
+
body: publishedMessage
|
|
383
|
+
} = (await this.sendPost({
|
|
384
|
+
paramString: 'comment/hide/',
|
|
385
|
+
postData: {
|
|
386
|
+
business_id: removePrefix(sourceId),
|
|
387
|
+
comment_id: removePrefix(comment_id),
|
|
388
|
+
video_id: removePrefix(video_id),
|
|
389
|
+
action: 'UNHIDE'
|
|
390
|
+
},
|
|
391
|
+
document
|
|
392
|
+
})) || {};
|
|
393
|
+
return publishedMessage;
|
|
394
|
+
}
|
|
395
|
+
async deleteComment(document) {
|
|
396
|
+
const {
|
|
397
|
+
metaData: {
|
|
398
|
+
inReplyTo: {
|
|
399
|
+
id: comment_id,
|
|
400
|
+
profileId: business_id
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} = document;
|
|
404
|
+
const {
|
|
405
|
+
body: publishedMessage
|
|
406
|
+
} = (await this.sendPost({
|
|
407
|
+
paramString: 'comment/delete/',
|
|
408
|
+
postData: {
|
|
409
|
+
business_id,
|
|
410
|
+
comment_id
|
|
411
|
+
},
|
|
412
|
+
document
|
|
413
|
+
})) || {};
|
|
414
|
+
return publishedMessage;
|
|
415
|
+
}
|
|
416
|
+
async getProfile(business_id, document) {
|
|
417
|
+
const fields = '["username", "display_name", "profile_image"]';
|
|
418
|
+
const profile = (await this.sendRequest({
|
|
419
|
+
paramString: `get/?business_id=${business_id}&fields=${fields}`,
|
|
420
|
+
document
|
|
421
|
+
})) || {};
|
|
422
|
+
return profile;
|
|
423
|
+
}
|
|
424
|
+
async getPostData(business_id, video_id, document) {
|
|
425
|
+
const fields = '["item_id", "thumbnail_url", "create_time", "username", "display_name", "profile_image"]';
|
|
426
|
+
const video_ids = `["${video_id}"]`;
|
|
427
|
+
const profile = (await this.sendRequest({
|
|
428
|
+
paramString: `video/list?business_id=${business_id}&fields=${fields}&filters={"video_ids":${video_ids}}`,
|
|
429
|
+
document
|
|
430
|
+
})) || {};
|
|
431
|
+
return profile;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { awsS3Client } from './http/amazonS3.js';
|
|
2
|
+
import { assetManagerTvmRepository } from './http/asset-manager-tvm.client.js';
|
|
3
|
+
import { CompanyApiClient } from './http/companiesApi.client.js';
|
|
4
|
+
import { CredentialsApiClient } from './http/credentialsApi.client.js';
|
|
5
|
+
import { EntitlementsApiClient } from './http/entitlementsApi.client.js';
|
|
6
|
+
import { FacebookApiClient } from './http/facebookApi.client.js';
|
|
7
|
+
import { FeatureToggleClient } from './http/featureToggleApi.client.js';
|
|
8
|
+
import { IdentityServicesClient } from './http/identityServices.client.js';
|
|
9
|
+
import { InstagramApiClient } from './http/instagramApi.client.js';
|
|
10
|
+
import { InstagramVideoClient } from './http/InstagramVideoClient.js';
|
|
11
|
+
import { IRClient } from './http/ir.client.js';
|
|
12
|
+
import { LinkedInApiClient, getOrganization, getProfile } from './http/linkedInApi.client.js';
|
|
13
|
+
import { TikTokApiClient } from './http/tiktokApi.client.js';
|
|
14
|
+
import { MasfClient } from './http/masf.client.js';
|
|
15
|
+
import { WarpZoneApiClient } from './http/WarpZoneApi.client.js';
|
|
16
|
+
import * as messageHelpers from '../lib/message.helpers.js';
|
|
17
|
+
import * as applicationTagFunctions from '../lib/applicationTags.helpers.js';
|
|
18
|
+
import * as hiddenHelpers from '../lib/hidden.helpers.js';
|
|
19
|
+
import * as FacebookNative from './http/facebook.native.js';
|
|
20
|
+
import * as TiktokNative from './http/tiktok.native.js';
|
|
21
|
+
const DocumentHelperFunctions = {
|
|
22
|
+
...messageHelpers,
|
|
23
|
+
...applicationTagFunctions,
|
|
24
|
+
...hiddenHelpers
|
|
25
|
+
};
|
|
26
|
+
const LinkedInHelpers = {
|
|
27
|
+
getOrganization,
|
|
28
|
+
getProfile
|
|
29
|
+
};
|
|
30
|
+
export { FacebookNative, TiktokNative, awsS3Client, assetManagerTvmRepository, CompanyApiClient, CredentialsApiClient, EntitlementsApiClient, FacebookApiClient, FeatureToggleClient, IdentityServicesClient, InstagramApiClient, InstagramVideoClient, IRClient, LinkedInApiClient, TikTokApiClient, MasfClient, WarpZoneApiClient, DocumentHelperFunctions, LinkedInHelpers };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EngageError } from "./engage-error";
|
|
2
|
+
export class EngageHttpError extends EngageError {
|
|
3
|
+
status;
|
|
4
|
+
statusText;
|
|
5
|
+
response;
|
|
6
|
+
headers;
|
|
7
|
+
constructor(status, statusText, response, headers) {
|
|
8
|
+
super(`HTTP ${status}: ${statusText}`, statusText);
|
|
9
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
10
|
+
this.name = this.constructor.name;
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.statusText = statusText;
|
|
13
|
+
this.response = response;
|
|
14
|
+
this.headers = headers;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function parseApplicationTags(tags) {
|
|
2
|
+
const parsedTags = {};
|
|
3
|
+
if (!tags) {
|
|
4
|
+
return parsedTags;
|
|
5
|
+
}
|
|
6
|
+
tags.map(tag => {
|
|
7
|
+
const [key, value] = tag.split('=');
|
|
8
|
+
parsedTags[key] = value;
|
|
9
|
+
});
|
|
10
|
+
return parsedTags;
|
|
11
|
+
}
|
|
12
|
+
export function checkApplicationTagsForUserLikes(applicationTags) {
|
|
13
|
+
const appTags = parseApplicationTags(applicationTags);
|
|
14
|
+
if (appTags.userLikes === 'true') {
|
|
15
|
+
return true;
|
|
16
|
+
} else {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function getCredentialIdFromApplicationTags(applicationTags) {
|
|
21
|
+
return applicationTags.find(appTag => appTag.startsWith('connectionsCredential=') || appTag.startsWith('connectionscredential='))?.substring(22);
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function removePrefix(document) {
|
|
2
|
+
return document && document.replace(/id\:[a-zA-Z]+\.com:/g, '');
|
|
3
|
+
}
|
|
4
|
+
export function twitterPrefixCheck(socialOriginType, value) {
|
|
5
|
+
let result = value;
|
|
6
|
+
if (socialOriginType === 'twitter') {
|
|
7
|
+
if (!value.includes('id:twitter.com:')) {
|
|
8
|
+
result = 'id:twitter.com:' + value;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|