n8n-nodes-upload-post 0.1.48 → 0.1.51
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/README.md
CHANGED
|
@@ -36,7 +36,7 @@ The node provides the following operations grouped for clarity:
|
|
|
36
36
|
- Supports file uploads and photo URLs (comma-separated list).
|
|
37
37
|
- Common parameters: User Identifier, Platform Names or IDs, Title / Main Content, Description (optional), Photos (Files or URLs), Scheduled Date (optional).
|
|
38
38
|
- Title/Description overrides per platform: `instagram_title`, `facebook_title`, `tiktok_title`, `linkedin_title`, `x_title`, `youtube_title`, `pinterest_title`, `threads_title`.
|
|
39
|
-
- Platform-specific parameters available for: Facebook, Instagram, LinkedIn, Pinterest, TikTok, X (Twitter).
|
|
39
|
+
- Platform-specific parameters available for: Discord, Facebook, Instagram, LinkedIn, Pinterest, Telegram, TikTok, X (Twitter).
|
|
40
40
|
- **Pinterest**: Requires Board selection via dynamic selector.
|
|
41
41
|
- **Instagram**: Media Type (Image/Stories), TikTok: Auto-add music, disable comments, brand content toggles.
|
|
42
42
|
- **Facebook**: Page selection via dynamic selector.
|
|
@@ -47,7 +47,7 @@ The node provides the following operations grouped for clarity:
|
|
|
47
47
|
- Supports file uploads and video URLs.
|
|
48
48
|
- Common parameters: User Identifier, Platform Names or IDs, Title / Main Content, Description (optional), Video (File or URL), Scheduled Date (optional).
|
|
49
49
|
- Title/Description overrides per platform: `instagram_title`, `facebook_title`, `tiktok_title`, `linkedin_title`, `x_title`, `youtube_title`, `pinterest_title`.
|
|
50
|
-
- Platform-specific parameters available for: Facebook, Instagram, LinkedIn, Pinterest, Threads, TikTok, X (Twitter), YouTube.
|
|
50
|
+
- Platform-specific parameters available for: Discord, Facebook, Instagram, LinkedIn, Pinterest, Telegram, Threads, TikTok, X (Twitter), YouTube.
|
|
51
51
|
- **YouTube**: Custom thumbnail (URL/binary), tags, category, privacy, embeddable, license, public stats, made for kids, synthetic media declaration, geo-restrictions, paid product placement, recording date, default language/audio language.
|
|
52
52
|
- **Facebook**: Page selection, video state (Published/Draft), media type (Reels/Stories).
|
|
53
53
|
- **Instagram**: Media type (Reels/Stories), share to feed, collaborators, cover URL, audio name, user tags, location ID, thumb offset.
|
|
@@ -58,7 +58,7 @@ The node provides the following operations grouped for clarity:
|
|
|
58
58
|
|
|
59
59
|
- **Upload Text**: Upload a text-based post to supported platforms.
|
|
60
60
|
- Common parameters: User Identifier, Platform Names or IDs, Title / Main Content (used as post content), Scheduled Date (optional).
|
|
61
|
-
- Platform-specific parameters available for: Facebook, LinkedIn, Reddit, Threads, X (Twitter).
|
|
61
|
+
- Platform-specific parameters available for: Discord, Facebook, LinkedIn, Reddit, Telegram, Threads, X (Twitter).
|
|
62
62
|
- **Facebook**: Page selection, link URL for preview.
|
|
63
63
|
- **LinkedIn**: Page selection ("Me" for personal profile).
|
|
64
64
|
- **X (Twitter)**: Reply settings, poll options (2-4 options, 5-10080 min duration), post URL, quote tweet ID, geo place ID, super followers exclusivity, community ID, sharing options, direct message deep link, card URI, long text handling.
|
|
@@ -70,8 +70,18 @@ The node provides the following operations grouped for clarity:
|
|
|
70
70
|
- Parameters: Request ID.
|
|
71
71
|
- **Get Upload History**: List past uploads.
|
|
72
72
|
- Parameters: Page (default 1), Limit (default 20). Limit can be 20, 50, or 100.
|
|
73
|
-
- **Get Analytics**: Retrieve aggregated analytics
|
|
74
|
-
-
|
|
73
|
+
- **Get Analytics**: Retrieve aggregated profile analytics.
|
|
74
|
+
- Parameters: Profile Username, Platforms (required — at least one).
|
|
75
|
+
- Supported: Facebook, Instagram, LinkedIn, Pinterest, Reddit, Threads, TikTok, X, YouTube. Analytics are not available for Bluesky, Discord, Google Business or Telegram.
|
|
76
|
+
- **Get Post Analytics**: Per-post metrics for an upload, by `request_id`.
|
|
77
|
+
- Parameters: Request ID, Platform (optional filter).
|
|
78
|
+
- **Get Post Analytics by Platform ID**: Per-post metrics for a post published outside Upload-Post.
|
|
79
|
+
- Parameters: Platform Post ID, Platform, Profile Username.
|
|
80
|
+
- **Get Total Impressions**: Impressions aggregated across connected platforms.
|
|
81
|
+
- Parameters: Profile Username, Period or Start/End Date, Platforms, Breakdown.
|
|
82
|
+
- **Get Platform Metrics**: Lists which metrics each platform exposes.
|
|
83
|
+
- **Get Reddit Detailed Posts**: Detailed Reddit posts with full media information.
|
|
84
|
+
- Parameters: Profile Username.
|
|
75
85
|
|
|
76
86
|
### Scheduled Posts
|
|
77
87
|
- **List Scheduled Posts**: Lists future scheduled jobs.
|
|
@@ -102,7 +112,9 @@ Refer to the [Upload Post API Documentation](https://docs.upload-post.com) for d
|
|
|
102
112
|
|
|
103
113
|
Some uploads are processed asynchronously and the API returns immediately with a `request_id`. This happens when:
|
|
104
114
|
- You enable "Upload Asynchronously" in the node, or
|
|
105
|
-
- The upload
|
|
115
|
+
- The upload exceeds the server's synchronous wait window, in which case the API switches to async mode automatically.
|
|
116
|
+
|
|
117
|
+
Leaving "Upload Asynchronously" enabled is recommended. Turning it off holds the HTTP connection open for the whole upload, which makes a proxy or execution timeout — and therefore a retry — more likely.
|
|
106
118
|
|
|
107
119
|
You have two ways to handle this in n8n:
|
|
108
120
|
|
|
@@ -117,6 +129,14 @@ You have two ways to handle this in n8n:
|
|
|
117
129
|
- Add a Wait node (e.g., 10s), then call "Get Upload Status" passing the `request_id`.
|
|
118
130
|
- Loop with an IF node until the status is final (success/failed) or a max attempts limit is reached.
|
|
119
131
|
|
|
132
|
+
### Duplicate posts and retries
|
|
133
|
+
|
|
134
|
+
Every upload sends an `Idempotency-Key`. The API collapses two uploads carrying the same key within a 24-hour window into a single post, so a retried upload does not publish twice.
|
|
135
|
+
|
|
136
|
+
The key is derived from the workflow execution, the node and the item index, so it stays the same when n8n's "Retry On Fail" re-runs the node, and changes when you genuinely start a new execution. Set the "Idempotency Key" field yourself only if you drive retries from outside n8n and need to control the key.
|
|
137
|
+
|
|
138
|
+
If you saw duplicate posts on an older version of this node, upgrade: versions before 0.1.51 sent no key at all, and a node-level retry after a network timeout would publish the post a second time.
|
|
139
|
+
|
|
120
140
|
### Platform-Specific Options
|
|
121
141
|
|
|
122
142
|
#### Facebook
|
|
@@ -70,6 +70,15 @@ const ensureArrayFromCommaSeparated = (value) => {
|
|
|
70
70
|
.map(item => item.trim())
|
|
71
71
|
.filter(item => item.length > 0);
|
|
72
72
|
};
|
|
73
|
+
const TERMINAL_UPLOAD_STATUSES = ['completed', 'failed', 'error', 'retryable', 'scheduled', 'not_found'];
|
|
74
|
+
const buildIdempotencyKey = (ctx) => {
|
|
75
|
+
var _a;
|
|
76
|
+
const override = String((_a = ctx.node.getNodeParameter('idempotencyKey', ctx.itemIndex, '')) !== null && _a !== void 0 ? _a : '').trim();
|
|
77
|
+
if (override) {
|
|
78
|
+
return override;
|
|
79
|
+
}
|
|
80
|
+
return `n8n:${ctx.node.getExecutionId()}:${ctx.node.getNode().id}:${ctx.itemIndex}`;
|
|
81
|
+
};
|
|
73
82
|
const getBinaryFieldFromItem = async (ctx, propertyName, errorLabel) => {
|
|
74
83
|
var _a, _b;
|
|
75
84
|
const { node, itemIndex, items } = ctx;
|
|
@@ -93,9 +102,9 @@ const getBinaryFieldFromItem = async (ctx, propertyName, errorLabel) => {
|
|
|
93
102
|
}
|
|
94
103
|
};
|
|
95
104
|
const PLATFORM_SUPPORT = {
|
|
96
|
-
uploadPhotos: ['bluesky', 'facebook', 'instagram', 'linkedin', 'pinterest', 'threads', 'tiktok', 'x', 'reddit', 'google_business'],
|
|
97
|
-
uploadVideo: ['bluesky', 'facebook', 'instagram', 'linkedin', 'pinterest', 'threads', 'tiktok', 'x', 'youtube', 'google_business'],
|
|
98
|
-
uploadText: ['bluesky', 'facebook', 'linkedin', 'reddit', 'threads', 'x', 'google_business'],
|
|
105
|
+
uploadPhotos: ['bluesky', 'discord', 'facebook', 'instagram', 'linkedin', 'pinterest', 'telegram', 'threads', 'tiktok', 'x', 'reddit', 'google_business'],
|
|
106
|
+
uploadVideo: ['bluesky', 'discord', 'facebook', 'instagram', 'linkedin', 'pinterest', 'telegram', 'threads', 'tiktok', 'x', 'youtube', 'google_business'],
|
|
107
|
+
uploadText: ['bluesky', 'discord', 'facebook', 'linkedin', 'reddit', 'telegram', 'threads', 'x', 'google_business'],
|
|
99
108
|
uploadDocument: ['linkedin'],
|
|
100
109
|
};
|
|
101
110
|
const DESCRIPTION_ENABLED_PLATFORMS = new Set(['linkedin', 'facebook', 'youtube', 'pinterest', 'tiktok']);
|
|
@@ -217,6 +226,10 @@ const prepareUploadBase = (ctx, operation) => {
|
|
|
217
226
|
}
|
|
218
227
|
const uploadAsync = ctx.node.getNodeParameter('uploadAsync', ctx.itemIndex);
|
|
219
228
|
formData.async_upload = String(uploadAsync);
|
|
229
|
+
const autogenerate = ctx.node.getNodeParameter('autogenerate', ctx.itemIndex, false);
|
|
230
|
+
if (autogenerate) {
|
|
231
|
+
formData.autogenerate = 'true';
|
|
232
|
+
}
|
|
220
233
|
const rawPlatforms = ctx.node.getNodeParameter('platform', ctx.itemIndex);
|
|
221
234
|
const platforms = getFilteredPlatforms(operation, Array.isArray(rawPlatforms) ? rawPlatforms : []);
|
|
222
235
|
formData['platform[]'] = platforms;
|
|
@@ -835,10 +848,12 @@ const buildMonitoringRequest = (ctx) => {
|
|
|
835
848
|
case 'getAnalytics': {
|
|
836
849
|
const profileUsername = ctx.node.getNodeParameter('analyticsProfileUsername', ctx.itemIndex);
|
|
837
850
|
const analyticsPlatforms = ctx.node.getNodeParameter('analyticsPlatforms', ctx.itemIndex, []);
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
851
|
+
if (!Array.isArray(analyticsPlatforms) || analyticsPlatforms.length === 0) {
|
|
852
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), 'Select at least one platform: the analytics API rejects a request without "platforms".', {
|
|
853
|
+
itemIndex: ctx.itemIndex,
|
|
854
|
+
});
|
|
841
855
|
}
|
|
856
|
+
const qs = { platforms: analyticsPlatforms.join(',') };
|
|
842
857
|
return {
|
|
843
858
|
endpoint: `/analytics/${encodeURIComponent(profileUsername)}`,
|
|
844
859
|
method: 'GET',
|
|
@@ -847,6 +862,77 @@ const buildMonitoringRequest = (ctx) => {
|
|
|
847
862
|
waitForCompletion: false,
|
|
848
863
|
};
|
|
849
864
|
}
|
|
865
|
+
case 'getPostAnalytics': {
|
|
866
|
+
const requestId = ctx.node.getNodeParameter('postAnalyticsRequestId', ctx.itemIndex);
|
|
867
|
+
const platform = ctx.node.getNodeParameter('postAnalyticsPlatform', ctx.itemIndex, '');
|
|
868
|
+
const qs = {};
|
|
869
|
+
if (platform) {
|
|
870
|
+
qs.platform = platform;
|
|
871
|
+
}
|
|
872
|
+
return {
|
|
873
|
+
endpoint: `/uploadposts/post-analytics/${encodeURIComponent(requestId)}`,
|
|
874
|
+
method: 'GET',
|
|
875
|
+
qs,
|
|
876
|
+
isUploadOperation: false,
|
|
877
|
+
waitForCompletion: false,
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
case 'getPostAnalyticsByPlatformId': {
|
|
881
|
+
return {
|
|
882
|
+
endpoint: '/uploadposts/post-analytics',
|
|
883
|
+
method: 'GET',
|
|
884
|
+
qs: {
|
|
885
|
+
platform_post_id: ctx.node.getNodeParameter('platformPostId', ctx.itemIndex),
|
|
886
|
+
platform: ctx.node.getNodeParameter('nativePostPlatform', ctx.itemIndex),
|
|
887
|
+
user: ctx.node.getNodeParameter('nativePostProfileUsername', ctx.itemIndex),
|
|
888
|
+
},
|
|
889
|
+
isUploadOperation: false,
|
|
890
|
+
waitForCompletion: false,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
case 'getPlatformMetrics': {
|
|
894
|
+
return {
|
|
895
|
+
endpoint: '/uploadposts/platform-metrics',
|
|
896
|
+
method: 'GET',
|
|
897
|
+
isUploadOperation: false,
|
|
898
|
+
waitForCompletion: false,
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
case 'getTotalImpressions': {
|
|
902
|
+
const profileUsername = ctx.node.getNodeParameter('impressionsProfileUsername', ctx.itemIndex);
|
|
903
|
+
const qs = {};
|
|
904
|
+
const period = ctx.node.getNodeParameter('impressionsPeriod', ctx.itemIndex, '');
|
|
905
|
+
const startDate = ctx.node.getNodeParameter('impressionsStartDate', ctx.itemIndex, '');
|
|
906
|
+
const endDate = ctx.node.getNodeParameter('impressionsEndDate', ctx.itemIndex, '');
|
|
907
|
+
const platforms = ctx.node.getNodeParameter('impressionsPlatforms', ctx.itemIndex, []);
|
|
908
|
+
const breakdown = ctx.node.getNodeParameter('impressionsBreakdown', ctx.itemIndex, false);
|
|
909
|
+
if (period)
|
|
910
|
+
qs.period = period;
|
|
911
|
+
if (startDate)
|
|
912
|
+
qs.start_date = startDate;
|
|
913
|
+
if (endDate)
|
|
914
|
+
qs.end_date = endDate;
|
|
915
|
+
if (Array.isArray(platforms) && platforms.length > 0)
|
|
916
|
+
qs.platform = platforms.join(',');
|
|
917
|
+
if (breakdown)
|
|
918
|
+
qs.breakdown = 'true';
|
|
919
|
+
return {
|
|
920
|
+
endpoint: `/uploadposts/total-impressions/${encodeURIComponent(profileUsername)}`,
|
|
921
|
+
method: 'GET',
|
|
922
|
+
qs,
|
|
923
|
+
isUploadOperation: false,
|
|
924
|
+
waitForCompletion: false,
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
case 'getRedditDetailedPosts': {
|
|
928
|
+
return {
|
|
929
|
+
endpoint: '/uploadposts/reddit/detailed-posts/',
|
|
930
|
+
method: 'GET',
|
|
931
|
+
qs: { profile_username: ctx.node.getNodeParameter('redditProfileUsername', ctx.itemIndex) },
|
|
932
|
+
isUploadOperation: false,
|
|
933
|
+
waitForCompletion: false,
|
|
934
|
+
};
|
|
935
|
+
}
|
|
850
936
|
case 'listScheduled': {
|
|
851
937
|
return {
|
|
852
938
|
endpoint: '/uploadposts/schedule',
|
|
@@ -929,6 +1015,7 @@ const buildUserRequest = (ctx) => {
|
|
|
929
1015
|
const readonlyCalendar = ctx.node.getNodeParameter('readonlyCalendar', ctx.itemIndex, false);
|
|
930
1016
|
const connectTitle = ctx.node.getNodeParameter('connectTitle', ctx.itemIndex, '');
|
|
931
1017
|
const connectDescription = ctx.node.getNodeParameter('connectDescription', ctx.itemIndex, '');
|
|
1018
|
+
const language = ctx.node.getNodeParameter('language', ctx.itemIndex, '');
|
|
932
1019
|
const body = { username };
|
|
933
1020
|
if (redirectUrl)
|
|
934
1021
|
body.redirect_url = redirectUrl;
|
|
@@ -944,6 +1031,8 @@ const buildUserRequest = (ctx) => {
|
|
|
944
1031
|
body.connect_title = connectTitle;
|
|
945
1032
|
if (connectDescription)
|
|
946
1033
|
body.connect_description = connectDescription;
|
|
1034
|
+
if (language)
|
|
1035
|
+
body.language = language;
|
|
947
1036
|
return {
|
|
948
1037
|
endpoint: '/uploadposts/users/generate-jwt',
|
|
949
1038
|
method: 'POST',
|
|
@@ -1153,8 +1242,7 @@ const pollUploadStatus = async (node, requestId, pollInterval, pollTimeout) => {
|
|
|
1153
1242
|
const statusData = await node.helpers.httpRequestWithAuthentication.call(node, 'uploadPostApi', statusOptions);
|
|
1154
1243
|
finalData = statusData;
|
|
1155
1244
|
const statusValue = (statusData && statusData.status);
|
|
1156
|
-
if (
|
|
1157
|
-
(typeof statusValue === 'string' && ['success', 'completed', 'failed', 'error', 'scheduled', 'queued', 'pending'].includes(statusValue.toLowerCase()))) {
|
|
1245
|
+
if (typeof statusValue === 'string' && TERMINAL_UPLOAD_STATUSES.includes(statusValue.toLowerCase())) {
|
|
1158
1246
|
break;
|
|
1159
1247
|
}
|
|
1160
1248
|
}
|
|
@@ -1203,9 +1291,9 @@ class UploadPost {
|
|
|
1203
1291
|
noDataExpression: true,
|
|
1204
1292
|
options: [
|
|
1205
1293
|
{ name: 'Upload Document', value: 'uploadDocument', action: 'Upload a document', description: 'Upload a document (PDF, PPT, PPTX, DOC, DOCX) as a native carousel/viewer (Supports: LinkedIn only)' },
|
|
1206
|
-
{ name: 'Upload Photo(s)', value: 'uploadPhotos', action: 'Upload photos', description: 'Upload one or more photos (Supports: TikTok, Instagram, LinkedIn, Facebook, X, Threads)' },
|
|
1207
|
-
{ name: 'Upload Text', value: 'uploadText', action: 'Upload a text post', description: 'Upload a text-based post (Supports: X, LinkedIn, Facebook, Threads)' },
|
|
1208
|
-
{ name: 'Upload Video', value: 'uploadVideo', action: 'Upload a video', description: 'Upload a single video (Supports: TikTok, Instagram, LinkedIn, YouTube, Facebook, X, Threads)' },
|
|
1294
|
+
{ name: 'Upload Photo(s)', value: 'uploadPhotos', action: 'Upload photos', description: 'Upload one or more photos (Supports: TikTok, Instagram, LinkedIn, Facebook, X, Threads, Discord, Telegram)' },
|
|
1295
|
+
{ name: 'Upload Text', value: 'uploadText', action: 'Upload a text post', description: 'Upload a text-based post (Supports: X, LinkedIn, Facebook, Threads, Discord, Telegram)' },
|
|
1296
|
+
{ name: 'Upload Video', value: 'uploadVideo', action: 'Upload a video', description: 'Upload a single video (Supports: TikTok, Instagram, LinkedIn, YouTube, Facebook, X, Threads, Discord, Telegram)' },
|
|
1209
1297
|
],
|
|
1210
1298
|
default: 'uploadPhotos',
|
|
1211
1299
|
displayOptions: { show: { resource: ['uploads'] } },
|
|
@@ -1220,6 +1308,11 @@ class UploadPost {
|
|
|
1220
1308
|
{ name: 'Edit Scheduled Post', value: 'editScheduled', action: 'Edit scheduled post', description: 'Edit schedule details (like date/time) by job ID' },
|
|
1221
1309
|
{ name: 'Get Analytics', value: 'getAnalytics', action: 'Get analytics', description: 'Retrieve aggregated analytics for uploads' },
|
|
1222
1310
|
{ name: 'Get Job Status', value: 'getJobStatus', action: 'Get job status', description: 'Check the status of a scheduled or queued post using the job_id' },
|
|
1311
|
+
{ name: 'Get Platform Metrics', value: 'getPlatformMetrics', action: 'Get platform metrics', description: 'List the analytics metrics available for each platform' },
|
|
1312
|
+
{ name: 'Get Post Analytics', value: 'getPostAnalytics', action: 'Get post analytics', description: 'Retrieve per-post analytics for an upload using its request_id' },
|
|
1313
|
+
{ 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' },
|
|
1314
|
+
{ name: 'Get Reddit Detailed Posts', value: 'getRedditDetailedPosts', action: 'Get reddit detailed posts', description: 'Retrieve detailed Reddit posts with full media information' },
|
|
1315
|
+
{ name: 'Get Total Impressions', value: 'getTotalImpressions', action: 'Get total impressions', description: 'Retrieve impressions aggregated across connected platforms for a date range' },
|
|
1223
1316
|
{ name: 'Get Upload History', value: 'getHistory', action: 'Get upload history', description: 'List past uploads with optional filters' },
|
|
1224
1317
|
{ name: 'Get Upload Status', value: 'getStatus', action: 'Get upload status', description: 'Check the status of an upload using the request_id' },
|
|
1225
1318
|
{ name: 'List Scheduled Posts', value: 'listScheduled', action: 'List scheduled posts', description: 'List your scheduled (future) posts' },
|
|
@@ -1670,13 +1763,37 @@ class UploadPost {
|
|
|
1670
1763
|
name: 'uploadAsync',
|
|
1671
1764
|
type: 'boolean',
|
|
1672
1765
|
default: true,
|
|
1673
|
-
description: 'Whether to process the upload asynchronously and return immediately.
|
|
1766
|
+
description: 'Whether to process the upload asynchronously and return immediately. Recommended: leaving this off holds the HTTP connection open, and if it exceeds the server\'s synchronous wait window the upload switches to asynchronous processing anyway. In that case, use the request_id with the Upload Status endpoint to check the upload status and result.',
|
|
1674
1767
|
displayOptions: {
|
|
1675
1768
|
show: {
|
|
1676
1769
|
operation: ['uploadPhotos', 'uploadVideo', 'uploadText', 'uploadDocument']
|
|
1677
1770
|
}
|
|
1678
1771
|
},
|
|
1679
1772
|
},
|
|
1773
|
+
{
|
|
1774
|
+
displayName: 'Idempotency Key',
|
|
1775
|
+
name: 'idempotencyKey',
|
|
1776
|
+
type: 'string',
|
|
1777
|
+
default: '',
|
|
1778
|
+
description: 'Key the API uses to collapse duplicate uploads within 24 hours. Leave empty to derive one automatically from the execution, node and item, which is what prevents a retried upload from posting twice. Set it explicitly only when you drive retries yourself.',
|
|
1779
|
+
displayOptions: {
|
|
1780
|
+
show: {
|
|
1781
|
+
operation: ['uploadPhotos', 'uploadVideo', 'uploadText', 'uploadDocument']
|
|
1782
|
+
}
|
|
1783
|
+
},
|
|
1784
|
+
},
|
|
1785
|
+
{
|
|
1786
|
+
displayName: 'Autogenerate Copy With AI',
|
|
1787
|
+
name: 'autogenerate',
|
|
1788
|
+
type: 'boolean',
|
|
1789
|
+
default: false,
|
|
1790
|
+
description: 'Whether the server should use AI to generate native per-platform title/description from the media and fill any platform field left empty',
|
|
1791
|
+
displayOptions: {
|
|
1792
|
+
show: {
|
|
1793
|
+
operation: ['uploadPhotos', 'uploadVideo']
|
|
1794
|
+
}
|
|
1795
|
+
},
|
|
1796
|
+
},
|
|
1680
1797
|
{
|
|
1681
1798
|
displayName: 'Wait for Completion',
|
|
1682
1799
|
name: 'waitForCompletion',
|
|
@@ -1800,19 +1917,171 @@ class UploadPost {
|
|
|
1800
1917
|
displayOptions: { show: { operation: ["editScheduled"] } },
|
|
1801
1918
|
},
|
|
1802
1919
|
{
|
|
1803
|
-
displayName: 'Platforms
|
|
1920
|
+
displayName: 'Platforms',
|
|
1804
1921
|
name: 'analyticsPlatforms',
|
|
1805
1922
|
type: 'multiOptions',
|
|
1923
|
+
required: true,
|
|
1806
1924
|
options: [
|
|
1925
|
+
{ name: 'Facebook', value: 'facebook' },
|
|
1807
1926
|
{ name: 'Instagram', value: 'instagram' },
|
|
1808
1927
|
{ name: 'LinkedIn', value: 'linkedin' },
|
|
1809
|
-
{ name: '
|
|
1928
|
+
{ name: 'Pinterest', value: 'pinterest' },
|
|
1929
|
+
{ name: 'Reddit', value: 'reddit' },
|
|
1930
|
+
{ name: 'Threads', value: 'threads' },
|
|
1931
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
1810
1932
|
{ name: 'X (Twitter)', value: 'x' },
|
|
1933
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
1811
1934
|
],
|
|
1812
1935
|
default: [],
|
|
1813
|
-
description: 'Platforms to fetch analytics for
|
|
1936
|
+
description: 'Platforms to fetch analytics for. Analytics are not available for Bluesky, Discord, Google Business or Telegram.',
|
|
1814
1937
|
displayOptions: { show: { operation: ['getAnalytics'] } },
|
|
1815
1938
|
},
|
|
1939
|
+
{
|
|
1940
|
+
displayName: 'Request ID',
|
|
1941
|
+
name: 'postAnalyticsRequestId',
|
|
1942
|
+
type: 'string',
|
|
1943
|
+
required: true,
|
|
1944
|
+
default: '',
|
|
1945
|
+
description: 'The request_id returned by the upload you want metrics for',
|
|
1946
|
+
displayOptions: { show: { operation: ['getPostAnalytics'] } },
|
|
1947
|
+
},
|
|
1948
|
+
{
|
|
1949
|
+
displayName: 'Platform',
|
|
1950
|
+
name: 'postAnalyticsPlatform',
|
|
1951
|
+
type: 'options',
|
|
1952
|
+
options: [
|
|
1953
|
+
{ name: 'All Platforms', value: '' },
|
|
1954
|
+
{ name: 'Facebook', value: 'facebook' },
|
|
1955
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
1956
|
+
{ name: 'LinkedIn', value: 'linkedin' },
|
|
1957
|
+
{ name: 'Pinterest', value: 'pinterest' },
|
|
1958
|
+
{ name: 'Reddit', value: 'reddit' },
|
|
1959
|
+
{ name: 'Threads', value: 'threads' },
|
|
1960
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
1961
|
+
{ name: 'X (Twitter)', value: 'x' },
|
|
1962
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
1963
|
+
],
|
|
1964
|
+
default: '',
|
|
1965
|
+
description: 'Restrict the result to a single platform',
|
|
1966
|
+
displayOptions: { show: { operation: ['getPostAnalytics'] } },
|
|
1967
|
+
},
|
|
1968
|
+
{
|
|
1969
|
+
displayName: 'Platform Post ID',
|
|
1970
|
+
name: 'platformPostId',
|
|
1971
|
+
type: 'string',
|
|
1972
|
+
required: true,
|
|
1973
|
+
default: '',
|
|
1974
|
+
description: 'The post ID as assigned by the social platform itself',
|
|
1975
|
+
displayOptions: { show: { operation: ['getPostAnalyticsByPlatformId'] } },
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
displayName: 'Platform',
|
|
1979
|
+
name: 'nativePostPlatform',
|
|
1980
|
+
type: 'options',
|
|
1981
|
+
required: true,
|
|
1982
|
+
options: [
|
|
1983
|
+
{ name: 'Facebook', value: 'facebook' },
|
|
1984
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
1985
|
+
{ name: 'LinkedIn', value: 'linkedin' },
|
|
1986
|
+
{ name: 'Pinterest', value: 'pinterest' },
|
|
1987
|
+
{ name: 'Reddit', value: 'reddit' },
|
|
1988
|
+
{ name: 'Threads', value: 'threads' },
|
|
1989
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
1990
|
+
{ name: 'X (Twitter)', value: 'x' },
|
|
1991
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
1992
|
+
],
|
|
1993
|
+
default: 'instagram',
|
|
1994
|
+
description: 'Platform the post lives on',
|
|
1995
|
+
displayOptions: { show: { operation: ['getPostAnalyticsByPlatformId'] } },
|
|
1996
|
+
},
|
|
1997
|
+
{
|
|
1998
|
+
displayName: 'Profile Username',
|
|
1999
|
+
name: 'nativePostProfileUsername',
|
|
2000
|
+
type: 'string',
|
|
2001
|
+
required: true,
|
|
2002
|
+
default: '',
|
|
2003
|
+
description: 'Upload-Post profile that owns the connected account',
|
|
2004
|
+
displayOptions: { show: { operation: ['getPostAnalyticsByPlatformId'] } },
|
|
2005
|
+
},
|
|
2006
|
+
{
|
|
2007
|
+
displayName: 'Profile Username',
|
|
2008
|
+
name: 'impressionsProfileUsername',
|
|
2009
|
+
type: 'string',
|
|
2010
|
+
required: true,
|
|
2011
|
+
default: '',
|
|
2012
|
+
description: 'Profile username to aggregate impressions for',
|
|
2013
|
+
displayOptions: { show: { operation: ['getTotalImpressions'] } },
|
|
2014
|
+
},
|
|
2015
|
+
{
|
|
2016
|
+
displayName: 'Period',
|
|
2017
|
+
name: 'impressionsPeriod',
|
|
2018
|
+
type: 'options',
|
|
2019
|
+
options: [
|
|
2020
|
+
{ name: 'Custom Range (Use Start/End Date)', value: '' },
|
|
2021
|
+
{ name: 'Last 3 Months', value: 'last_3months' },
|
|
2022
|
+
{ name: 'Last Day', value: 'last_day' },
|
|
2023
|
+
{ name: 'Last Month', value: 'last_month' },
|
|
2024
|
+
{ name: 'Last Week', value: 'last_week' },
|
|
2025
|
+
{ name: 'Last Year', value: 'last_year' },
|
|
2026
|
+
],
|
|
2027
|
+
default: '',
|
|
2028
|
+
description: 'Preset range. Leave on custom range to use Start and End Date instead. Defaults to the last 30 days when nothing is set.',
|
|
2029
|
+
displayOptions: { show: { operation: ['getTotalImpressions'] } },
|
|
2030
|
+
},
|
|
2031
|
+
{
|
|
2032
|
+
displayName: 'Start Date',
|
|
2033
|
+
name: 'impressionsStartDate',
|
|
2034
|
+
type: 'string',
|
|
2035
|
+
default: '',
|
|
2036
|
+
placeholder: 'YYYY-MM-DD',
|
|
2037
|
+
description: 'Start of a custom date range. Ignored when Period is set.',
|
|
2038
|
+
displayOptions: { show: { operation: ['getTotalImpressions'] } },
|
|
2039
|
+
},
|
|
2040
|
+
{
|
|
2041
|
+
displayName: 'End Date',
|
|
2042
|
+
name: 'impressionsEndDate',
|
|
2043
|
+
type: 'string',
|
|
2044
|
+
default: '',
|
|
2045
|
+
placeholder: 'YYYY-MM-DD',
|
|
2046
|
+
description: 'End of a custom date range. Ignored when Period is set.',
|
|
2047
|
+
displayOptions: { show: { operation: ['getTotalImpressions'] } },
|
|
2048
|
+
},
|
|
2049
|
+
{
|
|
2050
|
+
displayName: 'Platforms',
|
|
2051
|
+
name: 'impressionsPlatforms',
|
|
2052
|
+
type: 'multiOptions',
|
|
2053
|
+
options: [
|
|
2054
|
+
{ name: 'Facebook', value: 'facebook' },
|
|
2055
|
+
{ name: 'Instagram', value: 'instagram' },
|
|
2056
|
+
{ name: 'LinkedIn', value: 'linkedin' },
|
|
2057
|
+
{ name: 'Pinterest', value: 'pinterest' },
|
|
2058
|
+
{ name: 'Reddit', value: 'reddit' },
|
|
2059
|
+
{ name: 'Threads', value: 'threads' },
|
|
2060
|
+
{ name: 'TikTok', value: 'tiktok' },
|
|
2061
|
+
{ name: 'X (Twitter)', value: 'x' },
|
|
2062
|
+
{ name: 'YouTube', value: 'youtube' },
|
|
2063
|
+
],
|
|
2064
|
+
default: [],
|
|
2065
|
+
description: 'Restrict the aggregate to these platforms. Leave empty for all.',
|
|
2066
|
+
displayOptions: { show: { operation: ['getTotalImpressions'] } },
|
|
2067
|
+
},
|
|
2068
|
+
{
|
|
2069
|
+
displayName: 'Breakdown',
|
|
2070
|
+
name: 'impressionsBreakdown',
|
|
2071
|
+
type: 'boolean',
|
|
2072
|
+
default: false,
|
|
2073
|
+
description: 'Whether to return impressions broken down per platform instead of a single total',
|
|
2074
|
+
displayOptions: { show: { operation: ['getTotalImpressions'] } },
|
|
2075
|
+
},
|
|
2076
|
+
{
|
|
2077
|
+
displayName: 'Profile Username',
|
|
2078
|
+
name: 'redditProfileUsername',
|
|
2079
|
+
type: 'string',
|
|
2080
|
+
required: true,
|
|
2081
|
+
default: '',
|
|
2082
|
+
description: 'Profile username whose Reddit posts you want',
|
|
2083
|
+
displayOptions: { show: { operation: ['getRedditDetailedPosts'] } },
|
|
2084
|
+
},
|
|
1816
2085
|
{
|
|
1817
2086
|
displayName: 'New User Identifier',
|
|
1818
2087
|
name: 'newUser',
|
|
@@ -1864,9 +2133,11 @@ class UploadPost {
|
|
|
1864
2133
|
name: 'jwtPlatforms',
|
|
1865
2134
|
type: 'multiOptions',
|
|
1866
2135
|
options: [
|
|
2136
|
+
{ name: 'Discord', value: 'discord' },
|
|
1867
2137
|
{ name: 'Facebook', value: 'facebook' },
|
|
1868
2138
|
{ name: 'Instagram', value: 'instagram' },
|
|
1869
2139
|
{ name: 'LinkedIn', value: 'linkedin' },
|
|
2140
|
+
{ name: 'Telegram', value: 'telegram' },
|
|
1870
2141
|
{ name: 'Threads', value: 'threads' },
|
|
1871
2142
|
{ name: 'TikTok', value: 'tiktok' },
|
|
1872
2143
|
{ name: 'X (Twitter)', value: 'x' },
|
|
@@ -1908,6 +2179,22 @@ class UploadPost {
|
|
|
1908
2179
|
description: 'Optional custom description for the connection page',
|
|
1909
2180
|
displayOptions: { show: { operation: ['generateJwt'] } },
|
|
1910
2181
|
},
|
|
2182
|
+
{
|
|
2183
|
+
displayName: 'Language',
|
|
2184
|
+
name: 'language',
|
|
2185
|
+
type: 'options',
|
|
2186
|
+
default: '',
|
|
2187
|
+
description: 'Force the connection page language for this profile. When set to Auto-Detect, the page detects the visitor browser language and falls back to English.',
|
|
2188
|
+
options: [
|
|
2189
|
+
{ name: 'Auto-Detect (Default)', value: '' },
|
|
2190
|
+
{ name: 'English', value: 'en' },
|
|
2191
|
+
{ name: 'French', value: 'fr' },
|
|
2192
|
+
{ name: 'German', value: 'de' },
|
|
2193
|
+
{ name: 'Portuguese', value: 'pt' },
|
|
2194
|
+
{ name: 'Spanish', value: 'es' },
|
|
2195
|
+
],
|
|
2196
|
+
displayOptions: { show: { operation: ['generateJwt'] } },
|
|
2197
|
+
},
|
|
1911
2198
|
{
|
|
1912
2199
|
displayName: 'JWT',
|
|
1913
2200
|
name: 'jwtToken',
|
|
@@ -3171,21 +3458,23 @@ class UploadPost {
|
|
|
3171
3458
|
const operation = this.getCurrentNodeParameter('operation');
|
|
3172
3459
|
const allPlatforms = [
|
|
3173
3460
|
{ name: 'Bluesky', value: 'bluesky' },
|
|
3461
|
+
{ name: 'Discord', value: 'discord' },
|
|
3174
3462
|
{ name: 'Facebook', value: 'facebook' },
|
|
3175
3463
|
{ name: 'Google Business', value: 'google_business' },
|
|
3176
3464
|
{ name: 'Instagram', value: 'instagram' },
|
|
3177
3465
|
{ name: 'LinkedIn', value: 'linkedin' },
|
|
3178
3466
|
{ name: 'Pinterest', value: 'pinterest' },
|
|
3179
3467
|
{ name: 'Reddit', value: 'reddit' },
|
|
3468
|
+
{ name: 'Telegram', value: 'telegram' },
|
|
3180
3469
|
{ name: 'Threads', value: 'threads' },
|
|
3181
3470
|
{ name: 'TikTok', value: 'tiktok' },
|
|
3182
3471
|
{ name: 'X (Twitter)', value: 'x' },
|
|
3183
3472
|
{ name: 'YouTube', value: 'youtube' },
|
|
3184
3473
|
];
|
|
3185
3474
|
const platformSupport = {
|
|
3186
|
-
uploadPhotos: ['bluesky', 'facebook', 'google_business', 'instagram', 'linkedin', 'pinterest', 'threads', 'tiktok', 'x', 'reddit'],
|
|
3187
|
-
uploadVideo: ['bluesky', 'facebook', 'google_business', 'instagram', 'linkedin', 'pinterest', 'threads', 'tiktok', 'x', 'youtube'],
|
|
3188
|
-
uploadText: ['bluesky', 'facebook', 'google_business', 'linkedin', 'reddit', 'threads', 'x'],
|
|
3475
|
+
uploadPhotos: ['bluesky', 'discord', 'facebook', 'google_business', 'instagram', 'linkedin', 'pinterest', 'telegram', 'threads', 'tiktok', 'x', 'reddit'],
|
|
3476
|
+
uploadVideo: ['bluesky', 'discord', 'facebook', 'google_business', 'instagram', 'linkedin', 'pinterest', 'telegram', 'threads', 'tiktok', 'x', 'youtube'],
|
|
3477
|
+
uploadText: ['bluesky', 'discord', 'facebook', 'google_business', 'linkedin', 'reddit', 'telegram', 'threads', 'x'],
|
|
3189
3478
|
};
|
|
3190
3479
|
const supportedPlatforms = platformSupport[operation] || [];
|
|
3191
3480
|
const filtered = allPlatforms.filter(platform => supportedPlatforms.includes(platform.value));
|
|
@@ -3308,7 +3597,7 @@ class UploadPost {
|
|
|
3308
3597
|
};
|
|
3309
3598
|
}
|
|
3310
3599
|
async execute() {
|
|
3311
|
-
var _a, _b;
|
|
3600
|
+
var _a, _b, _c;
|
|
3312
3601
|
const items = this.getInputData();
|
|
3313
3602
|
const returnData = [];
|
|
3314
3603
|
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
@@ -3327,6 +3616,13 @@ class UploadPost {
|
|
|
3327
3616
|
if (config.headers) {
|
|
3328
3617
|
requestOptions.headers = config.headers;
|
|
3329
3618
|
}
|
|
3619
|
+
if (config.isUploadOperation) {
|
|
3620
|
+
requestOptions.headers = {
|
|
3621
|
+
...((_a = requestOptions.headers) !== null && _a !== void 0 ? _a : {}),
|
|
3622
|
+
'Idempotency-Key': buildIdempotencyKey(ctx),
|
|
3623
|
+
'X-Upload-Post-Source': 'n8n',
|
|
3624
|
+
};
|
|
3625
|
+
}
|
|
3330
3626
|
if (config.qs) {
|
|
3331
3627
|
requestOptions.qs = config.qs;
|
|
3332
3628
|
}
|
|
@@ -3351,7 +3647,7 @@ class UploadPost {
|
|
|
3351
3647
|
? responseData.request_id
|
|
3352
3648
|
: undefined;
|
|
3353
3649
|
if (requestId) {
|
|
3354
|
-
finalData = await pollUploadStatus(this, requestId, (
|
|
3650
|
+
finalData = await pollUploadStatus(this, requestId, (_b = config.pollInterval) !== null && _b !== void 0 ? _b : 10, (_c = config.pollTimeout) !== null && _c !== void 0 ? _c : 600);
|
|
3355
3651
|
}
|
|
3356
3652
|
}
|
|
3357
3653
|
returnData.push({
|