n8n-nodes-upload-post 0.5.1 → 0.7.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.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UploadPost = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
+ const NetworkOperations_1 = require("./NetworkOperations");
5
6
  const MANUAL_USER_VALUE = '__manual_user__';
6
7
  const MANUAL_FACEBOOK_VALUE = '__manual_facebook__';
7
8
  const MANUAL_LINKEDIN_VALUE = '__manual_linkedin__';
@@ -120,7 +121,7 @@ const PLATFORM_SUPPORT = {
120
121
  uploadPhotos: ['bluesky', 'discord', 'facebook', 'instagram', 'linkedin', 'pinterest', 'telegram', 'threads', 'tiktok', 'x', 'reddit', 'google_business', 'mastodon', 'lemmy', 'wordpress'],
121
122
  uploadVideo: ['bluesky', 'discord', 'facebook', 'instagram', 'linkedin', 'pinterest', 'reddit', 'telegram', 'threads', 'tiktok', 'x', 'youtube', 'google_business', 'mastodon', 'wordpress'],
122
123
  uploadText: ['bluesky', 'discord', 'facebook', 'linkedin', 'reddit', 'telegram', 'threads', 'x', 'google_business', 'slack', 'mastodon', 'nostr', 'lemmy', 'devto', 'hashnode', 'wordpress', 'whop', 'listmonk'],
123
- uploadDocument: ['linkedin'],
124
+ uploadDocument: ['linkedin', 'telegram'],
124
125
  };
125
126
  const DESCRIPTION_ENABLED_PLATFORMS = new Set(['linkedin', 'facebook', 'youtube', 'pinterest', 'tiktok']);
126
127
  const TITLE_OVERRIDES = [
@@ -900,6 +901,7 @@ const applyCredentialPlatformOptions = (ctx, platforms, formData) => {
900
901
  setIfFilled(formData, 'bluesky_labels', ctx.node.getNodeParameter('blueskyLabels', ctx.itemIndex, ''));
901
902
  setIfFilled(formData, 'bluesky_threadgate', ctx.node.getNodeParameter('blueskyThreadgate', ctx.itemIndex, ''));
902
903
  setIfFilled(formData, 'bluesky_quote_uri', ctx.node.getNodeParameter('blueskyQuoteUri', ctx.itemIndex, ''));
904
+ setIfFilled(formData, 'bluesky_gallery', ctx.node.getNodeParameter('blueskyGallery', ctx.itemIndex, false));
903
905
  }
904
906
  if (has('discord')) {
905
907
  setIfFilled(formData, 'discord_thread_id', ctx.node.getNodeParameter('discordThreadId', ctx.itemIndex, ''));
@@ -1020,6 +1022,7 @@ const applyUploadPlatformOptions = async (ctx, operation, prep) => {
1020
1022
  }
1021
1023
  }
1022
1024
  applyCredentialPlatformOptions(ctx, platforms, formData);
1025
+ (0, NetworkOperations_1.applyNetworkUploadOptions)(ctx.node, ctx.itemIndex, operation, platforms, formData);
1023
1026
  };
