n8n-nodes-upload-post 0.1.56 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -159,6 +159,7 @@ const FIRST_COMMENT_OVERRIDES = [
|
|
|
159
159
|
{ platform: 'reddit', param: 'redditFirstComment', field: 'reddit_first_comment' },
|
|
160
160
|
{ platform: 'bluesky', param: 'blueskyFirstComment', field: 'bluesky_first_comment' },
|
|
161
161
|
{ platform: 'linkedin', param: 'linkedinFirstComment', field: 'linkedin_first_comment' },
|
|
162
|
+
{ platform: 'tiktok', param: 'tiktokFirstComment', field: 'tiktok_first_comment', operations: ['uploadPhotos', 'uploadVideo'] },
|
|
162
163
|
];
|
|
163
164
|
const getFilteredPlatforms = (operation, platforms) => {
|
|
164
165
|
var _a;
|
|
@@ -394,6 +395,20 @@ const applyFacebookOptions = (ctx, operation, formData, isManualPlatform = false
|
|
|
394
395
|
}
|
|
395
396
|
}
|
|
396
397
|
};
|
|
398
|
+
const applySharedTiktokFields = (ctx, formData) => {
|
|
399
|
+
const musicId = ctx.node.getNodeParameter('tiktokMusicId', ctx.itemIndex, '');
|
|
400
|
+
if (musicId)
|
|
401
|
+
formData.tiktok_music_id = musicId;
|
|
402
|
+
const locationId = ctx.node.getNodeParameter('tiktokLocationId', ctx.itemIndex, '');
|
|
403
|
+
const locationName = ctx.node.getNodeParameter('tiktokLocationName', ctx.itemIndex, '');
|
|
404
|
+
if (locationId && locationName) {
|
|
405
|
+
formData.tiktok_location_id = locationId;
|
|
406
|
+
formData.tiktok_location_name = locationName;
|
|
407
|
+
}
|
|
408
|
+
const isAiGenerated = ctx.node.getNodeParameter('tiktokIsAiGenerated', ctx.itemIndex, false);
|
|
409
|
+
if (isAiGenerated)
|
|
410
|
+
formData.tiktok_is_ai_generated = 'true';
|
|
411
|
+
};
|
|
397
412
|
const applyTiktokOptions = (ctx, operation, formData) => {
|
|
398
413
|
if (operation === 'uploadPhotos') {
|
|
399
414
|
const autoAddMusic = ctx.node.getNodeParameter('tiktokAutoAddMusic', ctx.itemIndex, false);
|
|
@@ -410,6 +425,7 @@ const applyTiktokOptions = (ctx, operation, formData) => {
|
|
|
410
425
|
if (photoDescription && formData.description === undefined) {
|
|
411
426
|
formData.description = photoDescription;
|
|
412
427
|
}
|
|
428
|
+
applySharedTiktokFields(ctx, formData);
|
|
413
429
|
}
|
|
414
430
|
else if (operation === 'uploadVideo') {
|
|
415
431
|
const privacyLevel = ctx.node.getNodeParameter('tiktokPrivacyLevel', ctx.itemIndex, '');
|
|
@@ -432,6 +448,23 @@ const applyTiktokOptions = (ctx, operation, formData) => {
|
|
|
432
448
|
formData.is_aigc = String(isAigc);
|
|
433
449
|
if (postMode)
|
|
434
450
|
formData.post_mode = postMode;
|
|
451
|
+
applySharedTiktokFields(ctx, formData);
|
|
452
|
+
if (formData.tiktok_music_id) {
|
|
453
|
+
formData.tiktok_music_volume = ctx.node.getNodeParameter('tiktokMusicVolume', ctx.itemIndex, 50);
|
|
454
|
+
formData.tiktok_original_sound_volume = ctx.node.getNodeParameter('tiktokOriginalSoundVolume', ctx.itemIndex, 50);
|
|
455
|
+
const musicStart = ctx.node.getNodeParameter('tiktokMusicStart', ctx.itemIndex, 0);
|
|
456
|
+
const musicEnd = ctx.node.getNodeParameter('tiktokMusicEnd', ctx.itemIndex, 0);
|
|
457
|
+
if (musicStart > 0)
|
|
458
|
+
formData.tiktok_music_start = musicStart;
|
|
459
|
+
if (musicEnd > 0)
|
|
460
|
+
formData.tiktok_music_end = musicEnd;
|
|
461
|
+
}
|
|
462
|
+
const coverImageUrl = ctx.node.getNodeParameter('tiktokCoverImageUrl', ctx.itemIndex, '');
|
|
463
|
+
if (coverImageUrl)
|
|
464
|
+
formData.tiktok_cover_image_url = coverImageUrl;
|
|
465
|
+
const uploadToDraft = ctx.node.getNodeParameter('tiktokUploadToDraft', ctx.itemIndex, false);
|
|
466
|
+
if (uploadToDraft)
|
|
467
|
+
formData.tiktok_upload_to_draft = 'true';
|
|
435
468
|
}
|
|
436
469
|
};
|
|
437
470
|
const applyInstagramOptions = async (ctx, operation, formData) => {
|
|
@@ -1234,18 +1267,28 @@ const buildUserRequest = async (ctx) => {
|
|
|
1234
1267
|
});
|
|
1235
1268
|
}
|
|
1236
1269
|
};
|
|
1270
|
+
const COMMENT_ACTIONS_WITHOUT_POST = new Set(['like', 'unlike']);
|
|
1237
1271
|
const buildInstagramRequest = (ctx) => {
|
|
1238
1272
|
switch (ctx.operation) {
|
|
1239
1273
|
case 'getPostComments': {
|
|
1240
1274
|
const user = ctx.node.getNodeParameter('instagramUser', ctx.itemIndex);
|
|
1275
|
+
const platform = ctx.node.getNodeParameter('commentPlatform', ctx.itemIndex, 'instagram');
|
|
1241
1276
|
const postId = ctx.node.getNodeParameter('instagramPostId', ctx.itemIndex);
|
|
1242
|
-
const qs = { platform
|
|
1277
|
+
const qs = { platform, user };
|
|
1243
1278
|
if (postId.startsWith('http://') || postId.startsWith('https://')) {
|
|
1279
|
+
if (platform === 'tiktok') {
|
|
1280
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), 'TikTok comments are looked up by video ID, not by URL. Paste the numeric video ID instead.', {
|
|
1281
|
+
itemIndex: ctx.itemIndex,
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1244
1284
|
qs.post_url = postId;
|
|
1245
1285
|
}
|
|
1246
1286
|
else {
|
|
1247
1287
|
qs.post_id = postId;
|
|
1248
1288
|
}
|
|
1289
|
+
const commentId = ctx.node.getNodeParameter('commentsCommentId', ctx.itemIndex, '');
|
|
1290
|
+
if (commentId)
|
|
1291
|
+
qs.comment_id = commentId;
|
|
1249
1292
|
return {
|
|
1250
1293
|
endpoint: '/uploadposts/comments',
|
|
1251
1294
|
method: 'GET',
|
|
@@ -1278,12 +1321,91 @@ const buildInstagramRequest = (ctx) => {
|
|
|
1278
1321
|
waitForCompletion: false,
|
|
1279
1322
|
};
|
|
1280
1323
|
}
|
|
1324
|
+
case 'commentAction': {
|
|
1325
|
+
const user = ctx.node.getNodeParameter('instagramUser', ctx.itemIndex);
|
|
1326
|
+
const platform = ctx.node.getNodeParameter('commentActionPlatform', ctx.itemIndex, 'tiktok');
|
|
1327
|
+
const action = ctx.node.getNodeParameter('commentModerationAction', ctx.itemIndex);
|
|
1328
|
+
const body = {
|
|
1329
|
+
platform,
|
|
1330
|
+
user,
|
|
1331
|
+
comment_id: ctx.node.getNodeParameter('instagramCommentId', ctx.itemIndex),
|
|
1332
|
+
action,
|
|
1333
|
+
};
|
|
1334
|
+
if (!COMMENT_ACTIONS_WITHOUT_POST.has(action)) {
|
|
1335
|
+
const postId = ctx.node.getNodeParameter('commentActionPostId', ctx.itemIndex, '');
|
|
1336
|
+
if (!postId) {
|
|
1337
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `A Post ID is required to ${action} a comment.`, {
|
|
1338
|
+
itemIndex: ctx.itemIndex,
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
body.post_id = postId;
|
|
1342
|
+
}
|
|
1343
|
+
return {
|
|
1344
|
+
endpoint: '/uploadposts/comments/action',
|
|
1345
|
+
method: 'POST',
|
|
1346
|
+
body,
|
|
1347
|
+
isUploadOperation: false,
|
|
1348
|
+
waitForCompletion: false,
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1281
1351
|
default:
|
|
1282
1352
|
throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Unsupported Instagram operation: ${ctx.operation}`, {
|
|
1283
1353
|
itemIndex: ctx.itemIndex,
|
|
1284
1354
|
});
|
|
1285
1355
|
}
|
|
1286
1356
|
};
|
|
1357
|
+
const buildInsightsRequest = (ctx) => {
|
|
1358
|
+
const user = ctx.node.getNodeParameter('insightsUser', ctx.itemIndex);
|
|
1359
|
+
const platform = ctx.node.getNodeParameter('insightsPlatform', ctx.itemIndex, 'tiktok');
|
|
1360
|
+
switch (ctx.operation) {
|
|
1361
|
+
case 'getAudience': {
|
|
1362
|
+
const qs = { user, platform };
|
|
1363
|
+
const startDate = ctx.node.getNodeParameter('audienceStartDate', ctx.itemIndex, '');
|
|
1364
|
+
if (startDate)
|
|
1365
|
+
qs.start_date = startDate.slice(0, 10);
|
|
1366
|
+
const endDate = ctx.node.getNodeParameter('audienceEndDate', ctx.itemIndex, '');
|
|
1367
|
+
if (endDate)
|
|
1368
|
+
qs.end_date = endDate.slice(0, 10);
|
|
1369
|
+
const benchmarkCategory = ctx.node.getNodeParameter('audienceBenchmarkCategory', ctx.itemIndex, '');
|
|
1370
|
+
if (benchmarkCategory)
|
|
1371
|
+
qs.benchmark_category = benchmarkCategory;
|
|
1372
|
+
return {
|
|
1373
|
+
endpoint: '/uploadposts/audience',
|
|
1374
|
+
method: 'GET',
|
|
1375
|
+
qs,
|
|
1376
|
+
isUploadOperation: false,
|
|
1377
|
+
waitForCompletion: false,
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
case 'getSuggestions': {
|
|
1381
|
+
const qs = {
|
|
1382
|
+
user,
|
|
1383
|
+
platform,
|
|
1384
|
+
type: ctx.node.getNodeParameter('suggestionsType', ctx.itemIndex),
|
|
1385
|
+
};
|
|
1386
|
+
const query = ctx.node.getNodeParameter('suggestionsQuery', ctx.itemIndex, '');
|
|
1387
|
+
if (query)
|
|
1388
|
+
qs.q = query;
|
|
1389
|
+
const countryCode = ctx.node.getNodeParameter('suggestionsCountryCode', ctx.itemIndex, '');
|
|
1390
|
+
if (countryCode)
|
|
1391
|
+
qs.country_code = countryCode;
|
|
1392
|
+
const language = ctx.node.getNodeParameter('suggestionsLanguage', ctx.itemIndex, '');
|
|
1393
|
+
if (language)
|
|
1394
|
+
qs.language = language;
|
|
1395
|
+
return {
|
|
1396
|
+
endpoint: '/uploadposts/suggestions',
|
|
1397
|
+
method: 'GET',
|
|
1398
|
+
qs,
|
|
1399
|
+
isUploadOperation: false,
|
|
1400
|
+
waitForCompletion: false,
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
default:
|
|
1404
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Unsupported insights operation: ${ctx.operation}`, {
|
|
1405
|
+
itemIndex: ctx.itemIndex,
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1287
1409
|
const buildRequestConfig = async (ctx) => {
|
|
1288
1410
|
if (ctx.operation === 'uploadPhotos') {
|
|
1289
1411
|
return buildUploadPhotosRequest(ctx);
|
|
@@ -1301,6 +1423,9 @@ const buildRequestConfig = async (ctx) => {
|
|
|
1301
1423
|
if (resource === 'instagram') {
|
|
1302
1424
|
return buildInstagramRequest(ctx);
|
|
1303
1425
|
}
|
|
1426
|
+
if (resource === 'insights') {
|
|
1427
|
+
return buildInsightsRequest(ctx);
|
|
1428
|
+
}
|
|
1304
1429
|
if (resource === 'monitoring') {
|
|
1305
1430
|
return buildMonitoringRequest(ctx);
|
|
1306
1431
|
}
|
|
@@ -1404,9 +1529,10 @@ class UploadPost {
|
|
|
1404
1529
|
type: 'options',
|
|
1405
1530
|
noDataExpression: true,
|
|
1406
1531
|
options: [
|
|
1407
|
-
{ name: '
|
|
1408
|
-
{ name: '
|
|
1532
|
+
{ name: 'Comment', value: 'instagram' },
|
|
1533
|
+
{ name: 'Insight', value: 'insights' },
|
|
1409
1534
|
{ name: 'Status & History', value: 'monitoring' },
|
|
1535
|
+
{ name: 'Upload', value: 'uploads' },
|
|
1410
1536
|
{ name: 'User', value: 'users' },
|
|
1411
1537
|
],
|
|
1412
1538
|
default: 'uploads',
|
|
@@ -1437,7 +1563,7 @@ class UploadPost {
|
|
|
1437
1563
|
{ name: 'Get Cached Post Analytics', value: 'getCachedPostAnalytics', action: 'Get cached post analytics', description: 'Replay per-post metrics already fetched for a profile instead of querying the platforms again, so it is not subject to the live analytics rate limit (100 requests / 5 minutes). Only contains posts previously fetched through Get Post Analytics; there is no background refresh, so captured_at is the last time that post was read live. Use Get Post Analytics to refresh a post, and this operation for bulk re-reads.' },
|
|
1438
1564
|
{ name: 'Get Job Status', value: 'getJobStatus', action: 'Get job status', description: 'Check the status of a scheduled or queued post using the job_id' },
|
|
1439
1565
|
{ name: 'Get Platform Metrics', value: 'getPlatformMetrics', action: 'Get platform metrics', description: 'List the analytics metrics available for each platform' },
|
|
1440
|
-
{ name: 'Get Post Analytics', value: 'getPostAnalytics', action: 'Get post analytics', description: 'Retrieve per-post analytics for an upload using its request_id' },
|
|
1566
|
+
{ name: 'Get Post Analytics', value: 'getPostAnalytics', action: 'Get post analytics', description: 'Retrieve per-post analytics for an upload using its request_id. On TikTok post_metrics also carries retention, impression_sources, audience_types, new_followers, reach and the watch times.' },
|
|
1441
1567
|
{ name: 'Get Post Analytics by Platform ID', value: 'getPostAnalyticsByPlatformId', action: 'Get post analytics by platform id', description: 'Retrieve per-post analytics using the native platform post ID, for posts not published through Upload-Post' },
|
|
1442
1568
|
{ name: 'Get Reddit Detailed Posts', value: 'getRedditDetailedPosts', action: 'Get reddit detailed posts', description: 'Retrieve detailed Reddit posts with full media information' },
|
|
1443
1569
|
{ name: 'Get Total Impressions', value: 'getTotalImpressions', action: 'Get total impressions', description: 'Retrieve impressions aggregated across connected platforms for a date range' },
|
|
@@ -1473,9 +1599,10 @@ class UploadPost {
|
|
|
1473
1599
|
type: 'options',
|
|
1474
1600
|
noDataExpression: true,
|
|
1475
1601
|
options: [
|
|
1476
|
-
{ name: '
|
|
1477
|
-
{ name: '
|
|
1478
|
-
{ name: '
|
|
1602
|
+
{ name: 'Comment Action', value: 'commentAction', action: 'Act on a comment', description: 'Moderate one comment: hide, unhide, like, unlike, pin or unpin it. Platform says which connected network the comment lives on. Post ID is required to hide, unhide, pin or unpin, and is never sent for like or unlike.' },
|
|
1603
|
+
{ name: 'Get Post Comments', value: 'getPostComments', action: 'Get post comments', description: 'Retrieve the comments on a post from the network given by Platform (Instagram, Facebook, YouTube, LinkedIn or TikTok). Fill Comment ID to get the replies to that comment instead of the top-level comments of the post.' },
|
|
1604
|
+
{ name: 'Private Reply to Comment', value: 'privateReplyToComment', action: 'Private reply to comment', description: 'Send a private reply (DM) to the author of a comment. Instagram only.' },
|
|
1605
|
+
{ name: 'Public Reply to Comment', value: 'publicReplyToComment', action: 'Public reply to comment', description: 'Post a public reply visible under the original comment. Instagram only.' },
|
|
1479
1606
|
],
|
|
1480
1607
|
default: 'getPostComments',
|
|
1481
1608
|
displayOptions: { show: { resource: ['instagram'] } },
|
|
@@ -1492,7 +1619,40 @@ class UploadPost {
|
|
|
1492
1619
|
displayOptions: {
|
|
1493
1620
|
show: {
|
|
1494
1621
|
resource: ['instagram'],
|
|
1495
|
-
operation: ['getPostComments', 'privateReplyToComment', 'publicReplyToComment'],
|
|
1622
|
+
operation: ['getPostComments', 'privateReplyToComment', 'publicReplyToComment', 'commentAction'],
|
|
1623
|
+
},
|
|
1624
|
+
},
|
|
1625
|
+
},
|
|
1626
|
+
{
|
|
1627
|
+
displayName: 'Platform',
|
|
1628
|
+
name: 'commentPlatform',
|
|
1629
|
+
type: 'options',
|
|
1630
|
+
options: [
|
|
1631
|
+
{ name: 'Facebook', value: 'facebook' },
|
|
1632
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
1633
|
+
{ name: 'LinkedIn', value: 'linkedin' },
|
|
1634
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
1635
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
1636
|
+
],
|
|
1637
|
+
default: 'instagram',
|
|
1638
|
+
description: 'Network the post lives on. TikTok needs the "comments" capability on the profile (see the capabilities array returned by List Users); an account connected before that capability existed has to reconnect TikTok.',
|
|
1639
|
+
displayOptions: {
|
|
1640
|
+
show: {
|
|
1641
|
+
resource: ['instagram'],
|
|
1642
|
+
operation: ['getPostComments'],
|
|
1643
|
+
},
|
|
1644
|
+
},
|
|
1645
|
+
},
|
|
1646
|
+
{
|
|
1647
|
+
displayName: 'Comment ID',
|
|
1648
|
+
name: 'commentsCommentId',
|
|
1649
|
+
type: 'string',
|
|
1650
|
+
default: '',
|
|
1651
|
+
description: 'Leave empty for the top-level comments of the post. Set it to a comment ID to get the replies to that comment instead: it is the same question with one parameter more.',
|
|
1652
|
+
displayOptions: {
|
|
1653
|
+
show: {
|
|
1654
|
+
resource: ['instagram'],
|
|
1655
|
+
operation: ['getPostComments'],
|
|
1496
1656
|
},
|
|
1497
1657
|
},
|
|
1498
1658
|
},
|
|
@@ -1502,7 +1662,7 @@ class UploadPost {
|
|
|
1502
1662
|
type: 'string',
|
|
1503
1663
|
required: true,
|
|
1504
1664
|
default: '',
|
|
1505
|
-
description: '
|
|
1665
|
+
description: 'Post identifier. Instagram and Facebook accept the numeric media ID or the post URL, YouTube the video ID, LinkedIn the post URN, TikTok the video ID (TikTok has no URL lookup).',
|
|
1506
1666
|
displayOptions: {
|
|
1507
1667
|
show: {
|
|
1508
1668
|
resource: ['instagram'],
|
|
@@ -1516,11 +1676,55 @@ class UploadPost {
|
|
|
1516
1676
|
type: 'string',
|
|
1517
1677
|
required: true,
|
|
1518
1678
|
default: '',
|
|
1519
|
-
description: 'The ID of the comment to reply to (from Get Post Comments)',
|
|
1679
|
+
description: 'The ID of the comment to reply to or act on (from Get Post Comments)',
|
|
1520
1680
|
displayOptions: {
|
|
1521
1681
|
show: {
|
|
1522
1682
|
resource: ['instagram'],
|
|
1523
|
-
operation: ['privateReplyToComment', 'publicReplyToComment'],
|
|
1683
|
+
operation: ['privateReplyToComment', 'publicReplyToComment', 'commentAction'],
|
|
1684
|
+
},
|
|
1685
|
+
},
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
displayName: 'Platform',
|
|
1689
|
+
name: 'commentActionPlatform',
|
|
1690
|
+
type: 'options',
|
|
1691
|
+
required: true,
|
|
1692
|
+
options: [
|
|
1693
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
1694
|
+
],
|
|
1695
|
+
default: 'tiktok',
|
|
1696
|
+
description: 'Network the comment lives on. TikTok is the network that answers this question today; any other one replies with platform_not_supported and the list of the ones that can. On TikTok the profile needs the "comments" capability (see the capabilities array returned by List Users); an account connected before that capability existed has to reconnect TikTok.',
|
|
1697
|
+
displayOptions: { show: { resource: ['instagram'], operation: ['commentAction'] } },
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
displayName: 'Action',
|
|
1701
|
+
name: 'commentModerationAction',
|
|
1702
|
+
type: 'options',
|
|
1703
|
+
required: true,
|
|
1704
|
+
options: [
|
|
1705
|
+
{ name: 'Hide', value: 'hide' },
|
|
1706
|
+
{ name: 'Like', value: 'like' },
|
|
1707
|
+
{ name: 'Pin', value: 'pin' },
|
|
1708
|
+
{ name: 'Unhide', value: 'unhide' },
|
|
1709
|
+
{ name: 'Unlike', value: 'unlike' },
|
|
1710
|
+
{ name: 'Unpin', value: 'unpin' },
|
|
1711
|
+
],
|
|
1712
|
+
default: 'hide',
|
|
1713
|
+
description: 'What to do with the comment. Each action carries its own inverse, so there is no separate toggle: hide/unhide change its visibility, like/unlike react to it as the account, pin/unpin move it to the top of the thread.',
|
|
1714
|
+
displayOptions: { show: { resource: ['instagram'], operation: ['commentAction'] } },
|
|
1715
|
+
},
|
|
1716
|
+
{
|
|
1717
|
+
displayName: 'Post ID',
|
|
1718
|
+
name: 'commentActionPostId',
|
|
1719
|
+
type: 'string',
|
|
1720
|
+
required: true,
|
|
1721
|
+
default: '',
|
|
1722
|
+
description: 'Native ID of the post the comment hangs from, as the platform assigns it (on TikTok, the video ID). Required to hide, unhide, pin or unpin; it is not sent for like or unlike, which the API rejects when it carries a post.',
|
|
1723
|
+
displayOptions: {
|
|
1724
|
+
show: {
|
|
1725
|
+
resource: ['instagram'],
|
|
1726
|
+
operation: ['commentAction'],
|
|
1727
|
+
commentModerationAction: ['hide', 'unhide', 'pin', 'unpin'],
|
|
1524
1728
|
},
|
|
1525
1729
|
},
|
|
1526
1730
|
},
|
|
@@ -1539,6 +1743,107 @@ class UploadPost {
|
|
|
1539
1743
|
},
|
|
1540
1744
|
},
|
|
1541
1745
|
},
|
|
1746
|
+
{
|
|
1747
|
+
displayName: 'Operation',
|
|
1748
|
+
name: 'operation',
|
|
1749
|
+
type: 'options',
|
|
1750
|
+
noDataExpression: true,
|
|
1751
|
+
options: [
|
|
1752
|
+
{ name: 'Get Audience', value: 'getAudience', action: 'Get audience', description: 'Ask a connected network who follows the profile: audience countries, cities, age brackets and genders, how many followers are online each hour, daily followers gained and lost, profile actions and the bio. Platform says which network answers. Pass a Benchmark Category to also get that niche averages (comments, engagement rate, follower count and growth, likes, shares, video count and views) next to the numbers of the account; every response lists the 25 accepted categories in benchmark_categories. Use this to know WHO the audience is and WHEN to post; use Get Post Analytics for how a given post did.' },
|
|
1753
|
+
{ name: 'Get Suggestions', value: 'getSuggestions', action: 'Get suggestions', description: 'Ask a connected network what to write about: hashtags (each with its view count) or the searches people run, around a seed word. Type picks which of the two, Platform says which network answers. Use this before writing a caption to pick hashtags or find the wording an audience searches for.' },
|
|
1754
|
+
],
|
|
1755
|
+
default: 'getAudience',
|
|
1756
|
+
displayOptions: { show: { resource: ['insights'] } },
|
|
1757
|
+
},
|
|
1758
|
+
{
|
|
1759
|
+
displayName: 'User Identifier Name or ID',
|
|
1760
|
+
name: 'insightsUser',
|
|
1761
|
+
type: 'options',
|
|
1762
|
+
noDataExpression: true,
|
|
1763
|
+
required: true,
|
|
1764
|
+
default: '',
|
|
1765
|
+
description: 'Upload-Post profile whose connected account answers the question. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
1766
|
+
typeOptions: { loadOptionsMethod: 'getUserProfiles' },
|
|
1767
|
+
displayOptions: { show: { resource: ['insights'] } },
|
|
1768
|
+
},
|
|
1769
|
+
{
|
|
1770
|
+
displayName: 'Platform',
|
|
1771
|
+
name: 'insightsPlatform',
|
|
1772
|
+
type: 'options',
|
|
1773
|
+
required: true,
|
|
1774
|
+
options: [
|
|
1775
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
1776
|
+
],
|
|
1777
|
+
default: 'tiktok',
|
|
1778
|
+
description: 'Network that answers the question. TikTok is the network that answers this question today; any other one replies with platform_not_supported and the list of the ones that can. On TikTok the profile needs the "profile_analytics" capability (see the capabilities array returned by List Users).',
|
|
1779
|
+
displayOptions: { show: { resource: ['insights'] } },
|
|
1780
|
+
},
|
|
1781
|
+
{
|
|
1782
|
+
displayName: 'Start Date',
|
|
1783
|
+
name: 'audienceStartDate',
|
|
1784
|
+
type: 'string',
|
|
1785
|
+
default: '',
|
|
1786
|
+
placeholder: 'YYYY-MM-DD',
|
|
1787
|
+
description: 'First day of the window. The server clamps the range to 60 days at most and always below today, so a wider window is trimmed instead of rejected. Leave empty for the default range.',
|
|
1788
|
+
displayOptions: { show: { resource: ['insights'], operation: ['getAudience'] } },
|
|
1789
|
+
},
|
|
1790
|
+
{
|
|
1791
|
+
displayName: 'End Date',
|
|
1792
|
+
name: 'audienceEndDate',
|
|
1793
|
+
type: 'string',
|
|
1794
|
+
default: '',
|
|
1795
|
+
placeholder: 'YYYY-MM-DD',
|
|
1796
|
+
description: 'Last day of the window. The server moves it below today, because there are no audience numbers for the current day yet.',
|
|
1797
|
+
displayOptions: { show: { resource: ['insights'], operation: ['getAudience'] } },
|
|
1798
|
+
},
|
|
1799
|
+
{
|
|
1800
|
+
displayName: 'Benchmark Category',
|
|
1801
|
+
name: 'audienceBenchmarkCategory',
|
|
1802
|
+
type: 'string',
|
|
1803
|
+
default: '',
|
|
1804
|
+
placeholder: 'SOFTWARE_AND_APPS',
|
|
1805
|
+
description: 'Niche to compare the account against, which adds a benchmark object to the response. Leave empty to skip the comparison: the accepted values come back in benchmark_categories on every response anyway, so no extra call is needed to fill a picker.',
|
|
1806
|
+
displayOptions: { show: { resource: ['insights'], operation: ['getAudience'] } },
|
|
1807
|
+
},
|
|
1808
|
+
{
|
|
1809
|
+
displayName: 'Type',
|
|
1810
|
+
name: 'suggestionsType',
|
|
1811
|
+
type: 'options',
|
|
1812
|
+
required: true,
|
|
1813
|
+
options: [
|
|
1814
|
+
{ name: 'Hashtags', value: 'hashtags' },
|
|
1815
|
+
{ name: 'Keywords', value: 'keywords' },
|
|
1816
|
+
],
|
|
1817
|
+
default: 'hashtags',
|
|
1818
|
+
description: 'Which suggestions you want. Hashtags answers with a list of name and view_count; keywords answers with the searches people run around the word.',
|
|
1819
|
+
displayOptions: { show: { resource: ['insights'], operation: ['getSuggestions'] } },
|
|
1820
|
+
},
|
|
1821
|
+
{
|
|
1822
|
+
displayName: 'Query',
|
|
1823
|
+
name: 'suggestionsQuery',
|
|
1824
|
+
type: 'string',
|
|
1825
|
+
default: '',
|
|
1826
|
+
description: 'Seed word to get suggestions around, written without the "#"',
|
|
1827
|
+
displayOptions: { show: { resource: ['insights'], operation: ['getSuggestions'] } },
|
|
1828
|
+
},
|
|
1829
|
+
{
|
|
1830
|
+
displayName: 'Country Code',
|
|
1831
|
+
name: 'suggestionsCountryCode',
|
|
1832
|
+
type: 'string',
|
|
1833
|
+
default: '',
|
|
1834
|
+
placeholder: 'ES',
|
|
1835
|
+
description: 'ISO 3166-1 alpha-2 country to bias the suggestions towards, e.g. ES or US',
|
|
1836
|
+
displayOptions: { show: { resource: ['insights'], operation: ['getSuggestions'] } },
|
|
1837
|
+
},
|
|
1838
|
+
{
|
|
1839
|
+
displayName: 'Language',
|
|
1840
|
+
name: 'suggestionsLanguage',
|
|
1841
|
+
type: 'string',
|
|
1842
|
+
default: '',
|
|
1843
|
+
placeholder: 'es',
|
|
1844
|
+
description: 'Language code to bias the suggestions towards, e.g. es or en',
|
|
1845
|
+
displayOptions: { show: { resource: ['insights'], operation: ['getSuggestions'] } },
|
|
1846
|
+
},
|
|
1542
1847
|
{
|
|
1543
1848
|
displayName: 'User Identifier Name or ID',
|
|
1544
1849
|
name: 'user',
|
|
@@ -1593,7 +1898,7 @@ class UploadPost {
|
|
|
1593
1898
|
name: 'firstComment',
|
|
1594
1899
|
type: 'string',
|
|
1595
1900
|
default: '',
|
|
1596
|
-
description: 'Text to post as the first comment (or reply) immediately after publishing. Supported on Instagram, Facebook, X, Threads, YouTube, Reddit, Bluesky.',
|
|
1901
|
+
description: 'Text to post as the first comment (or reply) immediately after publishing. Supported on Instagram, Facebook, X, Threads, YouTube, Reddit, Bluesky, LinkedIn and TikTok. On TikTok the profile needs the "comments" capability (see the capabilities array returned by List Users).',
|
|
1597
1902
|
displayOptions: { show: { resource: ['uploads'], operation: ['uploadPhotos', 'uploadVideo', 'uploadText'] } },
|
|
1598
1903
|
},
|
|
1599
1904
|
{
|
|
@@ -1873,6 +2178,14 @@ class UploadPost {
|
|
|
1873
2178
|
description: 'Optional override for LinkedIn first comment. If provided, overrides the generic First Comment for LinkedIn.',
|
|
1874
2179
|
displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText', 'uploadDocument'], platform: ['linkedin', '__manual_platform__'] } },
|
|
1875
2180
|
},
|
|
2181
|
+
{
|
|
2182
|
+
displayName: 'TikTok First Comment (Override)',
|
|
2183
|
+
name: 'tiktokFirstComment',
|
|
2184
|
+
type: 'string',
|
|
2185
|
+
default: '',
|
|
2186
|
+
description: 'Optional override for the TikTok first comment. If provided, overrides the generic First Comment for TikTok. The profile needs the "comments" capability (see the capabilities array returned by List Users); an account connected before that capability existed has to reconnect TikTok.',
|
|
2187
|
+
displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo'], platform: ['tiktok', '__manual_platform__'] } },
|
|
2188
|
+
},
|
|
1876
2189
|
{
|
|
1877
2190
|
displayName: 'Photos (Files or URLs)',
|
|
1878
2191
|
name: 'photos',
|
|
@@ -2842,13 +3155,14 @@ class UploadPost {
|
|
|
2842
3155
|
name: 'tiktokPrivacyLevel',
|
|
2843
3156
|
type: 'options',
|
|
2844
3157
|
options: [
|
|
2845
|
-
{ name: '
|
|
2846
|
-
{ name: 'Mutual Follow Friends', value: 'MUTUAL_FOLLOW_FRIENDS' },
|
|
3158
|
+
{ name: 'Account Default', value: '' },
|
|
2847
3159
|
{ name: 'Follower of Creator', value: 'FOLLOWER_OF_CREATOR' },
|
|
3160
|
+
{ name: 'Mutual Follow Friends', value: 'MUTUAL_FOLLOW_FRIENDS' },
|
|
3161
|
+
{ name: 'Public to Everyone', value: 'PUBLIC_TO_EVERYONE' },
|
|
2848
3162
|
{ name: 'Self Only', value: 'SELF_ONLY' },
|
|
2849
3163
|
],
|
|
2850
|
-
default: '
|
|
2851
|
-
description: 'Privacy setting for TikTok video
|
|
3164
|
+
default: '',
|
|
3165
|
+
description: 'Privacy setting for the TikTok video. TikTok decides per account which levels are available: a private account has no Public to Everyone, and asking for one the account does not have fails with tiktok_privacy_unavailable listing the allowed ones. Account Default sends nothing and lets TikTok apply the account\'s own setting, which is the only value guaranteed to work on every account. Only for Upload Video.',
|
|
2852
3166
|
displayOptions: {
|
|
2853
3167
|
show: {
|
|
2854
3168
|
operation: ['uploadVideo'],
|
|
@@ -2951,6 +3265,140 @@ class UploadPost {
|
|
|
2951
3265
|
}
|
|
2952
3266
|
},
|
|
2953
3267
|
},
|
|
3268
|
+
{
|
|
3269
|
+
displayName: 'TikTok Music ID',
|
|
3270
|
+
name: 'tiktokMusicId',
|
|
3271
|
+
type: 'string',
|
|
3272
|
+
default: '',
|
|
3273
|
+
description: 'Commercial Music Library track ID to add to the post: the ID field returned by the TikTok trending-music or music-search endpoint, not its commercial_music_id. To find a track by song or artist, call GET /api/uploadposts/tiktok/music/search from an HTTP Request node first. Works on video and photo posts alike, but a photo post takes the ID alone: the volume and trim options below are video-only. Needs the music capability on the TikTok connection (see capabilities in the user-profiles response); without it the field is ignored with a warning and the post still publishes. Leave empty to skip.',
|
|
3274
|
+
displayOptions: {
|
|
3275
|
+
show: {
|
|
3276
|
+
operation: ['uploadVideo', 'uploadPhotos'],
|
|
3277
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3278
|
+
}
|
|
3279
|
+
},
|
|
3280
|
+
},
|
|
3281
|
+
{
|
|
3282
|
+
displayName: 'TikTok Music Volume',
|
|
3283
|
+
name: 'tiktokMusicVolume',
|
|
3284
|
+
type: 'number',
|
|
3285
|
+
default: 50,
|
|
3286
|
+
typeOptions: { minValue: 0, maxValue: 100 },
|
|
3287
|
+
description: 'Volume (0-100) of the Commercial Music Library track. Only sent when a TikTok Music ID is set.',
|
|
3288
|
+
displayOptions: {
|
|
3289
|
+
show: {
|
|
3290
|
+
operation: ['uploadVideo'],
|
|
3291
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3292
|
+
}
|
|
3293
|
+
},
|
|
3294
|
+
},
|
|
3295
|
+
{
|
|
3296
|
+
displayName: 'TikTok Music Start (Ms)',
|
|
3297
|
+
name: 'tiktokMusicStart',
|
|
3298
|
+
type: 'number',
|
|
3299
|
+
default: 0,
|
|
3300
|
+
typeOptions: { minValue: 0 },
|
|
3301
|
+
description: 'Start offset (ms) of the music track. Only sent when a TikTok Music ID is set and this is above 0.',
|
|
3302
|
+
displayOptions: {
|
|
3303
|
+
show: {
|
|
3304
|
+
operation: ['uploadVideo'],
|
|
3305
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3306
|
+
}
|
|
3307
|
+
},
|
|
3308
|
+
},
|
|
3309
|
+
{
|
|
3310
|
+
displayName: 'TikTok Music End (Ms)',
|
|
3311
|
+
name: 'tiktokMusicEnd',
|
|
3312
|
+
type: 'number',
|
|
3313
|
+
default: 0,
|
|
3314
|
+
typeOptions: { minValue: 0 },
|
|
3315
|
+
description: 'End offset (ms) of the music track. Only sent when a TikTok Music ID is set and this is above 0.',
|
|
3316
|
+
displayOptions: {
|
|
3317
|
+
show: {
|
|
3318
|
+
operation: ['uploadVideo'],
|
|
3319
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3320
|
+
}
|
|
3321
|
+
},
|
|
3322
|
+
},
|
|
3323
|
+
{
|
|
3324
|
+
displayName: 'TikTok Original Sound Volume',
|
|
3325
|
+
name: 'tiktokOriginalSoundVolume',
|
|
3326
|
+
type: 'number',
|
|
3327
|
+
default: 50,
|
|
3328
|
+
typeOptions: { minValue: 0, maxValue: 100 },
|
|
3329
|
+
description: 'Volume (0-100) of the original video audio when music is added. Only sent when a TikTok Music ID is set. Keep it above 0 so the original audio is not muted.',
|
|
3330
|
+
displayOptions: {
|
|
3331
|
+
show: {
|
|
3332
|
+
operation: ['uploadVideo'],
|
|
3333
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3334
|
+
}
|
|
3335
|
+
},
|
|
3336
|
+
},
|
|
3337
|
+
{
|
|
3338
|
+
displayName: 'TikTok Location ID',
|
|
3339
|
+
name: 'tiktokLocationId',
|
|
3340
|
+
type: 'string',
|
|
3341
|
+
default: '',
|
|
3342
|
+
description: 'Location ID to tag on the post (video or photos). Needs the location capability on the TikTok connection (see capabilities in the user-profiles response) and a TikTok Location Name — the tag is only sent when both are filled in. Without the capability the field is ignored with a warning and the post still publishes. Leave empty to skip.',
|
|
3343
|
+
displayOptions: {
|
|
3344
|
+
show: {
|
|
3345
|
+
operation: ['uploadVideo', 'uploadPhotos'],
|
|
3346
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3347
|
+
}
|
|
3348
|
+
},
|
|
3349
|
+
},
|
|
3350
|
+
{
|
|
3351
|
+
displayName: 'TikTok Location Name',
|
|
3352
|
+
name: 'tiktokLocationName',
|
|
3353
|
+
type: 'string',
|
|
3354
|
+
default: '',
|
|
3355
|
+
description: 'Location name matching the TikTok Location ID. TikTok requires both together, so the location tag is only sent when this and the ID are both filled in.',
|
|
3356
|
+
displayOptions: {
|
|
3357
|
+
show: {
|
|
3358
|
+
operation: ['uploadVideo', 'uploadPhotos'],
|
|
3359
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3360
|
+
}
|
|
3361
|
+
},
|
|
3362
|
+
},
|
|
3363
|
+
{
|
|
3364
|
+
displayName: 'TikTok Cover Image URL',
|
|
3365
|
+
name: 'tiktokCoverImageUrl',
|
|
3366
|
+
type: 'string',
|
|
3367
|
+
default: '',
|
|
3368
|
+
description: 'Custom cover image URL for the video. Needs the cover_image capability on the TikTok connection (see capabilities in the user-profiles response); without it the field is ignored with a warning and the post still publishes. Takes priority over TikTok Cover Timestamp.',
|
|
3369
|
+
displayOptions: {
|
|
3370
|
+
show: {
|
|
3371
|
+
operation: ['uploadVideo'],
|
|
3372
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3373
|
+
}
|
|
3374
|
+
},
|
|
3375
|
+
},
|
|
3376
|
+
{
|
|
3377
|
+
displayName: 'TikTok Is AI Generated',
|
|
3378
|
+
name: 'tiktokIsAiGenerated',
|
|
3379
|
+
type: 'boolean',
|
|
3380
|
+
default: false,
|
|
3381
|
+
description: 'Whether to disclose the post as AI-generated on TikTok. Works on video and photo posts. Only sent when enabled.',
|
|
3382
|
+
displayOptions: {
|
|
3383
|
+
show: {
|
|
3384
|
+
operation: ['uploadVideo', 'uploadPhotos'],
|
|
3385
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3386
|
+
}
|
|
3387
|
+
},
|
|
3388
|
+
},
|
|
3389
|
+
{
|
|
3390
|
+
displayName: 'TikTok Upload to Draft',
|
|
3391
|
+
name: 'tiktokUploadToDraft',
|
|
3392
|
+
type: 'boolean',
|
|
3393
|
+
default: false,
|
|
3394
|
+
description: 'Whether to send the video to TikTok drafts instead of publishing it. Needs the draft capability on the TikTok connection (see capabilities in the user-profiles response); without it the field is ignored with a warning and the post still publishes. When enabled TikTok ignores the rest of the post settings.',
|
|
3395
|
+
displayOptions: {
|
|
3396
|
+
show: {
|
|
3397
|
+
operation: ['uploadVideo'],
|
|
3398
|
+
platform: ['tiktok', '__manual_platform__']
|
|
3399
|
+
}
|
|
3400
|
+
},
|
|
3401
|
+
},
|
|
2954
3402
|
{
|
|
2955
3403
|
displayName: 'Instagram Media Type',
|
|
2956
3404
|
name: 'instagramMediaType',
|