1024
1027
  const buildUploadPhotosRequest = async (ctx) => {
1025
1028
  const prep = prepareUploadBase(ctx, 'uploadPhotos');
@@ -1106,6 +1109,7 @@ const buildUploadDocumentRequest = async (ctx) => {
1106
1109
  if (prep.platforms.includes('linkedin') || isManualDoc) {
1107
1110
  applyLinkedinOptions(ctx, 'uploadDocument', prep.formData, isManualDoc);
1108
1111
  }
1112
+ (0, NetworkOperations_1.applyNetworkUploadOptions)(ctx.node, ctx.itemIndex, 'uploadDocument', prep.platforms, prep.formData);
1109
1113
  return {
1110
1114
  endpoint: '/upload_document',
1111
1115
  method: 'POST',
@@ -1332,6 +1336,18 @@ const buildMonitoringRequest = (ctx) => {
1332
1336
  waitForCompletion: false,
1333
1337
  };
1334
1338
  }
1339
+ case 'resolveLinkedinOrganization': {
1340
+ return {
1341
+ endpoint: '/uploadposts/linkedin/resolve-organization',
1342
+ method: 'GET',
1343
+ qs: {
1344
+ user: ctx.node.getNodeParameter('linkedinResolveProfile', ctx.itemIndex),
1345
+ url: ctx.node.getNodeParameter('linkedinResolveUrl', ctx.itemIndex).trim(),
1346
+ },
1347
+ isUploadOperation: false,
1348
+ waitForCompletion: false,
1349
+ };
1350
+ }
1335
1351
  case 'getGoogleBusinessLocations': {
1336
1352
  const qs = {};
1337
1353
  const profile = ctx.node.getNodeParameter('gbpLocationsProfile', ctx.itemIndex, '');
@@ -1417,6 +1433,7 @@ const buildMonitoringRequest = (ctx) => {
1417
1433
  }
1418
1434
  };
1419
1435
  const buildPostActionRequest = (ctx) => {
1436
+ var _a;
1420
1437
  switch (ctx.operation) {
1421
1438
  case 'retryPost': {
1422
1439
  const body = {};
@@ -1440,14 +1457,19 @@ const buildPostActionRequest = (ctx) => {
1440
1457
  };
1441
1458
  }
1442
1459
  case 'unpublishPost': {
1460
+ const body = {
1461
+ platform: ctx.node.getNodeParameter('postActionPlatform', ctx.itemIndex),
1462
+ user: ctx.node.getNodeParameter('postActionUser', ctx.itemIndex),
1463
+ post_id: ctx.node.getNodeParameter('postActionPostId', ctx.itemIndex),
1464
+ };
1465
+ if (body.platform === 'telegram' && ctx.node.getNodeParameter('telegramDeleteAllMessages', ctx.itemIndex, false) === true) {
1466
+ body.delete_all_messages = true;
1467
+ }
1468
+ Object.assign(body, parseJsonObjectParameter(ctx, 'postActionExtraFields', 'Extra Fields (JSON)'));
1443
1469
  return {
1444
1470
  endpoint: '/uploadposts/posts/unpublish',
1445
1471
  method: 'POST',
1446
- body: {
1447
- platform: ctx.node.getNodeParameter('postActionPlatform', ctx.itemIndex),
1448
- user: ctx.node.getNodeParameter('postActionUser', ctx.itemIndex),
1449
- post_id: ctx.node.getNodeParameter('postActionPostId', ctx.itemIndex),
1450
- },
1472
+ body,
1451
1473
  isUploadOperation: false,
1452
1474
  waitForCompletion: false,
1453
1475
  };
@@ -1459,8 +1481,20 @@ const buildPostActionRequest = (ctx) => {
1459
1481
  post_id: ctx.node.getNodeParameter('postActionPostId', ctx.itemIndex),
1460
1482
  };
1461
1483
  const message = ctx.node.getNodeParameter('postActionMessage', ctx.itemIndex, '');
1462
- if (message)
1463
- body.message = message;
1484
+ if (message) {
1485
+ if (body.platform === 'telegram')
1486
+ body.text = message;
1487
+ else if (body.platform === 'google_business')
1488
+ body.summary = message;
1489
+ else
1490
+ body.message = message;
1491
+ }
1492
+ if (body.platform === 'telegram') {
1493
+ const telegramAction = ctx.node.getNodeParameter('telegramEditAction', ctx.itemIndex, '');
1494
+ if (telegramAction)
1495
+ body.action = telegramAction;
1496
+ }
1497
+ Object.assign(body, parseJsonObjectParameter(ctx, 'postActionExtraFields', 'Extra Fields (JSON)'));
1464
1498
  return {
1465
1499
  endpoint: '/uploadposts/posts/edit',
1466
1500
  method: 'POST',
@@ -1476,8 +1510,20 @@ const buildPostActionRequest = (ctx) => {
1476
1510
  post_id: ctx.node.getNodeParameter('postActionPostId', ctx.itemIndex),
1477
1511
  };
1478
1512
  const commentary = ctx.node.getNodeParameter('postActionMessage', ctx.itemIndex, '');
1479
- if (commentary)
1513
+ if (body.platform === 'telegram') {
1514
+ body.mode = ctx.node.getNodeParameter('telegramRepostMode', ctx.itemIndex, 'forward');
1515
+ const target = String((_a = ctx.node.getNodeParameter('telegramRepostTargetChatId', ctx.itemIndex, '')) !== null && _a !== void 0 ? _a : '').trim();
1516
+ if (target)
1517
+ body.target_chat_id = target;
1518
+ if (ctx.node.getNodeParameter('telegramRepostAllMessages', ctx.itemIndex, false) === true)
1519
+ body.all_messages = true;
1520
+ if (commentary && body.mode === 'copy')
1521
+ body.caption = commentary;
1522
+ }
1523
+ else if (commentary) {
1480
1524
  body.commentary = commentary;
1525
+ }
1526
+ Object.assign(body, parseJsonObjectParameter(ctx, 'postActionExtraFields', 'Extra Fields (JSON)'));
1481
1527
  return {
1482
1528
  endpoint: '/uploadposts/posts/repost',
1483
1529
  method: 'POST',
@@ -1661,6 +1707,56 @@ const buildUserRequest = async (ctx) => {
1661
1707
  isUploadOperation: false,
1662
1708
  waitForCompletion: false,
1663
1709
  };
1710
+ case 'listWebhookDeliveries': {
1711
+ const qs = {};
1712
+ const filters = [
1713
+ ['webhookDeliveriesEvent', 'event'],
1714
+ ['webhookDeliveriesStatus', 'status'],
1715
+ ['webhookDeliveriesRequestId', 'request_id'],
1716
+ ['webhookDeliveriesJobId', 'job_id'],
1717
+ ['webhookDeliveriesProfile', 'profile_username'],
1718
+ ];
1719
+ for (const [param, field] of filters) {
1720
+ const value = ctx.node.getNodeParameter(param, ctx.itemIndex, '');
1721
+ if (value)
1722
+ qs[field] = value;
1723
+ }
1724
+ const limit = ctx.node.getNodeParameter('webhookDeliveriesLimit', ctx.itemIndex, 20);
1725
+ if (limit)
1726
+ qs.limit = limit;
1727
+ const page = ctx.node.getNodeParameter('webhookDeliveriesPage', ctx.itemIndex, 1);
1728
+ if (page)
1729
+ qs.page = page;
1730
+ const includePayload = ctx.node.getNodeParameter('webhookDeliveriesIncludePayload', ctx.itemIndex, false);
1731
+ if (includePayload)
1732
+ qs.include_payload = 'true';
1733
+ return {
1734
+ endpoint: '/uploadposts/webhooks/deliveries',
1735
+ method: 'GET',
1736
+ qs,
1737
+ isUploadOperation: false,
1738
+ waitForCompletion: false,
1739
+ };
1740
+ }
1741
+ case 'getWebhookDelivery': {
1742
+ const deliveryId = ctx.node.getNodeParameter('webhookDeliveryId', ctx.itemIndex);
1743
+ return {
1744
+ endpoint: `/uploadposts/webhooks/deliveries/${encodeURIComponent(deliveryId.trim())}`,
1745
+ method: 'GET',
1746
+ isUploadOperation: false,
1747
+ waitForCompletion: false,
1748
+ };
1749
+ }
1750
+ case 'redeliverWebhook': {
1751
+ const deliveryId = ctx.node.getNodeParameter('webhookDeliveryId', ctx.itemIndex);
1752
+ return {
1753
+ endpoint: `/uploadposts/webhooks/deliveries/${encodeURIComponent(deliveryId.trim())}/redeliver`,
1754
+ method: 'POST',
1755
+ body: {},
1756
+ isUploadOperation: false,
1757
+ waitForCompletion: false,
1758
+ };
1759
+ }
1664
1760
  default:
1665
1761
  throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Unsupported user operation: ${ctx.operation}`, {
1666
1762
  itemIndex: ctx.itemIndex,
@@ -1668,7 +1764,139 @@ const buildUserRequest = async (ctx) => {
1668
1764
  }
1669
1765
  };
1670
1766
  const COMMENT_ACTIONS_WITHOUT_POST = new Set(['like', 'unlike']);
1767
+ const AUTODM_RULE_ACTIONS = {
1768
+ pauseAutodmRule: 'pause',
1769
+ resumeAutodmRule: 'resume',
1770
+ deleteAutodmRule: 'delete',
1771
+ };
1772
+ const DM_ACTIONS_NEEDING_MESSAGE_ID = new Set(['react', 'unreact', 'delete']);
1773
+ const parseJsonObjectParameter = (ctx, name, label) => {
1774
+ const raw = ctx.node.getNodeParameter(name, ctx.itemIndex, '');
1775
+ if (raw === '' || raw === undefined || raw === null)
1776
+ return {};
1777
+ if (typeof raw === 'object')
1778
+ return raw;
1779
+ const trimmed = raw.trim();
1780
+ if (!trimmed)
1781
+ return {};
1782
+ try {
1783
+ const parsed = JSON.parse(trimmed);
1784
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
1785
+ return parsed;
1786
+ }
1787
+ catch {
1788
+ }
1789
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `${label} must be a JSON object, e.g. {"quick_replies": [...]}`, {
1790
+ itemIndex: ctx.itemIndex,
1791
+ });
1792
+ };
1793
+ const buildDmRequest = (ctx) => {
1794
+ var _a;
1795
+ const user = ctx.node.getNodeParameter('dmUser', ctx.itemIndex);
1796
+ const platform = ctx.node.getNodeParameter('dmPlatform', ctx.itemIndex, 'instagram');
1797
+ switch (ctx.operation) {
1798
+ case 'sendDm': {
1799
+ const body = { platform, user };
1800
+ const recipientId = ctx.node.getNodeParameter('dmRecipientId', ctx.itemIndex, '');
1801
+ const conversationId = platform === 'bluesky'
1802
+ ? ctx.node.getNodeParameter('dmConversationId', ctx.itemIndex, '')
1803
+ : '';
1804
+ if (!recipientId && !conversationId) {
1805
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), platform === 'bluesky'
1806
+ ? 'Set Recipient ID (handle, DID or profile link) or Conversation ID.'
1807
+ : 'Recipient ID is required.', { itemIndex: ctx.itemIndex });
1808
+ }
1809
+ if (recipientId)
1810
+ body.recipient_id = recipientId;
1811
+ if (conversationId)
1812
+ body.conversation_id = conversationId;
1813
+ const message = ctx.node.getNodeParameter('dmMessage', ctx.itemIndex, '');
1814
+ if (message)
1815
+ body.message = message;
1816
+ const replyTo = ctx.node.getNodeParameter('dmReplyTo', ctx.itemIndex, '');
1817
+ if (replyTo)
1818
+ body.reply_to = replyTo;
1819
+ const senderAction = ctx.node.getNodeParameter(platform === 'bluesky' ? 'dmBlueskySenderAction' : 'dmInstagramSenderAction', ctx.itemIndex, '');
1820
+ if (senderAction) {
1821
+ body.sender_action = senderAction;
1822
+ const messageId = ctx.node.getNodeParameter('dmMessageId', ctx.itemIndex, '');
1823
+ if (messageId)
1824
+ body.message_id = messageId;
1825
+ if (DM_ACTIONS_NEEDING_MESSAGE_ID.has(senderAction) && !messageId) {
1826
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Message ID is required for the ${senderAction} action.`, {
1827
+ itemIndex: ctx.itemIndex,
1828
+ });
1829
+ }
1830
+ if (senderAction === 'react' || senderAction === 'unreact') {
1831
+ const reaction = ctx.node.getNodeParameter('dmReaction', ctx.itemIndex, '');
1832
+ if (reaction)
1833
+ body.reaction = reaction;
1834
+ }
1835
+ }
1836
+ if (platform === 'bluesky') {
1837
+ const sharePostUrl = ctx.node.getNodeParameter('dmBlueskySharePostUrl', ctx.itemIndex, '');
1838
+ if (sharePostUrl)
1839
+ body.attachments = { type: 'post', url: sharePostUrl };
1840
+ }
1841
+ else {
1842
+ const buttons = ctx.node.getNodeParameter('dmButtons', ctx.itemIndex, {});
1843
+ const buttonList = ((_a = buttons.button) !== null && _a !== void 0 ? _a : [])
1844
+ .filter(button => button.title && button.url)
1845
+ .map(button => ({ title: button.title, url: button.url }));
1846
+ if (buttonList.length > 0)
1847
+ body.buttons = buttonList;
1848
+ Object.assign(body, parseJsonObjectParameter(ctx, 'dmExtraFields', 'Extra Fields (JSON)'));
1849
+ }
1850
+ if (!body.message && !body.sender_action && !body.attachments) {
1851
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), 'Message is required unless you send a Sender Action or share a post.', {
1852
+ itemIndex: ctx.itemIndex,
1853
+ });
1854
+ }
1855
+ return {
1856
+ endpoint: '/uploadposts/dms/send',
1857
+ method: 'POST',
1858
+ body,
1859
+ isUploadOperation: false,
1860
+ waitForCompletion: false,
1861
+ };
1862
+ }
1863
+ case 'listDmConversations': {
1864
+ const qs = { platform, user };
1865
+ const limit = ctx.node.getNodeParameter('dmLimit', ctx.itemIndex, 0);
1866
+ if (limit)
1867
+ qs.limit = limit;
1868
+ const messagesLimit = ctx.node.getNodeParameter('dmMessagesLimit', ctx.itemIndex, 0);
1869
+ if (messagesLimit)
1870
+ qs.messages_limit = messagesLimit;
1871
+ const stringParams = [
1872
+ ['dmCursor', 'cursor'],
1873
+ ['dmSince', 'since'],
1874
+ ['dmParticipantId', 'participant_id'],
1875
+ ];
1876
+ if (platform === 'bluesky') {
1877
+ stringParams.push(['dmConversationFilterId', 'conversation_id'], ['dmMessagesCursor', 'messages_cursor'], ['dmStatus', 'status'], ['dmReadState', 'read_state']);
1878
+ }
1879
+ for (const [param, field] of stringParams) {
1880
+ const value = ctx.node.getNodeParameter(param, ctx.itemIndex, '');
1881
+ if (value)
1882
+ qs[field] = value;
1883
+ }
1884
+ return {
1885
+ endpoint: '/uploadposts/dms/conversations',
1886
+ method: 'GET',
1887
+ qs,
1888
+ isUploadOperation: false,
1889
+ waitForCompletion: false,
1890
+ };
1891
+ }
1892
+ default:
1893
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Unsupported direct message operation: ${ctx.operation}`, {
1894
+ itemIndex: ctx.itemIndex,
1895
+ });
1896
+ }
1897
+ };
1671
1898
  const buildInstagramRequest = (ctx) => {
1899
+ var _a;
1672
1900
  switch (ctx.operation) {
1673
1901
  case 'getPostComments': {
1674
1902
  const user = ctx.node.getNodeParameter('instagramUser', ctx.itemIndex);
@@ -1689,6 +1917,12 @@ const buildInstagramRequest = (ctx) => {
1689
1917
  const commentId = ctx.node.getNodeParameter('commentsCommentId', ctx.itemIndex, '');
1690
1918
  if (commentId)
1691
1919
  qs.comment_id = commentId;
1920
+ const limit = ctx.node.getNodeParameter('commentsLimit', ctx.itemIndex, 0);
1921
+ if (limit)
1922
+ qs.limit = limit;
1923
+ const after = ctx.node.getNodeParameter('commentsAfter', ctx.itemIndex, '');
1924
+ if (after)
1925
+ qs.after = after;
1692
1926
  return {
1693
1927
  endpoint: '/uploadposts/comments',
1694
1928
  method: 'GET',
@@ -1792,6 +2026,68 @@ const buildInstagramRequest = (ctx) => {
1792
2026
  waitForCompletion: false,
1793
2027
  };
1794
2028
  }
2029
+ case 'createAutodmRule': {
2030
+ const body = {
2031
+ profile_username: ctx.node.getNodeParameter('autodmRuleProfile', ctx.itemIndex),
2032
+ reply_message: ctx.node.getNodeParameter('autodmRuleReplyMessage', ctx.itemIndex),
2033
+ };
2034
+ const triggers = ctx.node.getNodeParameter('autodmRuleTriggers', ctx.itemIndex, ['comment']);
2035
+ if (Array.isArray(triggers) && triggers.length > 0)
2036
+ body.triggers = triggers;
2037
+ const keywords = ensureArrayFromCommaSeparated(ctx.node.getNodeParameter('autodmRuleKeywords', ctx.itemIndex, ''));
2038
+ if (body.triggers && body.triggers.includes('dm_keyword') && keywords.length === 0) {
2039
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), 'The DM Keyword trigger needs at least one Trigger Keyword.', {
2040
+ itemIndex: ctx.itemIndex,
2041
+ });
2042
+ }
2043
+ if (keywords.length > 0)
2044
+ body.trigger_keywords = keywords;
2045
+ const buttons = ctx.node.getNodeParameter('autodmRuleButtons', ctx.itemIndex, {});
2046
+ const buttonList = ((_a = buttons.button) !== null && _a !== void 0 ? _a : [])
2047
+ .filter(button => button.title && button.url)
2048
+ .map(button => ({ title: button.title, url: button.url }));
2049
+ if (buttonList.length > 0)
2050
+ body.buttons = buttonList;
2051
+ const cooldown = ctx.node.getNodeParameter('autodmRuleCooldownHours', ctx.itemIndex, 24);
2052
+ if (!Number.isInteger(cooldown)) {
2053
+ throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), 'Per-User Cooldown (Hours) must be a whole number from 0 to 720.', {
2054
+ itemIndex: ctx.itemIndex,
2055
+ });
2056
+ }
2057
+ body.per_user_cooldown_hours = cooldown;
2058
+ return {
2059
+ endpoint: '/uploadposts/autodms/rules',
2060
+ method: 'POST',
2061
+ body,
2062
+ isUploadOperation: false,
2063
+ waitForCompletion: false,
2064
+ };
2065
+ }
2066
+ case 'listAutodmRules': {
2067
+ const qs = {};
2068
+ const profile = ctx.node.getNodeParameter('autodmRulesListProfile', ctx.itemIndex, '');
2069
+ if (profile)
2070
+ qs.profile_username = profile;
2071
+ return {
2072
+ endpoint: '/uploadposts/autodms/rules',
2073
+ method: 'GET',
2074
+ qs,
2075
+ isUploadOperation: false,
2076
+ waitForCompletion: false,
2077
+ };
2078
+ }
2079
+ case 'pauseAutodmRule':
2080
+ case 'resumeAutodmRule':
2081
+ case 'deleteAutodmRule': {
2082
+ const action = AUTODM_RULE_ACTIONS[ctx.operation];
2083
+ return {
2084
+ endpoint: `/uploadposts/autodms/rules/${action}`,
2085
+ method: 'POST',
2086
+ body: { rule_id: ctx.node.getNodeParameter('autodmRuleId', ctx.itemIndex) },
2087
+ isUploadOperation: false,
2088
+ waitForCompletion: false,
2089
+ };
2090
+ }
1795
2091
  default:
1796
2092
  throw new n8n_workflow_1.NodeOperationError(ctx.node.getNode(), `Unsupported Instagram operation: ${ctx.operation}`, {
1797
2093
  itemIndex: ctx.itemIndex,
@@ -1864,12 +2160,18 @@ const buildRequestConfig = async (ctx) => {
1864
2160
  return buildUploadDocumentRequest(ctx);
1865
2161
  }
1866
2162
  const resource = ctx.node.getNodeParameter('resource', ctx.itemIndex);
2163
+ if (NetworkOperations_1.NETWORK_RESOURCES.includes(resource)) {
2164
+ return (0, NetworkOperations_1.buildNetworkRequest)(ctx.node, ctx.itemIndex, resource, ctx.operation);
2165
+ }
1867
2166
  if (resource === 'instagram') {
1868
2167
  return buildInstagramRequest(ctx);
1869
2168
  }
1870
2169
  if (resource === 'insights') {
1871
2170
  return buildInsightsRequest(ctx);
1872
2171
  }
2172
+ if (resource === 'dms') {
2173
+ return buildDmRequest(ctx);
2174
+ }
1873
2175
  if (resource === 'monitoring') {
1874
2176
  return buildMonitoringRequest(ctx);
1875
2177
  }
@@ -1976,22 +2278,28 @@ class UploadPost {
1976
2278
  type: 'options',
1977
2279
  noDataExpression: true,
1978
2280
  options: [
2281
+ { name: 'Bluesky', value: 'bluesky' },
1979
2282
  { name: 'Comment', value: 'instagram' },
2283
+ { name: 'Direct Message', value: 'dms' },
2284
+ { name: 'Google Business', value: 'googleBusiness' },
1980
2285
  { name: 'Insight', value: 'insights' },
1981
2286
  { name: 'Post Action', value: 'postActions' },
2287
+ { name: 'Queue & Best Time', value: 'queue' },
1982
2288
  { name: 'Status & History', value: 'monitoring' },
2289
+ { name: 'Telegram', value: 'telegram' },
1983
2290
  { name: 'Upload', value: 'uploads' },
1984
2291
  { name: 'User', value: 'users' },
1985
2292
  ],
1986
2293
  default: 'uploads',
1987
2294
  },
2295
+ ...NetworkOperations_1.networkProperties,
1988
2296
  {
1989
2297
  displayName: 'Operation',
1990
2298
  name: 'operation',
1991
2299
  type: 'options',
1992
2300
  noDataExpression: true,
1993
2301
  options: [
1994
- { 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)' },
2302
+ { name: 'Upload Document', value: 'uploadDocument', action: 'Upload a document', description: 'Upload a document (PDF, PPT, PPTX, DOC, DOCX) as a native carousel/viewer on LinkedIn, or any file up to 50 MB (audio, voice, sticker, GIF...) to Telegram (Supports: LinkedIn, Telegram)' },
1995
2303
  { name: 'Upload Photo(s)', value: 'uploadPhotos', action: 'Upload photos', description: 'Upload one or more photos (Supports: Bluesky, Discord, Facebook, Google Business, Instagram, Lemmy, LinkedIn, Mastodon, Pinterest, Reddit, Telegram, Threads, TikTok, WordPress, X)' },
1996
2304
  { name: 'Upload Text', value: 'uploadText', action: 'Upload a text post', description: 'Upload a text-based post (Supports: Bluesky, Dev.to, Discord, Facebook, Google Business, Hashnode, Lemmy, LinkedIn, Listmonk, Mastodon, Nostr, Reddit, Slack, Telegram, Threads, Whop, WordPress, X)' },
1997
2305
  { name: 'Upload Video', value: 'uploadVideo', action: 'Upload a video', description: 'Upload a single video (Supports: Bluesky, Discord, Facebook, Google Business, Instagram, LinkedIn, Mastodon, Pinterest, Reddit, Telegram, Threads, TikTok, WordPress, X, YouTube)' },
@@ -2013,7 +2321,7 @@ class UploadPost {
2013
2321
  { name: 'Get Job Status', value: 'getJobStatus', action: 'Get job status', description: 'Check the status of a scheduled or queued post using the job_id' },
2014
2322
  { name: 'Get Media', value: 'getMedia', action: 'Get media', description: 'List published media for a connected network (LinkedIn, Facebook, Instagram, YouTube, and others the API exposes)' },
2015
2323
  { name: 'Get Platform Metrics', value: 'getPlatformMetrics', action: 'Get platform metrics', description: 'List the analytics metrics available for each platform' },
2016
- { 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.' },
2324
+ { 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, plus original_publish_id. A TikTok draft gets its metrics once the creator publishes it as public from the TikTok app (TikTok gives no post ID for private posts); after 30 days without a post use Get Post Analytics by Platform ID.' },
2017
2325
  { 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' },
2018
2326
  { name: 'Get Reddit Detailed Posts', value: 'getRedditDetailedPosts', action: 'Get reddit detailed posts', description: 'Retrieve detailed Reddit posts with full media information' },
2019
2327
  { name: 'Get TikTok Locations', value: 'getTiktokLocations', action: 'Get tiktok locations', description: 'Search TikTok locations for a profile to use as tiktok_location_id' },
@@ -2023,6 +2331,7 @@ class UploadPost {
2023
2331
  { name: 'Get Upload History', value: 'getHistory', action: 'Get upload history', description: 'List past uploads with optional filters' },
2024
2332
  { name: 'Get Upload Status', value: 'getStatus', action: 'Get upload status', description: 'Check the status of an upload using the request_id' },
2025
2333
  { name: 'List Scheduled Posts', value: 'listScheduled', action: 'List scheduled posts', description: 'List your scheduled (future) posts' },
2334
+ { name: 'Resolve LinkedIn Organization', value: 'resolveLinkedinOrganization', action: 'Resolve a linkedin organization', description: 'Turn any LinkedIn company, showcase or school Page URL, vanity name or ID into its URN and exact name, ready to mention in a post as @[Exact Name](urn:li:organization:ID). Works for any Page, not only the ones you administer.' },
2026
2335
  { name: 'Search TikTok Music', value: 'searchTiktokMusic', action: 'Search tiktok music', description: 'Search TikTok music by query for a connected profile' },
2027
2336
  ],
2028
2337
  default: 'getStatus',
@@ -2039,9 +2348,12 @@ class UploadPost {
2039
2348
  { name: 'Generate JWT (for Platform Integration)', value: 'generateJwt', action: 'Generate jwt for platform integration', description: 'Generate a connection URL (JWT) for a profile. Only needed when integrating Upload-Post into your own platform.' },
2040
2349
  { name: 'Get Notification Preferences', value: 'getNotificationPrefs', action: 'Get notification preferences', description: 'Get current webhook and notification settings' },
2041
2350
  { name: 'Get User Preferences', value: 'getUserPreferences', action: 'Get user preferences', description: 'Get user preferences including calendar week start day' },
2351
+ { name: 'Get Webhook Delivery', value: 'getWebhookDelivery', action: 'Get a webhook delivery', description: 'Read one logged webhook delivery with its attempts and the exact payload that was sent' },
2042
2352
  { name: 'List Users', value: 'listUsers', action: 'List users', description: 'List Upload-Post users (profiles)' },
2353
+ { name: 'List Webhook Deliveries', value: 'listWebhookDeliveries', action: 'List webhook deliveries', description: 'History of the last 7 days of outgoing webhooks (account and per-profile): status, attempts and the HTTP code your endpoint answered. Use it to check whether an event reached your server.' },
2354
+ { name: 'Redeliver Webhook', value: 'redeliverWebhook', action: 'Redeliver a webhook', description: 'Send a logged delivery again now, with the same body, to the webhook URL configured today. It is a new delivery with its own ID (header X-Upload-Post-Redelivery-Of names the original).' },
2043
2355
  { name: 'Test Notifications', value: 'testNotifications', action: 'Test notifications', description: 'Send a test event to the configured webhook and notification channels' },
2044
- { name: 'Update Notification Preferences', value: 'updateNotificationPrefs', action: 'Update notification preferences', description: 'Configure webhook URL and event types for real-time notifications (upload_completed, social_account.connected, social_account.disconnected, social_account.reauth_required)' },
2356
+ { name: 'Update Notification Preferences', value: 'updateNotificationPrefs', action: 'Update notification preferences', description: 'Configure webhook URL and event types for real-time notifications (upload_completed, social_account.connected, social_account.disconnected, social_account.reauth_required, dm_received, comment_received). Webhooks are delivered at least once with automatic retries: deduplicate by the X-Upload-Post-Delivery header.' },
2045
2357
  { name: 'Update User Preferences', value: 'updateUserPreferences', action: 'Update user preferences', description: 'Update user preferences including calendar week start day (0=Sunday, 1=Monday)' },
2046
2358
  { name: 'Validate JWT (for Platform Integration)', value: 'validateJwt', action: 'Validate jwt for platform integration', description: 'Validate a connection token from your backend. Only needed for custom platform integration.' },
2047
2359
  ],
@@ -2055,11 +2367,16 @@ class UploadPost {
2055
2367
  noDataExpression: true,
2056
2368
  options: [
2057
2369
  { 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.' },
2370
+ { name: 'Create AutoDM Rule', value: 'createAutodmRule', action: 'Create an account wide autodm rule', description: 'Instagram: answer with a DM every comment on any post or reel of the account (present and future), story replies, story mentions and DMs with a keyword. Rules never expire and work from Meta webhooks only.' },
2058
2371
  { name: 'Create Comment', value: 'createComment', action: 'Create a comment', description: 'Post a comment or a reply. Instagram only supports replies (comment ID required). TikTok always needs the video ID, plus comment ID to reply.' },
2372
+ { name: 'Delete AutoDM Rule', value: 'deleteAutodmRule', action: 'Delete an autodm rule', description: 'Delete an account-wide AutoDM rule and drop its queued DMs' },
2059
2373
  { name: 'Delete Comment', value: 'deleteComment', action: 'Delete a comment', description: 'Delete a comment you own or that is on your post' },
2060
- { 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.' },
2374
+ { name: 'Get Post Comments', value: 'getPostComments', action: 'Get post comments', description: 'Retrieve the comments on a post from the network given by Platform. Fill Comment ID to get the replies to that comment instead (Instagram, Facebook, LinkedIn company Pages, TikTok, Threads and YouTube; X and Bluesky answer platform_not_supported).' },
2375
+ { name: 'List AutoDM Rules', value: 'listAutodmRules', action: 'List autodm rules', description: 'List the account-wide AutoDM rules with their status, stats, queued DMs and whether Meta webhooks are arriving for each Instagram account' },
2376
+ { name: 'Pause AutoDM Rule', value: 'pauseAutodmRule', action: 'Pause an autodm rule', description: 'Pause an account-wide AutoDM rule: it answers nothing until resumed' },
2061
2377
  { 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.' },
2062
2378
  { name: 'Public Reply to Comment', value: 'publicReplyToComment', action: 'Public reply to comment', description: 'Post a public reply visible under the original comment. Instagram only.' },
2379
+ { name: 'Resume AutoDM Rule', value: 'resumeAutodmRule', action: 'Resume an autodm rule', description: 'Resume a paused account-wide AutoDM rule. A stopped rule (its Instagram account left the profile) only comes back once the account is connected to that profile again.' },
2063
2380
  ],
2064
2381
  default: 'getPostComments',
2065
2382
  displayOptions: { show: { resource: ['instagram'] } },
@@ -2108,7 +2425,34 @@ class UploadPost {
2108
2425
  name: 'commentsCommentId',
2109
2426
  type: 'string',
2110
2427
  default: '',
2111
- 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.',
2428
+ 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 (Instagram, Facebook, LinkedIn, TikTok, Threads, YouTube). Instagram: the numeric comment ID. Facebook: the {post}_{comment} ID from the listing (Post ID is still needed to pick the Page). LinkedIn: preferably the comment_urn from the listing; a bare ID is only looked up among the first 250 comments of the post. Post ID is always required.',
2429
+ displayOptions: {
2430
+ show: {
2431
+ resource: ['instagram'],
2432
+ operation: ['getPostComments'],
2433
+ },
2434
+ },
2435
+ },
2436
+ {
2437
+ displayName: 'Page Size',
2438
+ name: 'commentsLimit',
2439
+ type: 'number',
2440
+ typeOptions: { minValue: 0, maxValue: 50 },
2441
+ default: 0,
2442
+ description: 'Comments per page, 1-50. 0 = the platform default. LinkedIn\'s top-level listing ignores it; LinkedIn replies apply it.',
2443
+ displayOptions: {
2444
+ show: {
2445
+ resource: ['instagram'],
2446
+ operation: ['getPostComments'],
2447
+ },
2448
+ },
2449
+ },
2450
+ {
2451
+ displayName: 'After (Cursor)',
2452
+ name: 'commentsAfter',
2453
+ type: 'string',
2454
+ default: '',
2455
+ description: 'The pagination.next_cursor value of the previous page, to read the next one. On LinkedIn replies it is a number (the offset of the next page).',
2112
2456
  displayOptions: {
2113
2457
  show: {
2114
2458
  resource: ['instagram'],
@@ -2257,6 +2601,338 @@ class UploadPost {
2257
2601
  },
2258
2602
  },
2259
2603
  },
2604
+ {
2605
+ displayName: 'User Identifier Name or ID',
2606
+ name: 'autodmRuleProfile',
2607
+ type: 'options',
2608
+ noDataExpression: true,
2609
+ required: true,
2610
+ default: '',
2611
+ description: 'Profile with the Instagram professional account connected. Choose from your created profiles. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
2612
+ typeOptions: { loadOptionsMethod: 'getUserProfiles' },
2613
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2614
+ },
2615
+ {
2616
+ displayName: 'Reply Message',
2617
+ name: 'autodmRuleReplyMessage',
2618
+ type: 'string',
2619
+ required: true,
2620
+ default: '',
2621
+ description: 'The DM text (max 1,000 characters; 640 when buttons are added)',
2622
+ typeOptions: { rows: 3 },
2623
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2624
+ },
2625
+ {
2626
+ displayName: 'Triggers',
2627
+ name: 'autodmRuleTriggers',
2628
+ type: 'multiOptions',
2629
+ options: [
2630
+ { name: 'Comment', value: 'comment', description: 'A top-level comment on any post or reel of the account (private reply, one per comment, within 7 days)' },
2631
+ { name: 'DM Keyword', value: 'dm_keyword', description: 'Someone sends a DM containing one of the Trigger Keywords (required for this trigger)' },
2632
+ { name: 'Story Mention', value: 'story_mention', description: 'Someone mentions the account in their story (keywords do not apply: a mention carries no text)' },
2633
+ { name: 'Story Reply', value: 'story_reply', description: 'Someone replies to one of the account\'s stories' },
2634
+ ],
2635
+ default: ['comment'],
2636
+ description: 'What fires the rule. Story replies, story mentions and keyword DMs are answered inside Meta\'s 24-hour messaging window.',
2637
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2638
+ },
2639
+ {
2640
+ displayName: 'Trigger Keywords',
2641
+ name: 'autodmRuleKeywords',
2642
+ type: 'string',
2643
+ default: '',
2644
+ placeholder: 'guide, price, info',
2645
+ description: 'Comma-separated, up to 20. Only events whose text contains one of them fire the rule (case- and accent-insensitive). Leave empty for a rule without a filter. Required for the DM Keyword trigger.',
2646
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2647
+ },
2648
+ {
2649
+ displayName: 'Buttons',
2650
+ name: 'autodmRuleButtons',
2651
+ type: 'fixedCollection',
2652
+ typeOptions: { multipleValues: true, maxValue: 3 },
2653
+ default: {},
2654
+ placeholder: 'Add Button',
2655
+ description: 'Up to 3 link buttons added to the DM',
2656
+ options: [
2657
+ {
2658
+ name: 'button',
2659
+ displayName: 'Button',
2660
+ values: [
2661
+ { displayName: 'Title', name: 'title', type: 'string', default: '', description: 'Button label (max 20 characters)' },
2662
+ { displayName: 'URL', name: 'url', type: 'string', default: '', description: 'Link (http or https) the button opens' },
2663
+ ],
2664
+ },
2665
+ ],
2666
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2667
+ },
2668
+ {
2669
+ displayName: 'Per-User Cooldown (Hours)',
2670
+ name: 'autodmRuleCooldownHours',
2671
+ type: 'number',
2672
+ typeOptions: { minValue: 0, maxValue: 720, numberPrecision: 0 },
2673
+ default: 24,
2674
+ description: 'The same person gets at most one DM from this rule in this many hours, whatever they comment on or reply to. Whole number from 0 to 720. 0 turns it off for comments only: story replies, story mentions and keyword DMs always wait at least 24 hours, so two bots cannot answer each other in a loop.',
2675
+ displayOptions: { show: { resource: ['instagram'], operation: ['createAutodmRule'] } },
2676
+ },
2677
+ {
2678
+ displayName: 'Profile Username',
2679
+ name: 'autodmRulesListProfile',
2680
+ type: 'string',
2681
+ default: '',
2682
+ description: 'Only the rules of this profile. Leave empty for every rule of the account.',
2683
+ displayOptions: { show: { resource: ['instagram'], operation: ['listAutodmRules'] } },
2684
+ },
2685
+ {
2686
+ displayName: 'Rule ID',
2687
+ name: 'autodmRuleId',
2688
+ type: 'string',
2689
+ required: true,
2690
+ default: '',
2691
+ placeholder: 'rule_4f1c2a9e0b7d4c51a2e3f6b8',
2692
+ description: 'The rule_id returned by Create AutoDM Rule or List AutoDM Rules',
2693
+ displayOptions: { show: { resource: ['instagram'], operation: ['pauseAutodmRule', 'resumeAutodmRule', 'deleteAutodmRule'] } },
2694
+ },
2695
+ {
2696
+ displayName: 'Operation',
2697
+ name: 'operation',
2698
+ type: 'options',
2699
+ noDataExpression: true,
2700
+ options: [
2701
+ { name: 'List Conversations', value: 'listDmConversations', action: 'List DM conversations', description: 'List the DM conversations of the account connected to a profile (Instagram or Bluesky), newest first, with participants and recent messages' },
2702
+ { name: 'Send DM', value: 'sendDm', action: 'Send a direct message', description: 'Send a direct message, or a sender action (mark read, react...), from the Instagram or Bluesky account of a profile. Sender actions spend no daily DM quota.' },
2703
+ ],
2704
+ default: 'sendDm',
2705
+ displayOptions: { show: { resource: ['dms'] } },
2706
+ },
2707
+ {
2708
+ displayName: 'User Identifier Name or ID',
2709
+ name: 'dmUser',
2710
+ type: 'options',
2711
+ noDataExpression: true,
2712
+ required: true,
2713
+ default: '',
2714
+ description: 'Choose from your created profiles. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
2715
+ typeOptions: { loadOptionsMethod: 'getUserProfiles' },
2716
+ displayOptions: { show: { resource: ['dms'] } },
2717
+ },
2718
+ {
2719
+ displayName: 'Platform',
2720
+ name: 'dmPlatform',
2721
+ type: 'options',
2722
+ options: [
2723
+ { name: 'Bluesky', value: 'bluesky' },
2724
+ { name: 'Instagram', value: 'instagram' },
2725
+ ],
2726
+ default: 'instagram',
2727
+ description: 'Network of the account. Bluesky needs an app password created with "Allow access to your direct messages" (otherwise 403 bluesky_dm_scope_missing: create a new app password and reconnect Bluesky). Instagram needs the person to have written to the account in the last 24 hours.',
2728
+ displayOptions: { show: { resource: ['dms'] } },
2729
+ },
2730
+ {
2731
+ displayName: 'Recipient ID',
2732
+ name: 'dmRecipientId',
2733
+ type: 'string',
2734
+ default: '',
2735
+ description: 'Instagram: the Instagram-scoped ID of the person (from List Conversations or the dm_received webhook). Bluesky: handle (alice.bsky.social), DID (did:plc:...) or profile link; leave empty when Conversation ID is set.',
2736
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2737
+ },
2738
+ {
2739
+ displayName: 'Conversation ID',
2740
+ name: 'dmConversationId',
2741
+ type: 'string',
2742
+ default: '',
2743
+ description: 'Bluesky: send to this conversation (the ID from List Conversations) instead of looking it up from Recipient ID. Also works for group conversations.',
2744
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['bluesky'] } },
2745
+ },
2746
+ {
2747
+ displayName: 'Message',
2748
+ name: 'dmMessage',
2749
+ type: 'string',
2750
+ default: '',
2751
+ description: 'Text to send. Required unless you send a Sender Action or share a Bluesky post. Bluesky: up to 1,000 characters (an emoji counts as one); links, #hashtags and up to 10 different @handle mentions become clickable.',
2752
+ typeOptions: { rows: 3 },
2753
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2754
+ },
2755
+ {
2756
+ displayName: 'Reply To (Message ID)',
2757
+ name: 'dmReplyTo',
2758
+ type: 'string',
2759
+ default: '',
2760
+ description: 'ID of a message in the same conversation to reply to',
2761
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2762
+ },
2763
+ {
2764
+ displayName: 'Sender Action',
2765
+ name: 'dmInstagramSenderAction',
2766
+ type: 'options',
2767
+ options: [
2768
+ { name: 'Mark Seen', value: 'mark_seen' },
2769
+ { name: 'None', value: '' },
2770
+ { name: 'React', value: 'react' },
2771
+ { name: 'Typing Off', value: 'typing_off' },
2772
+ { name: 'Typing On', value: 'typing_on' },
2773
+ { name: 'Unreact', value: 'unreact' },
2774
+ ],
2775
+ default: '',
2776
+ description: 'Instead of (or with) a message. React and Unreact need Message ID.',
2777
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['instagram'] } },
2778
+ },
2779
+ {
2780
+ displayName: 'Sender Action',
2781
+ name: 'dmBlueskySenderAction',
2782
+ type: 'options',
2783
+ options: [
2784
+ { name: 'Accept Request', value: 'accept' },
2785
+ { name: 'Delete Message (For Me)', value: 'delete' },
2786
+ { name: 'Leave Conversation', value: 'leave' },
2787
+ { name: 'Mark Read', value: 'mark_read' },
2788
+ { name: 'Mute', value: 'mute' },
2789
+ { name: 'None', value: '' },
2790
+ { name: 'React', value: 'react' },
2791
+ { name: 'Unmute', value: 'unmute' },
2792
+ { name: 'Unreact', value: 'unreact' },
2793
+ ],
2794
+ default: '',
2795
+ description: 'An action on the conversation instead of a message. React, Unreact and Delete need Message ID; Mark Read takes it optionally (read up to that message). Pass Recipient ID or Conversation ID to say which conversation.',
2796
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['bluesky'] } },
2797
+ },
2798
+ {
2799
+ displayName: 'Message ID',
2800
+ name: 'dmMessageId',
2801
+ type: 'string',
2802
+ default: '',
2803
+ description: 'The message the sender action applies to',
2804
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2805
+ },
2806
+ {
2807
+ displayName: 'Reaction',
2808
+ name: 'dmReaction',
2809
+ type: 'string',
2810
+ default: '',
2811
+ placeholder: '❤️',
2812
+ description: 'Emoji for React / Unreact. Bluesky accepts a single emoji only (anything else: 400 bluesky_rejected, ReactionInvalidValue).',
2813
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'] } },
2814
+ },
2815
+ {
2816
+ displayName: 'Share Bluesky Post URL',
2817
+ name: 'dmBlueskySharePostUrl',
2818
+ type: 'string',
2819
+ default: '',
2820
+ placeholder: 'https://bsky.app/profile/alice.bsky.social/post/3l5tt...',
2821
+ description: 'A Bluesky post to share in the DM (bsky.app link or at:// URI). Bluesky chat takes no images, video or files.',
2822
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['bluesky'] } },
2823
+ },
2824
+ {
2825
+ displayName: 'Buttons',
2826
+ name: 'dmButtons',
2827
+ type: 'fixedCollection',
2828
+ typeOptions: { multipleValues: true, maxValue: 3 },
2829
+ default: {},
2830
+ placeholder: 'Add Button',
2831
+ description: 'Up to 3 link buttons (Instagram)',
2832
+ options: [
2833
+ {
2834
+ name: 'button',
2835
+ displayName: 'Button',
2836
+ values: [
2837
+ { displayName: 'Title', name: 'title', type: 'string', default: '', description: 'Button label (max 20 characters)' },
2838
+ { displayName: 'URL', name: 'url', type: 'string', default: '', description: 'Link (http or https) the button opens' },
2839
+ ],
2840
+ },
2841
+ ],
2842
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['instagram'] } },
2843
+ },
2844
+ {
2845
+ displayName: 'Extra Fields (JSON)',
2846
+ name: 'dmExtraFields',
2847
+ type: 'json',
2848
+ default: '',
2849
+ description: 'Other Instagram DM fields, merged into the request body as is: attachments (objects with a type and a link or an attachment_id; types image, video, audio, file, like_heart, media_share), quick_replies (up to 13) or generic_template (up to 10 elements)',
2850
+ displayOptions: { show: { resource: ['dms'], operation: ['sendDm'], dmPlatform: ['instagram'] } },
2851
+ },
2852
+ {
2853
+ displayName: 'Page Size',
2854
+ name: 'dmLimit',
2855
+ type: 'number',
2856
+ typeOptions: { minValue: 0, maxValue: 50 },
2857
+ default: 0,
2858
+ description: 'Conversations per page, 1-50 (Bluesky returns at most 25). 0 = the default (20).',
2859
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2860
+ },
2861
+ {
2862
+ displayName: 'Messages per Conversation',
2863
+ name: 'dmMessagesLimit',
2864
+ type: 'number',
2865
+ typeOptions: { minValue: 0, maxValue: 25 },
2866
+ default: 0,
2867
+ description: 'Latest messages returned per conversation, 1-25. 0 = the default (10). On Bluesky 1 is the fastest page.',
2868
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2869
+ },
2870
+ {
2871
+ displayName: 'Cursor',
2872
+ name: 'dmCursor',
2873
+ type: 'string',
2874
+ default: '',
2875
+ description: 'The pagination.next_cursor value of the previous page',
2876
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2877
+ },
2878
+ {
2879
+ displayName: 'Since',
2880
+ name: 'dmSince',
2881
+ type: 'string',
2882
+ default: '',
2883
+ placeholder: '2026-10-20T12:00:00Z',
2884
+ description: 'Only conversations with activity after this moment (Unix seconds, Unix milliseconds or ISO 8601)',
2885
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2886
+ },
2887
+ {
2888
+ displayName: 'Participant ID',
2889
+ name: 'dmParticipantId',
2890
+ type: 'string',
2891
+ default: '',
2892
+ description: 'Only your conversation with this person. Instagram: Instagram-scoped ID. Bluesky: handle or DID (reading never creates the conversation).',
2893
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'] } },
2894
+ },
2895
+ {
2896
+ displayName: 'Conversation ID',
2897
+ name: 'dmConversationFilterId',
2898
+ type: 'string',
2899
+ default: '',
2900
+ description: 'Bluesky: only this conversation (direct or group)',
2901
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'], dmPlatform: ['bluesky'] } },
2902
+ },
2903
+ {
2904
+ displayName: 'Messages Cursor',
2905
+ name: 'dmMessagesCursor',
2906
+ type: 'string',
2907
+ default: '',
2908
+ description: 'Bluesky, with Conversation ID: messages.paging.next_cursor of that conversation, to read older messages',
2909
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'], dmPlatform: ['bluesky'] } },
2910
+ },
2911
+ {
2912
+ displayName: 'Status',
2913
+ name: 'dmStatus',
2914
+ type: 'options',
2915
+ options: [
2916
+ { name: 'All', value: '' },
2917
+ { name: 'Accepted', value: 'accepted' },
2918
+ { name: 'Requests', value: 'request' },
2919
+ ],
2920
+ default: '',
2921
+ description: 'Bluesky: only message requests or only accepted conversations',
2922
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'], dmPlatform: ['bluesky'] } },
2923
+ },
2924
+ {
2925
+ displayName: 'Read State',
2926
+ name: 'dmReadState',
2927
+ type: 'options',
2928
+ options: [
2929
+ { name: 'All', value: '' },
2930
+ { name: 'Unread', value: 'unread' },
2931
+ ],
2932
+ default: '',
2933
+ description: 'Bluesky: only conversations with unread messages',
2934
+ displayOptions: { show: { resource: ['dms'], operation: ['listDmConversations'], dmPlatform: ['bluesky'] } },
2935
+ },
2260
2936
  {
2261
2937
  displayName: 'Operation',
2262
2938
  name: 'operation',
@@ -2264,7 +2940,7 @@ class UploadPost {
2264
2940
  noDataExpression: true,
2265
2941
  options: [
2266
2942
  { name: 'Edit Post', value: 'editPost', action: 'Edit a published post', description: 'Update caption or metadata on a live post. Fields you omit are left unchanged.' },
2267
- { name: 'Repost', value: 'repostPost', action: 'Repost a post', description: 'Repost on LinkedIn or X' },
2943
+ { name: 'Repost', value: 'repostPost', action: 'Repost a post', description: 'Repost on LinkedIn, X or Threads, or forward / copy a Telegram post to the same chat or another one' },
2268
2944
  { name: 'Retry Post', value: 'retryPost', action: 'Retry a failed upload', description: 'Re-enqueue platforms that failed on an upload without re-uploading media. Provide request ID or job ID.' },
2269
2945
  { name: 'Save Pin', value: 'savePin', action: 'Save a pinterest pin', description: 'Save a Pinterest pin to a board' },
2270
2946
  { name: 'Unpublish Post', value: 'unpublishPost', action: 'Unpublish a post', description: 'Delete a live post from a platform. Instagram, TikTok and Threads are not supported.' },
@@ -2322,6 +2998,8 @@ class UploadPost {
2322
2998
  required: true,
2323
2999
  options: [
2324
3000
  { name: 'LinkedIn', value: 'linkedin' },
3001
+ { name: 'Telegram', value: 'telegram' },
3002
+ { name: 'Threads', value: 'threads' },
2325
3003
  { name: 'X (Twitter)', value: 'x' },
2326
3004
  ],
2327
3005
  default: 'linkedin',
@@ -2363,9 +3041,67 @@ class UploadPost {
2363
3041
  name: 'postActionMessage',
2364
3042
  type: 'string',
2365
3043
  default: '',
2366
- description: 'New caption. Facebook uses message, X uses text, LinkedIn uses commentary.',
3044
+ description: 'New caption. Facebook uses message, X uses text, LinkedIn uses commentary, Telegram the new text or caption (repost: the caption of a copy), Google Business the post summary.',
2367
3045
  displayOptions: { show: { resource: ['postActions'], operation: ['editPost', 'repostPost'] } },
2368
3046
  },
3047
+ {
3048
+ displayName: 'Telegram Action',
3049
+ name: 'telegramEditAction',
3050
+ type: 'options',
3051
+ options: [
3052
+ { name: 'None', value: '' },
3053
+ { name: 'Pin', value: 'pin' },
3054
+ { name: 'Stop Live Location', value: 'stop_live_location' },
3055
+ { name: 'Stop Poll', value: 'stop_poll' },
3056
+ { name: 'Unpin', value: 'unpin' },
3057
+ ],
3058
+ default: '',
3059
+ description: 'Pin or unpin the message (silent unless telegram_pin_notify is true), stop a poll (returns the final poll; not together with a new text) or stop a live location',
3060
+ displayOptions: { show: { resource: ['postActions'], operation: ['editPost'], postActionPlatform: ['telegram'] } },
3061
+ },
3062
+ {
3063
+ displayName: 'Telegram Mode',
3064
+ name: 'telegramRepostMode',
3065
+ type: 'options',
3066
+ options: [
3067
+ { name: 'Copy (New Message Without the Link to the Original)', value: 'copy' },
3068
+ { name: 'Forward (Keeps "Forwarded From")', value: 'forward' },
3069
+ ],
3070
+ default: 'forward',
3071
+ displayOptions: { show: { resource: ['postActions'], operation: ['repostPost'], repostPlatform: ['telegram'] } },
3072
+ },
3073
+ {
3074
+ displayName: 'Telegram Target Chat ID',
3075
+ name: 'telegramRepostTargetChatId',
3076
+ type: 'string',
3077
+ default: '',
3078
+ description: 'Destination chat ID or public @username (the bot must be able to post there). Empty = the same chat.',
3079
+ displayOptions: { show: { resource: ['postActions'], operation: ['repostPost'], repostPlatform: ['telegram'] } },
3080
+ },
3081
+ {
3082
+ displayName: 'Telegram All Messages',
3083
+ name: 'telegramRepostAllMessages',
3084
+ type: 'boolean',
3085
+ default: false,
3086
+ description: 'Whether to also send the rest of a long caption, the first comment and the text sent before a location, not only the album items',
3087
+ displayOptions: { show: { resource: ['postActions'], operation: ['repostPost'], repostPlatform: ['telegram'] } },
3088
+ },
3089
+ {
3090
+ displayName: 'Telegram Delete All Messages',
3091
+ name: 'telegramDeleteAllMessages',
3092
+ type: 'boolean',
3093
+ default: false,
3094
+ description: 'Whether to delete every message of the post (album items, caption rest, first comment), not only Post ID. Bots can delete messages for 48 hours.',
3095
+ displayOptions: { show: { resource: ['postActions'], operation: ['unpublishPost'], postActionPlatform: ['telegram'] } },
3096
+ },
3097
+ {
3098
+ displayName: 'Extra Fields (JSON)',
3099
+ name: 'postActionExtraFields',
3100
+ type: 'json',
3101
+ default: '',
3102
+ description: 'Other fields merged into the request body as is, e.g. Telegram {"telegram_reply_markup": {...}}, {"telegram_media": "https://example.com/new.jpg", "caption": "Fixed"} or {"telegram_link_preview": false}, Google Business {"cta_type": "LEARN_MORE", "cta_url": "https://example.com"}',
3103
+ displayOptions: { show: { resource: ['postActions'], operation: ['editPost', 'repostPost', 'unpublishPost'] } },
3104
+ },
2369
3105
  {
2370
3106
  displayName: 'Pinterest Board ID',
2371
3107
  name: 'savePinBoardId',
@@ -2602,7 +3338,7 @@ class UploadPost {
2602
3338
  name: 'linkedinTitle',
2603
3339
  type: 'string',
2604
3340
  default: '',
2605
- description: 'Optional override for LinkedIn title',
3341
+ description: 'Optional override for LinkedIn title (the post text of Upload Text). LinkedIn mentions: @[Exact Page Name](urn:li:organization:ID), @[Name](urn:li:person:ID) or @company-vanity. A company name must match LinkedIn exactly, capitals included, or it is published as plain text; get the URN and name with Resolve LinkedIn Organization.',
2606
3342
  displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['linkedin', '__manual_platform__'] } },
2607
3343
  },
2608
3344
  {
@@ -2743,7 +3479,7 @@ class UploadPost {
2743
3479
  name: 'linkedinDescription',
2744
3480
  type: 'string',
2745
3481
  default: '',
2746
- description: 'Override for LinkedIn post commentary. When empty we repeat the main title.',
3482
+ description: 'Override for LinkedIn post commentary. When empty we repeat the main title. Put mentions here rather than only in the title (the title also becomes the images\' alt text). LinkedIn mentions: @[Exact Page Name](urn:li:organization:ID), @[Name](urn:li:person:ID) or @company-vanity. A company name must match LinkedIn exactly, capitals included, or it is published as plain text; get the URN and name with Resolve LinkedIn Organization.',
2747
3483
  displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo'], platform: ['linkedin', '__manual_platform__'] } },
2748
3484
  },
2749
3485
  {
@@ -3084,13 +3820,14 @@ class UploadPost {
3084
3820
  { name: 'LinkedIn', value: 'linkedin' },
3085
3821
  { name: 'Pinterest', value: 'pinterest' },
3086
3822
  { name: 'Reddit', value: 'reddit' },
3823
+ { name: 'Telegram', value: 'telegram' },
3087
3824
  { name: 'Threads', value: 'threads' },
3088
3825
  { name: 'TikTok', value: 'tiktok' },
3089
3826
  { name: 'X (Twitter)', value: 'x' },
3090
3827
  { name: 'YouTube', value: 'youtube' },
3091
3828
  ],
3092
3829
  default: [],
3093
- description: 'Platforms to fetch analytics for. Analytics are not available for Bluesky, Discord, Google Business or Telegram.',
3830
+ description: 'Platforms to fetch analytics for. Telegram returns subscribers or members, reactions and active boosts (the Bot API has no views). Analytics are not available for Discord.',
3094
3831
  displayOptions: { show: { operation: ['getAnalytics'] } },
3095
3832
  },
3096
3833
  {
@@ -3362,6 +4099,27 @@ class UploadPost {
3362
4099
  description: 'Pagination cursor from a previous Get Media response',
3363
4100
  displayOptions: { show: { operation: ['getMedia'] } },
3364
4101
  },
4102
+ {
4103
+ displayName: 'User Identifier Name or ID',
4104
+ name: 'linkedinResolveProfile',
4105
+ type: 'options',
4106
+ noDataExpression: true,
4107
+ required: true,
4108
+ default: '',
4109
+ description: 'Profile whose LinkedIn connection makes the lookup. Choose from your created profiles. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
4110
+ typeOptions: { loadOptionsMethod: 'getUserProfiles' },
4111
+ displayOptions: { show: { operation: ['resolveLinkedinOrganization'] } },
4112
+ },
4113
+ {
4114
+ displayName: 'LinkedIn Page',
4115
+ name: 'linkedinResolveUrl',
4116
+ type: 'string',
4117
+ required: true,
4118
+ default: '',
4119
+ placeholder: 'https://www.linkedin.com/company/linkedin/',
4120
+ description: 'Company, showcase or school Page URL, vanity name (with or without @), numeric ID or urn:li:organization:ID. Person profiles (/in/) cannot be resolved. Live lookups count against 60 per hour per account; cached Pages are free.',
4121
+ displayOptions: { show: { operation: ['resolveLinkedinOrganization'] } },
4122
+ },
3365
4123
  {
3366
4124
  displayName: 'User Identifier Name or ID',
3367
4125
  name: 'gbpLocationsProfile',
@@ -3595,6 +4353,97 @@ class UploadPost {
3595
4353
  description: 'JWT to validate',
3596
4354
  displayOptions: { show: { operation: ['validateJwt'] } },
3597
4355
  },
4356
+ {
4357
+ displayName: 'Event',
4358
+ name: 'webhookDeliveriesEvent',
4359
+ type: 'options',
4360
+ options: [
4361
+ { name: 'Account Connected', value: 'social_account_connected' },
4362
+ { name: 'Account Disconnected', value: 'social_account_disconnected' },
4363
+ { name: 'All Events', value: '' },
4364
+ { name: 'Comment Received', value: 'comment_received' },
4365
+ { name: 'DM Received', value: 'dm_received' },
4366
+ { name: 'Re-Auth Required', value: 'social_account_reauth_required' },
4367
+ { name: 'Upload Completed', value: 'upload_completed' },
4368
+ ],
4369
+ default: '',
4370
+ description: 'Only deliveries of this event',
4371
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4372
+ },
4373
+ {
4374
+ displayName: 'Status',
4375
+ name: 'webhookDeliveriesStatus',
4376
+ type: 'options',
4377
+ options: [
4378
+ { name: 'All Statuses', value: '' },
4379
+ { name: 'Cancelled', value: 'cancelled', description: 'The webhook or the event subscription was removed' },
4380
+ { name: 'Delivered', value: 'delivered' },
4381
+ { name: 'Failed', value: 'failed', description: 'Gave up after 6 attempts, or refused with a 4xx' },
4382
+ { name: 'Pending', value: 'pending', description: 'A retry is scheduled' },
4383
+ ],
4384
+ default: '',
4385
+ description: 'Only deliveries in this state',
4386
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4387
+ },
4388
+ {
4389
+ displayName: 'Request ID',
4390
+ name: 'webhookDeliveriesRequestId',
4391
+ type: 'string',
4392
+ default: '',
4393
+ description: 'Only the deliveries of one upload (request_id of the upload response)',
4394
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4395
+ },
4396
+ {
4397
+ displayName: 'Job ID',
4398
+ name: 'webhookDeliveriesJobId',
4399
+ type: 'string',
4400
+ default: '',
4401
+ description: 'Only the deliveries of one scheduled or async job',
4402
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4403
+ },
4404
+ {
4405
+ displayName: 'Profile Username',
4406
+ name: 'webhookDeliveriesProfile',
4407
+ type: 'string',
4408
+ default: '',
4409
+ description: 'Only the deliveries of that profile\'s webhook',
4410
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4411
+ },
4412
+ {
4413
+ displayName: 'Page Size',
4414
+ name: 'webhookDeliveriesLimit',
4415
+ type: 'number',
4416
+ typeOptions: { minValue: 1, maxValue: 100 },
4417
+ default: 20,
4418
+ description: 'Deliveries per page, 1-100, newest first',
4419
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4420
+ },
4421
+ {
4422
+ displayName: 'Page',
4423
+ name: 'webhookDeliveriesPage',
4424
+ type: 'number',
4425
+ typeOptions: { minValue: 1, maxValue: 50 },
4426
+ default: 1,
4427
+ description: 'Page number, 1-50. For older rows narrow the list with Status, Event or Request ID.',
4428
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4429
+ },
4430
+ {
4431
+ displayName: 'Include Payload',
4432
+ name: 'webhookDeliveriesIncludePayload',
4433
+ type: 'boolean',
4434
+ default: false,
4435
+ description: 'Whether to include the exact body that was sent with each delivery (left out by default to keep the list light; Get Webhook Delivery always includes it)',
4436
+ displayOptions: { show: { resource: ['users'], operation: ['listWebhookDeliveries'] } },
4437
+ },
4438
+ {
4439
+ displayName: 'Delivery ID',
4440
+ name: 'webhookDeliveryId',
4441
+ type: 'string',
4442
+ required: true,
4443
+ default: '',
4444
+ description: 'The delivery_id from List Webhook Deliveries (the X-Upload-Post-Delivery header your endpoint received). Deliveries are kept for 7 days.',
4445
+ displayOptions: { show: { resource: ['users'], operation: ['getWebhookDelivery', 'redeliverWebhook'] } },
4446
+ },
3598
4447
  {
3599
4448
  displayName: 'Webhook Enabled',
3600
4449
  name: 'webhookEnabled',
@@ -3617,10 +4466,12 @@ class UploadPost {
3617
4466
  name: 'webhookEvents',
3618
4467
  type: 'multiOptions',
3619
4468
  options: [
3620
- { name: 'Upload Completed', value: 'upload_completed', description: 'When a post upload finishes (success or failure)' },
3621
4469
  { name: 'Account Connected', value: 'social_account.connected', description: 'When a social account is connected or reconnected' },
3622
4470
  { name: 'Account Disconnected', value: 'social_account.disconnected', description: 'When a social account is disconnected' },
4471
+ { name: 'Comment Received', value: 'comment_received', description: 'When a comment arrives on a connected Instagram, TikTok or LinkedIn Page account' },
4472
+ { name: 'DM Received', value: 'dm_received', description: 'When a direct message arrives on a connected Instagram or Bluesky account (Bluesky is checked every 5 minutes)' },
3623
4473
  { name: 'Re-Auth Required', value: 'social_account.reauth_required', description: 'When a social account needs re-authentication' },
4474
+ { name: 'Upload Completed', value: 'upload_completed', description: 'When a post upload finishes (success or failure)' },
3624
4475
  ],
3625
4476
  default: ['upload_completed', 'social_account.connected', 'social_account.disconnected', 'social_account.reauth_required'],
3626
4477
  description: 'Which webhook events to subscribe to',
@@ -4651,6 +5502,14 @@ class UploadPost {
4651
5502
  description: 'Language code for the Google Business post (e.g. en, es)',
4652
5503
  displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['google_business', '__manual_platform__'] } },
4653
5504
  },
5505
+ {
5506
+ displayName: 'Bluesky Gallery',
5507
+ name: 'blueskyGallery',
5508
+ type: 'boolean',
5509
+ default: false,
5510
+ description: 'Whether to publish up to 10 images as a Bluesky gallery (app.bsky.embed.gallery). Without it Bluesky takes the first 4 images; with more than 10 the first 10 are published and the response carries a warning.',
5511
+ displayOptions: { show: { operation: ['uploadPhotos'], platform: ['bluesky', '__manual_platform__'] } },
5512
+ },
4654
5513
  {
4655
5514
  displayName: 'Bluesky Alt Text',
4656
5515
  name: 'blueskyAltText',
@@ -5926,6 +6785,7 @@ class UploadPost {
5926
6785
  }
5927
6786
  },
5928
6787
  },
6788
+ ...NetworkOperations_1.uploadExtraProperties,
5929
6789
  ],
5930
6790
  };
5931
6791
  this.methods = {
@@ -5960,7 +6820,7 @@ class UploadPost {
5960
6820
  uploadPhotos: ['bluesky', 'discord', 'facebook', 'google_business', 'instagram', 'linkedin', 'pinterest', 'telegram', 'threads', 'tiktok', 'x', 'reddit', 'mastodon', 'lemmy', 'wordpress'],
5961
6821
  uploadVideo: ['bluesky', 'discord', 'facebook', 'google_business', 'instagram', 'linkedin', 'pinterest', 'reddit', 'telegram', 'threads', 'tiktok', 'x', 'youtube', 'mastodon', 'wordpress'],
5962
6822
  uploadText: ['bluesky', 'discord', 'facebook', 'google_business', 'linkedin', 'reddit', 'telegram', 'threads', 'x', 'slack', 'mastodon', 'nostr', 'lemmy', 'devto', 'hashnode', 'wordpress', 'whop', 'listmonk'],
5963
- uploadDocument: ['linkedin'],
6823
+ uploadDocument: ['linkedin', 'telegram'],
5964
6824
  };
5965
6825
  const supportedPlatforms = platformSupport[operation] || [];
5966
6826
  const filtered = allPlatforms.filter(platform => supportedPlatforms.includes(platform.value));