diff --git a/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts b/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts index 45399a3d77..6c5284ee44 100644 --- a/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts @@ -4,6 +4,16 @@ import { } from 'n8n-workflow'; //https://api.slack.com/authentication/oauth-v2 +const userScopes = [ + 'chat:write', + 'conversations:history', + 'conversations:read', + 'files:read', + 'files:write', + 'stars:read', + 'stars:write', +] + export class SlackOAuth2Api implements ICredentialType { name = 'slackOAuth2Api'; @@ -35,7 +45,7 @@ export class SlackOAuth2Api implements ICredentialType { displayName: 'Auth URI Query Parameters', name: 'authQueryParameters', type: 'hidden' as NodePropertyTypes, - default: 'user_scope=chat:write', + default: `user_scope=${userScopes.join(' ')}`, }, { displayName: 'Authentication', diff --git a/packages/nodes-base/nodes/Slack/ChannelDescription.ts b/packages/nodes-base/nodes/Slack/ChannelDescription.ts index 88565716d5..cda5824105 100644 --- a/packages/nodes-base/nodes/Slack/ChannelDescription.ts +++ b/packages/nodes-base/nodes/Slack/ChannelDescription.ts @@ -13,16 +13,86 @@ export const channelOperations = [ }, }, options: [ + { + name: 'Archive', + value: 'archive', + description: 'Archives a conversation.', + }, + { + name: 'Close', + value: 'close', + description: 'Closes a direct message or multi-person direct message.', + }, { name: 'Create', value: 'create', description: 'Initiates a public or private channel-based conversation', }, + { + name: 'Get', + value: 'get', + description: 'Get information about a channel.', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all channels in a Slack team.', + }, + { + name: 'History', + value: 'history', + description: `Get a conversation's history of messages and events.`, + }, { name: 'Invite', value: 'invite', description: 'Invite a user to a channel', }, + { + name: 'Join', + value: 'join', + description: 'Joins an existing conversation.', + }, + { + name: 'Kick', + value: 'kick', + description: 'Removes a user from a channel.', + }, + { + name: 'Leave', + value: 'leave', + description: 'Leaves a conversation.', + }, + { + name: 'Open', + value: 'open', + description: 'Opens or resumes a direct message or multi-person direct message.', + }, + { + name: 'Rename', + value: 'rename', + description: 'Renames a conversation.', + }, + { + name: 'Replies', + value: 'replies', + description: 'Get a thread of messages posted to a channel', + }, + { + name: 'Set Purpose', + value: 'setPurpose', + description: 'Sets the purpose for a conversation.', + }, + { + name: 'Set Topic', + value: 'setTopic', + description: 'Sets the topic for a conversation.', + }, + { + name: 'Unarchive', + value: 'unarchive', + description: 'Unarchives a conversation.', + }, ], default: 'create', description: 'The operation to perform.', @@ -31,12 +101,60 @@ export const channelOperations = [ export const channelFields = [ +/* -------------------------------------------------------------------------- */ +/* channel:archive */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + displayOptions: { + show: { + operation: [ + 'archive' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'The name of the channel to archive.', + }, +/* -------------------------------------------------------------------------- */ +/* channel:close */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + displayOptions: { + show: { + operation: [ + 'close' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'The name of the channel to close.', + }, /* -------------------------------------------------------------------------- */ /* channel:create */ /* -------------------------------------------------------------------------- */ { - displayName: 'Name', - name: 'channel', + displayName: 'Channel', + name: 'channelId', type: 'string', default: '', placeholder: 'Channel name', @@ -75,6 +193,7 @@ export const channelFields = [ name: 'isPrivate', type: 'boolean', default: false, + description: 'Create a private channel instead of a public one', }, { displayName: 'Users', @@ -84,6 +203,7 @@ export const channelFields = [ loadOptionsMethod: 'getUsers', }, default: [], + description: `Required for workspace apps. A list of between 1 and 30 human users that will be added to the newly-created conversation`, }, ] }, @@ -91,11 +211,13 @@ export const channelFields = [ /* channel:invite */ /* -------------------------------------------------------------------------- */ { - displayName: 'Channel ID', - name: 'channel', - type: 'string', + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, default: '', - placeholder: 'myChannel', displayOptions: { show: { operation: [ @@ -111,10 +233,9 @@ export const channelFields = [ }, { displayName: 'User ID', - name: 'username', + name: 'userId', type: 'string', default: '', - placeholder: 'frank', displayOptions: { show: { operation: [ @@ -131,11 +252,114 @@ export const channelFields = [ /* -------------------------------------------------------------------------- */ /* channel:get */ /* -------------------------------------------------------------------------- */ - + { + displayName: 'Channel', + name: 'channelId', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'get' + ], + resource: [ + 'channel', + ], + }, + }, + required: true, + description: 'Channel ID to learn more about', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'channel', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + displayName: 'Include Num of Members', + name: 'includeNumMembers', + type: 'boolean', + default: false, + }, + ] + }, /* -------------------------------------------------------------------------- */ -/* channel:delete */ +/* channel:kick */ /* -------------------------------------------------------------------------- */ - + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + default: '', + placeholder: 'Channel name', + displayOptions: { + show: { + operation: [ + 'kick' + ], + resource: [ + 'channel', + ], + }, + }, + required: true, + description: 'The name of the channel to create.', + }, + { + displayName: 'User ID', + name: 'userId', + type: 'string', + displayOptions: { + show: { + operation: [ + 'kick' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + }, +/* -------------------------------------------------------------------------- */ +/* channel:join */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + default: '', + placeholder: 'Channel name', + displayOptions: { + show: { + operation: [ + 'join' + ], + resource: [ + 'channel', + ], + }, + }, + required: true, + }, /* -------------------------------------------------------------------------- */ /* channel:getAll */ /* -------------------------------------------------------------------------- */ @@ -181,8 +405,8 @@ export const channelFields = [ description: 'How many results to return.', }, { - displayName: 'Options', - name: 'options', + displayName: 'Filters', + name: 'filters', type: 'collection', placeholder: 'Add Field', default: {}, @@ -198,12 +422,487 @@ export const channelFields = [ }, options: [ { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: '', - description: 'Fields to include separated by ,', + displayName: 'Exclude Archived', + name: 'excludeArchived', + type: 'boolean', + default: false, + description: 'Set to true to exclude archived channels from the list', + }, + { + displayName: 'Types', + name: 'types', + type: 'multiOptions', + options: [ + { + name: 'Public Channel', + value: 'public_channel' + }, + { + name: 'Private Channel', + value: 'private_channel' + }, + { + name: 'mpim', + value: 'mpim' + }, + { + name: 'im', + value: 'im' + }, + ], + default: ['public_channel'], + description: 'Mix and match channel types', }, ] }, +/* -------------------------------------------------------------------------- */ +/* channel:history */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + default: '', + placeholder: 'Channel name', + displayOptions: { + show: { + operation: [ + 'history' + ], + resource: [ + 'channel', + ], + }, + }, + required: true, + description: 'The name of the channel to create.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'channel', + ], + operation: [ + 'history', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'channel', + ], + operation: [ + 'history', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'channel', + ], + operation: [ + 'history', + ], + }, + }, + options: [ + { + displayName: 'Inclusive', + name: 'inclusive', + type: 'boolean', + default: false, + description: 'Include messages with latest or oldest timestamp in results only when either timestamp is specified.', + }, + { + displayName: 'Latest', + name: 'latest', + type: 'dateTime', + default: '', + description: 'End of time range of messages to include in results.', + }, + { + displayName: 'Oldest', + name: 'oldest', + type: 'dateTime', + default: '', + description: 'Start of time range of messages to include in results.', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* channel:leave */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + displayOptions: { + show: { + operation: [ + 'leave' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'The name of the channel to leave.', + }, +/* -------------------------------------------------------------------------- */ +/* channel:open */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'channel', + ], + operation: [ + 'open', + ], + }, + }, + options: [ + { + displayName: 'Channel ID', + name: 'channelId', + type: 'string', + default: '', + description: `Resume a conversation by supplying an im or mpim's ID. Or provide the users field instead`, + }, + { + displayName: 'Return IM', + name: 'returnIm', + type: 'boolean', + default: false, + description: 'Boolean, indicates you want the full IM channel definition in the response.', + }, + { + displayName: 'Users', + name: 'users', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getUsers', + }, + default: [], + description: `If only one user is included, this creates a 1:1 DM. The ordering of the users is preserved whenever a multi-person direct message is returned. Supply a channel when not supplying users.`, + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* channel:rename */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + displayOptions: { + show: { + operation: [ + 'rename' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'The name of the channel to rename.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + displayOptions: { + show: { + operation: [ + 'rename' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'New name for conversation.', + }, +/* -------------------------------------------------------------------------- */ +/* channel:replies */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + default: '', + placeholder: 'Channel name', + displayOptions: { + show: { + operation: [ + 'replies' + ], + resource: [ + 'channel', + ], + }, + }, + required: true, + description: 'The name of the channel to create.', + }, + { + displayName: 'TS', + name: 'ts', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'replies' + ], + resource: [ + 'channel', + ], + }, + }, + required: true, + description: `Unique identifier of a thread's parent message.`, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'channel', + ], + operation: [ + 'replies', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'channel', + ], + operation: [ + 'replies', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'channel', + ], + operation: [ + 'replies', + ], + }, + }, + options: [ + { + displayName: 'Inclusive', + name: 'inclusive', + type: 'boolean', + default: false, + description: 'Include messages with latest or oldest timestamp in results only when either timestamp is specified.', + }, + { + displayName: 'Latest', + name: 'latest', + type: 'string', + default: '', + description: 'End of time range of messages to include in results.', + }, + { + displayName: 'Oldest', + name: 'oldest', + type: 'string', + default: '', + description: 'Start of time range of messages to include in results.', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* channel:setPurpose */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + displayOptions: { + show: { + operation: [ + 'setPurpose' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'Conversation to set the purpose of', + }, + { + displayName: 'Purpose', + name: 'purpose', + type: 'string', + displayOptions: { + show: { + operation: [ + 'setPurpose' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'A new, specialer purpose', + }, +/* -------------------------------------------------------------------------- */ +/* channel:setTopic */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + displayOptions: { + show: { + operation: [ + 'setTopic' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'Conversation to set the topic of', + }, + { + displayName: 'Topic', + name: 'topic', + type: 'string', + displayOptions: { + show: { + operation: [ + 'setTopic' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'The new topic string. Does not support formatting or linkification.', + }, +/* -------------------------------------------------------------------------- */ +/* channel:unarchive */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + displayOptions: { + show: { + operation: [ + 'unarchive' + ], + resource: [ + 'channel', + ], + }, + }, + default: '', + required: true, + description: 'The ID of the channel to unarchive.', + }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Slack/ConversationDescription.ts b/packages/nodes-base/nodes/Slack/ConversationDescription.ts deleted file mode 100644 index 62628cc654..0000000000 --- a/packages/nodes-base/nodes/Slack/ConversationDescription.ts +++ /dev/null @@ -1,846 +0,0 @@ -import { INodeProperties } from 'n8n-workflow'; - -export const conversationOperations = [ - { - displayName: 'Operation', - name: 'operation', - type: 'options', - displayOptions: { - show: { - authentication: [ - 'accessToken', - ], - resource: [ - 'conversation', - ], - }, - }, - options: [ - { - name: 'Post', - value: 'post', - description: 'Post a conversation into a channel', - }, - ], - default: 'post', - description: 'The operation to perform.', - }, -] as INodeProperties[]; - -export const conversationFields = [ - -/* -------------------------------------------------------------------------- */ -/* conversation:post */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Channel', - name: 'channel', - type: 'string', - default: '', - placeholder: 'Channel name', - displayOptions: { - show: { - operation: [ - 'post' - ], - resource: [ - 'conversation', - ], - }, - }, - required: true, - description: 'The channel to send the conversation to.', - }, - { - displayName: 'Text', - name: 'text', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - displayOptions: { - show: { - operation: [ - 'post' - ], - resource: [ - 'conversation', - ], - }, - }, - description: 'The text to send.', - }, - { - displayName: 'As User', - name: 'as_user', - type: 'boolean', - default: false, - displayOptions: { - show: { - operation: [ - 'post' - ], - resource: [ - 'conversation', - ], - }, - }, - description: 'Post the conversation as authenticated user instead of bot.', - }, - { - displayName: 'User Name', - name: 'username', - type: 'string', - default: '', - displayOptions: { - show: { - as_user: [ - false - ], - operation: [ - 'post' - ], - resource: [ - 'conversation', - ], - }, - }, - description: 'Set the bot\'s user name.', - }, - { - displayName: 'Attachments', - name: 'attachments', - type: 'collection', - typeOptions: { - multipleValues: true, - multipleValueButtonText: 'Add attachment', - }, - displayOptions: { - show: { - operation: [ - 'post' - ], - resource: [ - 'conversation', - ], - }, - }, - default: {}, // TODO: Remove comment: has to make default array for the main property, check where that happens in UI - description: 'The attachment to add', - placeholder: 'Add attachment item', - options: [ - { - displayName: 'Fallback Text', - name: 'fallback', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Required plain-text summary of the attachment.', - }, - { - displayName: 'Text', - name: 'text', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Text to send.', - }, - { - displayName: 'Title', - name: 'title', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Title of the conversation.', - }, - { - displayName: 'Title Link', - name: 'title_link', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Link of the title.', - }, - { - displayName: 'Color', - name: 'color', - type: 'color', - default: '#ff0000', - description: 'Color of the line left of text.', - }, - { - displayName: 'Pretext', - name: 'pretext', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Text which appears before the conversation block.', - }, - { - displayName: 'Author Name', - name: 'author_name', - type: 'string', - default: '', - description: 'Name that should appear.', - }, - { - displayName: 'Author Link', - name: 'author_link', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Link for the author.', - }, - { - displayName: 'Author Icon', - name: 'author_icon', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Icon which should appear for the user.', - }, - { - displayName: 'Image URL', - name: 'image_url', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'URL of image.', - }, - { - displayName: 'Thumbnail URL', - name: 'thumb_url', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'URL of thumbnail.', - }, - { - displayName: 'Footer', - name: 'footer', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Text of footer to add.', - }, - { - displayName: 'Footer Icon', - name: 'footer_icon', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: 'Icon which should appear next to footer.', - }, - { - displayName: 'Timestamp', - name: 'ts', - type: 'dateTime', - default: '', - description: 'Time conversation relates to.', - }, - { - displayName: 'Fields', - name: 'fields', - placeholder: 'Add Fields', - description: 'Fields to add to conversation.', - type: 'fixedCollection', - typeOptions: { - multipleValues: true, - }, - default: {}, - options: [ - { - name: 'item', - displayName: 'Item', - values: [ - { - displayName: 'Title', - name: 'title', - type: 'string', - default: '', - description: 'Title of the item.', - }, - { - displayName: 'Value', - name: 'value', - type: 'string', - default: '', - description: 'Value of the item.', - }, - { - displayName: 'Short', - name: 'short', - type: 'boolean', - default: true, - description: 'If items can be displayed next to each other.', - }, - ] - }, - ], - } - ], - }, - { - displayName: 'Other Options', - name: 'otherOptions', - type: 'collection', - displayOptions: { - show: { - operation: [ - 'post' - ], - resource: [ - 'conversation', - ], - }, - }, - default: {}, - description: 'Other options to set', - placeholder: 'Add options', - options: [ - { - displayName: 'Icon Emoji', - name: 'icon_emoji', - type: 'string', - displayOptions: { - show: { - '/as_user': [ - false - ], - '/operation': [ - 'post' - ], - '/resource': [ - 'conversation', - ], - }, - }, - default: '', - description: 'Emoji to use as the icon for this conversation. Overrides icon_url.', - }, - { - displayName: 'Icon URL', - name: 'icon_url', - type: 'string', - displayOptions: { - show: { - '/as_user': [ - false - ], - '/operation': [ - 'post' - ], - '/resource': [ - 'conversation', - ], - }, - }, - default: '', - description: 'URL to an image to use as the icon for this conversation.', - }, - { - displayName: 'Make Reply', - name: 'thread_ts', - type: 'string', - default: '', - description: 'Provide another conversation\'s ts value to make this conversation a reply.', - }, - { - displayName: 'Unfurl Links', - name: 'unfurl_links', - type: 'boolean', - default: false, - description: 'Pass true to enable unfurling of primarily text-based content.', - }, - { - displayName: 'Unfurl Media', - name: 'unfurl_media', - type: 'boolean', - default: true, - description: 'Pass false to disable unfurling of media content.', - }, - { - displayName: 'Markdown', - name: 'mrkdwn', - type: 'boolean', - default: true, - description: 'Use Slack Markdown parsing.', - }, - { - displayName: 'Reply Broadcast', - name: 'reply_broadcast', - type: 'boolean', - default: false, - description: 'Used in conjunction with thread_ts and indicates whether reply should be made visible to everyone in the channel or conversation.', - }, - { - displayName: 'Link Names', - name: 'link_names', - type: 'boolean', - default: false, - description: 'Find and link channel names and usernames.', - }, - ], - }, -/* ----------------------------------------------------------------------- */ -/* conversation:update */ -/* ----------------------------------------------------------------------- */ - { - displayName: 'conversation ID', - name: 'conversationId', - type: 'string', - required: true, - default: '', - displayOptions: { - show: { - resource: [ - 'conversation', - ], - operation: [ - 'update', - ] - }, - }, - description: 'Id of conversation that needs to be fetched', - }, - { - displayName: 'Update Fields', - name: 'updateFields', - type: 'collection', - placeholder: 'Add Field', - default: {}, - displayOptions: { - show: { - resource: [ - 'conversation', - ], - operation: [ - 'update', - ], - }, - }, - options: [ - { - displayName: 'Activity Date', - name: 'activityDate', - type: 'dateTime', - default: '', - description: `Represents the due date of the conversation.
- This field has a timestamp that is always set to midnight
- in the Coordinated Universal Time (UTC) time zone.`, - }, - { - displayName: 'Call Disposition', - name: 'callDisposition', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: `Represents the result of a given call, for example, “we'll call back,” or “call
- unsuccessful.” Limit is 255 characters. Not subject to field-level security, available for any user
- in an organization with Salesforce CRM Call Center.`, - }, - { - displayName: 'Call Duration In Seconds', - name: 'callDurationInSeconds', - type: 'number', - default: '', - description: `Duration of the call in seconds. Not subject to field-level security,
- available for any user in an organization with Salesforce CRM Call Cente`, - }, - { - displayName: 'Call Object', - name: 'callObject', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: `Name of a call center. Limit is 255 characters.
- Not subject to field-level security, available for any user in an
- organization with Salesforce CRM Call Center.`, - }, - { - displayName: 'Call Type', - name: 'callType', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getconversationCallTypes', - }, - description: 'The type of call being answered: Inbound, Internal, or Outbound.', - }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - typeOptions: { - alwaysOpenEditWindow: true, - }, - description: 'Contains a text description of the conversation.', - }, - { - displayName: 'Is ReminderSet', - name: 'isReminderSet', - type: 'boolean', - default: false, - description: 'Indicates whether a popup reminder has been set for the conversation (true) or not (false).', - }, - { - displayName: 'Owner', - name: 'owner', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getUsers', - }, - default: '', - description: 'ID of the User who owns the record.', - }, - { - displayName: 'Priority', - name: 'priority', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getconversationPriorities', - }, - description: `Indicates the importance or urgency of a conversation, such as high or low.`, - }, - { - displayName: 'Status', - name: 'status', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getconversationStatuses', - }, - description: 'The current status of the conversation, such as In Progress or Completed.', - }, - { - displayName: 'Subject', - name: 'subject', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getconversationSubjects', - }, - description: 'The subject line of the conversation, such as “Call” or “Send Quote.” Limit: 255 characters.', - }, - { - displayName: 'Recurrence Day Of Month', - name: 'recurrenceDayOfMonth', - type: 'number', - default: '', - description: 'The day of the month in which the conversation repeats.', - }, - { - displayName: 'Recurrence Day Of Week Mask', - name: 'recurrenceDayOfWeekMask', - type: 'number', - default: '', - description: `The day or days of the week on which the conversation repeats.
- This field contains a bitmask. The values are as follows: Sunday = 1 Monday = 2
- Tuesday = 4 Wednesday = 8 Thursday = 16 Friday = 32 Saturday = 64
- Multiple days are represented as the sum of their numerical values.
- For example, Tuesday and Thursday = 4 + 16 = 20.`, - }, - { - displayName: 'Recurrence End Date Only', - name: 'recurrenceEndDateOnly', - type: 'dateTime', - default: '', - description: `The last date on which the conversation repeats. This field has a timestamp that
- is always set to midnight in the Coordinated Universal Time (UTC) time zone.`, - }, - { - displayName: 'Recurrence Instance', - name: 'recurrenceInstance', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getconversationRecurrenceInstances', - }, - default: '', - description: `The frequency of the recurring conversation. For example, “2nd” or “3rd.”`, - }, - { - displayName: 'Recurrence Interval', - name: 'recurrenceInterval', - type: 'number', - default: '', - description: 'The interval between recurring conversations.', - }, - { - displayName: 'Recurrence Month Of Year', - name: 'recurrenceMonthOfYear', - type: 'options', - options: [ - { - name: 'January', - value: 'January' - }, - { - name: 'February', - value: 'February' - }, - { - name: 'March', - value: 'March' - }, - { - name: 'April', - value: 'April' - }, - { - name: 'May', - value: 'May' - }, - { - name: 'June', - value: 'June' - }, - { - name: 'July', - value: 'July' - }, - { - name: 'August', - value: 'August' - }, - { - name: 'September', - value: 'September' - }, - { - name: 'October', - value: 'October' - }, - { - name: 'November', - value: 'November' - }, - { - name: 'December', - value: 'December' - } - ], - default: '', - description: 'The month of the year in which the conversation repeats.', - }, - { - displayName: 'Recurrence Start Date Only', - name: 'recurrenceEndDateOnly', - type: 'dateTime', - default: '', - description: `The date when the recurring conversation begins.
- Must be a date and time before RecurrenceEndDateOnly.`, - }, - { - displayName: 'Recurrence Regenerated Type', - name: 'recurrenceRegeneratedType', - type: 'options', - default: '', - options: [ - { - name: 'After due date', - value: 'RecurrenceRegenerateAfterDueDate' - }, - { - name: 'After date completed', - value: 'RecurrenceRegenerateAfterToday' - }, - { - name: '(conversation Closed)', - value: 'RecurrenceRegenerated' - } - ], - description: `Represents what triggers a repeating conversation to repeat.
- Add this field to a page layout together with the RecurrenceInterval field,
- which determines the number of days between the triggering date (due date or close date)
- and the due date of the next repeating conversation in the series.Label is Repeat This conversation.`, - }, - { - displayName: 'Recurrence Type', - name: 'recurrenceType', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getconversationRecurrenceTypes' - }, - description: 'Website for the conversation.', - }, - { - displayName: 'Recurrence TimeZone SidKey', - name: 'recurrenceTimeZoneSidKey', - type: 'string', - default: '', - description: `The time zone associated with the recurring conversation.
- For example, “UTC-8:00” for Pacific Standard Time.`, - }, - { - displayName: 'Reminder Date Time', - name: 'reminderDateTime', - type: 'dateTime', - default: '', - description: `Represents the time when the reminder is scheduled to fire,
- if IsReminderSet is set to true. If IsReminderSet is set to false, then the
- user may have deselected the reminder checkbox in the Salesforce user interface,
- or the reminder has already fired at the time indicated by the value.`, - }, - { - displayName: 'What Id', - name: 'whatId', - type: 'string', - default: '', - description: `The WhatId represents nonhuman objects such as accounts, opportunities,
- campaigns, cases, or custom objects. WhatIds are polymorphic. Polymorphic means a
- WhatId is equivalent to the ID of a related object.`, - }, - { - displayName: 'Who Id', - name: 'whoId', - type: 'string', - default: '', - description: `The WhoId represents a human such as a lead or a contact.
- WhoIds are polymorphic. Polymorphic means a WhoId is equivalent to a contact’s ID or a lead’s ID.`, - }, - ] - }, - -/* -------------------------------------------------------------------------- */ -/* conversation:get */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'conversation ID', - name: 'conversationId', - type: 'string', - required: true, - default: '', - displayOptions: { - show: { - resource: [ - 'conversation', - ], - operation: [ - 'get', - ] - }, - }, - description: 'Id of conversation that needs to be fetched', - }, -/* -------------------------------------------------------------------------- */ -/* conversation:delete */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'conversation ID', - name: 'conversationId', - type: 'string', - required: true, - default: '', - displayOptions: { - show: { - resource: [ - 'conversation', - ], - operation: [ - 'delete', - ] - }, - }, - description: 'Id of conversation that needs to be fetched', - }, -/* -------------------------------------------------------------------------- */ -/* conversation:getAll */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - resource: [ - 'conversation', - ], - operation: [ - 'getAll', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - resource: [ - 'conversation', - ], - operation: [ - 'getAll', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 100, - }, - default: 50, - description: 'How many results to return.', - }, - { - displayName: 'Options', - name: 'options', - type: 'collection', - placeholder: 'Add Field', - default: {}, - displayOptions: { - show: { - resource: [ - 'conversation', - ], - operation: [ - 'getAll', - ], - }, - }, - options: [ - { - displayName: 'Fields', - name: 'fields', - type: 'string', - default: '', - description: 'Fields to include separated by ,', - }, - ] - }, -] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Slack/FileDescription.ts b/packages/nodes-base/nodes/Slack/FileDescription.ts new file mode 100644 index 0000000000..fe1bbe8f8e --- /dev/null +++ b/packages/nodes-base/nodes/Slack/FileDescription.ts @@ -0,0 +1,322 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const fileOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'file', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Get a file info', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get & filters team files.', + }, + { + name: 'Upload', + value: 'upload', + description: 'Create or upload an existing file.', + }, + ], + default: 'upload', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const fileFields = [ + +/* -------------------------------------------------------------------------- */ +/* file:upload */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Binary Data', + name: 'binaryData', + type: 'boolean', + default: false, + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + }, + }, + description: 'If the data to upload should be taken from binary field.', + }, + { + displayName: 'File Content', + name: 'fileContent', + type: 'string', + default: '', + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + binaryData: [ + false + ], + }, + + }, + placeholder: '', + description: 'The text content of the file to upload.', + }, + { + displayName: 'Binary Property', + name: 'binaryPropertyName', + type: 'string', + default: 'data', + required: true, + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + binaryData: [ + true + ], + }, + + }, + placeholder: '', + description: 'Name of the binary property which contains
the data for the file to be uploaded.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'upload' + ], + resource: [ + 'file', + ], + }, + }, + default: {}, + description: 'Other options to set', + placeholder: 'Add options', + options: [ + { + displayName: 'Channels', + name: 'channelIds', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + default: [], + description: 'The channels to send the file to.', + }, + { + displayName: 'File Name', + name: 'fileName', + type: 'string', + default: '', + description: 'Filename of file.', + }, + { + displayName: 'Initial Comment', + name: 'initialComment', + type: 'string', + default: '', + description: 'The message text introducing the file in specified channels.', + }, + { + displayName: 'Thread TS', + name: 'threadTs', + type: 'string', + default: '', + description: `Provide another message's ts value to upload this file as a reply. Never use a reply's ts value; use its parent instead.`, + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + description: 'Title of file.', + }, + ], + }, +/* ----------------------------------------------------------------------- */ +/* file:getAll */ +/* ----------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'file', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'file', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'getAll' + ], + resource: [ + 'file', + ], + }, + }, + default: {}, + placeholder: 'Add Field', + options: [ + { + displayName: 'Channel', + name: 'channelId', + type: 'options', + default: '', + typeOptions: { + loadOptionsMethod: 'getChannels', + }, + description: 'Channel containing the file to be listed.', + }, + { + displayName: 'Show Files Hidden By Limit', + name: 'showFilesHidden', + type: 'boolean', + default: false, + description: 'Show truncated file info for files hidden due to being too old, and the team who owns the file being over the file limit.', + }, + { + displayName: 'TS From', + name: 'tsFrom', + type: 'string', + default: '', + description: 'Filter files created after this timestamp (inclusive).', + }, + { + displayName: 'TS To', + name: 'tsTo', + type: 'string', + default: '', + description: 'Filter files created before this timestamp (inclusive).', + }, + { + displayName: 'Types', + name: 'types', + type: 'multiOptions', + options: [ + { + name: 'All', + value: 'all', + }, + { + name: 'Spaces', + value: 'spaces', + }, + { + name: 'Snippets', + value: 'snippets', + }, + { + name: 'Images', + value: 'images', + }, + { + name: 'Google Docs', + value: 'gdocs', + }, + { + name: 'Zips', + value: 'zips', + }, + { + name: 'pdfs', + value: 'pdfs', + }, + ], + default: ['all'], + description: 'Filter files by type', + }, + { + displayName: 'User', + name: 'userId', + type: 'options', + default: '', + typeOptions: { + loadOptionsMethod: 'getUsers', + }, + description: 'Filter files created by a single user.', + }, + ], + }, +/* ----------------------------------------------------------------------- */ +/* file:get */ +/* ----------------------------------------------------------------------- */ + { + displayName: 'File ID', + name: 'fileId', + type: 'string', + displayOptions: { + show: { + resource: [ + 'file', + ], + operation: [ + 'get', + ], + }, + }, + default: '', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Slack/GenericFunctions.ts b/packages/nodes-base/nodes/Slack/GenericFunctions.ts index 57b5b00c11..b73de1cac5 100644 --- a/packages/nodes-base/nodes/Slack/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Slack/GenericFunctions.ts @@ -12,11 +12,11 @@ import { } from 'n8n-workflow'; import * as _ from 'lodash'; -export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: object = {}, query: object = {}): Promise { // tslint:disable-line:no-any +export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: object = {}, query: object = {}, headers: {} | undefined = undefined, option: {} = {}): Promise { // tslint:disable-line:no-any const authenticationMethod = this.getNodeParameter('authentication', 0, 'accessToken') as string; - const options: OptionsWithUri = { + let options: OptionsWithUri = { method, - headers: { + headers: headers || { 'Content-Type': 'application/json; charset=utf-8' }, body, @@ -24,6 +24,7 @@ export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFu uri: `https://slack.com/api${resource}`, json: true }; + options = Object.assign({}, options, option); if (Object.keys(body).length === 0) { delete options.body; } @@ -62,15 +63,23 @@ export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFu export async function salckApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; let responseData; + query.page = 1; + query.count = 100; do { responseData = await slackApiRequest.call(this, method, endpoint, body, query); query.cursor = encodeURIComponent(_.get(responseData, 'response_metadata.next_cursor')); + query.page++; returnData.push.apply(returnData, responseData[propertyName]); } while ( - responseData.response_metadata !== undefined && + (responseData.response_metadata !== undefined && responseData.response_metadata.mext_cursor !== undefined && responseData.response_metadata.next_cursor !== "" && - responseData.response_metadata.next_cursor !== null + responseData.response_metadata.next_cursor !== null) || + (responseData.paging !== undefined && + responseData.paging.pages !== undefined && + responseData.paging.page !== undefined && + responseData.paging.page < responseData.paging.pages + ) ); return returnData; diff --git a/packages/nodes-base/nodes/Slack/MessageDescription.ts b/packages/nodes-base/nodes/Slack/MessageDescription.ts index 7f9dff3ac3..8c582503e5 100644 --- a/packages/nodes-base/nodes/Slack/MessageDescription.ts +++ b/packages/nodes-base/nodes/Slack/MessageDescription.ts @@ -18,6 +18,11 @@ export const messageOperations = [ value: 'post', description: 'Post a message into a channel', }, + { + name: 'Update', + value: 'update', + description: 'Updates a message.', + }, ], default: 'post', description: 'The operation to perform.', @@ -406,8 +411,8 @@ export const messageFields = [ /* message:update */ /* ----------------------------------------------------------------------- */ { - displayName: 'message ID', - name: 'messageId', + displayName: 'Channel ID', + name: 'channelId', type: 'string', required: true, default: '', @@ -421,7 +426,60 @@ export const messageFields = [ ] }, }, - description: 'Id of message that needs to be fetched', + description: 'Channel containing the message to be updated.', + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'message', + ], + operation: [ + 'update', + ] + }, + }, + description: `New text for the message, using the default formatting rules. It's not required when presenting attachments.`, + }, + { + displayName: 'TS', + name: 'ts', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'message', + ], + operation: [ + 'update', + ] + }, + }, + description: `Timestamp of the message to be updated.`, + }, + { + displayName: 'As User', + name: 'as_user', + type: 'boolean', + default: false, + displayOptions: { + show: { + operation: [ + 'update' + ], + resource: [ + 'message', + ], + }, + }, + description: 'Pass true to update the message as the authed user. Bot users in this context are considered authed users.', }, { displayName: 'Update Fields', @@ -441,403 +499,228 @@ export const messageFields = [ }, options: [ { - displayName: 'Activity Date', - name: 'activityDate', - type: 'dateTime', - default: '', - description: `Represents the due date of the message.
- This field has a timestamp that is always set to midnight
- in the Coordinated Universal Time (UTC) time zone.`, - }, - { - displayName: 'Call Disposition', - name: 'callDisposition', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: `Represents the result of a given call, for example, “we'll call back,” or “call
- unsuccessful.” Limit is 255 characters. Not subject to field-level security, available for any user
- in an organization with Salesforce CRM Call Center.`, - }, - { - displayName: 'Call Duration In Seconds', - name: 'callDurationInSeconds', - type: 'number', - default: '', - description: `Duration of the call in seconds. Not subject to field-level security,
- available for any user in an organization with Salesforce CRM Call Cente`, - }, - { - displayName: 'Call Object', - name: 'callObject', - type: 'string', - typeOptions: { - alwaysOpenEditWindow: true, - }, - default: '', - description: `Name of a call center. Limit is 255 characters.
- Not subject to field-level security, available for any user in an
- organization with Salesforce CRM Call Center.`, - }, - { - displayName: 'Call Type', - name: 'callType', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getmessageCallTypes', - }, - description: 'The type of call being answered: Inbound, Internal, or Outbound.', - }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - typeOptions: { - alwaysOpenEditWindow: true, - }, - description: 'Contains a text description of the message.', - }, - { - displayName: 'Is ReminderSet', - name: 'isReminderSet', + displayName: 'Link Names', + name: 'link_names', type: 'boolean', default: false, - description: 'Indicates whether a popup reminder has been set for the message (true) or not (false).', + description: 'Find and link channel names and usernames.', }, { - displayName: 'Owner', - name: 'owner', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getUsers', - }, - default: '', - description: 'ID of the User who owns the record.', - }, - { - displayName: 'Priority', - name: 'priority', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getmessagePriorities', - }, - description: `Indicates the importance or urgency of a message, such as high or low.`, - }, - { - displayName: 'Status', - name: 'status', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getmessageStatuses', - }, - description: 'The current status of the message, such as In Progress or Completed.', - }, - { - displayName: 'Subject', - name: 'subject', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getmessageSubjects', - }, - description: 'The subject line of the message, such as “Call” or “Send Quote.” Limit: 255 characters.', - }, - { - displayName: 'Recurrence Day Of Month', - name: 'recurrenceDayOfMonth', - type: 'number', - default: '', - description: 'The day of the month in which the message repeats.', - }, - { - displayName: 'Recurrence Day Of Week Mask', - name: 'recurrenceDayOfWeekMask', - type: 'number', - default: '', - description: `The day or days of the week on which the message repeats.
- This field contains a bitmask. The values are as follows: Sunday = 1 Monday = 2
- Tuesday = 4 Wednesday = 8 Thursday = 16 Friday = 32 Saturday = 64
- Multiple days are represented as the sum of their numerical values.
- For example, Tuesday and Thursday = 4 + 16 = 20.`, - }, - { - displayName: 'Recurrence End Date Only', - name: 'recurrenceEndDateOnly', - type: 'dateTime', - default: '', - description: `The last date on which the message repeats. This field has a timestamp that
- is always set to midnight in the Coordinated Universal Time (UTC) time zone.`, - }, - { - displayName: 'Recurrence Instance', - name: 'recurrenceInstance', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getmessageRecurrenceInstances', - }, - default: '', - description: `The frequency of the recurring message. For example, “2nd” or “3rd.”`, - }, - { - displayName: 'Recurrence Interval', - name: 'recurrenceInterval', - type: 'number', - default: '', - description: 'The interval between recurring messages.', - }, - { - displayName: 'Recurrence Month Of Year', - name: 'recurrenceMonthOfYear', + displayName: 'Parse', + name: 'parse', type: 'options', options: [ { - name: 'January', - value: 'January' + name: 'Client', + value: 'client', }, { - name: 'February', - value: 'February' + name: 'Full', + value: 'full', }, { - name: 'March', - value: 'March' + name: 'None', + value: 'none', }, - { - name: 'April', - value: 'April' - }, - { - name: 'May', - value: 'May' - }, - { - name: 'June', - value: 'June' - }, - { - name: 'July', - value: 'July' - }, - { - name: 'August', - value: 'August' - }, - { - name: 'September', - value: 'September' - }, - { - name: 'October', - value: 'October' - }, - { - name: 'November', - value: 'November' - }, - { - name: 'December', - value: 'December' - } ], - default: '', - description: 'The month of the year in which the message repeats.', + default: 'client', + description: 'Change how messages are treated', }, - { - displayName: 'Recurrence Start Date Only', - name: 'recurrenceEndDateOnly', - type: 'dateTime', - default: '', - description: `The date when the recurring message begins.
- Must be a date and time before RecurrenceEndDateOnly.`, - }, - { - displayName: 'Recurrence Regenerated Type', - name: 'recurrenceRegeneratedType', - type: 'options', - default: '', - options: [ - { - name: 'After due date', - value: 'RecurrenceRegenerateAfterDueDate' - }, - { - name: 'After date completed', - value: 'RecurrenceRegenerateAfterToday' - }, - { - name: '(message Closed)', - value: 'RecurrenceRegenerated' - } - ], - description: `Represents what triggers a repeating message to repeat.
- Add this field to a page layout together with the RecurrenceInterval field,
- which determines the number of days between the triggering date (due date or close date)
- and the due date of the next repeating message in the series.Label is Repeat This message.`, - }, - { - displayName: 'Recurrence Type', - name: 'recurrenceType', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getmessageRecurrenceTypes' - }, - description: 'Website for the message.', - }, - { - displayName: 'Recurrence TimeZone SidKey', - name: 'recurrenceTimeZoneSidKey', - type: 'string', - default: '', - description: `The time zone associated with the recurring message.
- For example, “UTC-8:00” for Pacific Standard Time.`, - }, - { - displayName: 'Reminder Date Time', - name: 'reminderDateTime', - type: 'dateTime', - default: '', - description: `Represents the time when the reminder is scheduled to fire,
- if IsReminderSet is set to true. If IsReminderSet is set to false, then the
- user may have deselected the reminder checkbox in the Salesforce user interface,
- or the reminder has already fired at the time indicated by the value.`, - }, - { - displayName: 'What Id', - name: 'whatId', - type: 'string', - default: '', - description: `The WhatId represents nonhuman objects such as accounts, opportunities,
- campaigns, cases, or custom objects. WhatIds are polymorphic. Polymorphic means a
- WhatId is equivalent to the ID of a related object.`, - }, - { - displayName: 'Who Id', - name: 'whoId', - type: 'string', - default: '', - description: `The WhoId represents a human such as a lead or a contact.
- WhoIds are polymorphic. Polymorphic means a WhoId is equivalent to a contact’s ID or a lead’s ID.`, - }, - ] - }, - -/* -------------------------------------------------------------------------- */ -/* message:get */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'message ID', - name: 'messageId', - type: 'string', - required: true, - default: '', - displayOptions: { - show: { - resource: [ - 'message', - ], - operation: [ - 'get', - ] - }, - }, - description: 'Id of message that needs to be fetched', - }, -/* -------------------------------------------------------------------------- */ -/* message:delete */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'message ID', - name: 'messageId', - type: 'string', - required: true, - default: '', - displayOptions: { - show: { - resource: [ - 'message', - ], - operation: [ - 'delete', - ] - }, - }, - description: 'Id of message that needs to be fetched', - }, -/* -------------------------------------------------------------------------- */ -/* message:getAll */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - resource: [ - 'message', - ], - operation: [ - 'getAll', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', + ], }, { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - resource: [ - 'message', - ], - operation: [ - 'getAll', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 100, - }, - default: 50, - description: 'How many results to return.', - }, - { - displayName: 'Options', - name: 'options', + displayName: 'Attachments', + name: 'attachments', type: 'collection', - placeholder: 'Add Field', - default: {}, + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add attachment', + }, displayOptions: { show: { + operation: [ + 'update' + ], resource: [ 'message', ], - operation: [ - 'getAll', - ], }, }, + default: {}, // TODO: Remove comment: has to make default array for the main property, check where that happens in UI + description: 'The attachment to add', + placeholder: 'Add attachment item', options: [ + { + displayName: 'Fallback Text', + name: 'fallback', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Required plain-text summary of the attachment.', + }, + { + displayName: 'Text', + name: 'text', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Text to send.', + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Title of the message.', + }, + { + displayName: 'Title Link', + name: 'title_link', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Link of the title.', + }, + { + displayName: 'Color', + name: 'color', + type: 'color', + default: '#ff0000', + description: 'Color of the line left of text.', + }, + { + displayName: 'Pretext', + name: 'pretext', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Text which appears before the message block.', + }, + { + displayName: 'Author Name', + name: 'author_name', + type: 'string', + default: '', + description: 'Name that should appear.', + }, + { + displayName: 'Author Link', + name: 'author_link', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Link for the author.', + }, + { + displayName: 'Author Icon', + name: 'author_icon', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Icon which should appear for the user.', + }, + { + displayName: 'Image URL', + name: 'image_url', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'URL of image.', + }, + { + displayName: 'Thumbnail URL', + name: 'thumb_url', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'URL of thumbnail.', + }, + { + displayName: 'Footer', + name: 'footer', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Text of footer to add.', + }, + { + displayName: 'Footer Icon', + name: 'footer_icon', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Icon which should appear next to footer.', + }, + { + displayName: 'Timestamp', + name: 'ts', + type: 'dateTime', + default: '', + description: 'Time message relates to.', + }, { displayName: 'Fields', name: 'fields', - type: 'string', - default: '', - description: 'Fields to include separated by ,', - }, - ] + placeholder: 'Add Fields', + description: 'Fields to add to message.', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'item', + displayName: 'Item', + values: [ + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + description: 'Title of the item.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Value of the item.', + }, + { + displayName: 'Short', + name: 'short', + type: 'boolean', + default: true, + description: 'If items can be displayed next to each other.', + }, + ] + }, + ], + } + ], }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index c3b074c1e1..1afeb4ebd0 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -1,5 +1,6 @@ import { IExecuteFunctions, + BINARY_ENCODING, } from 'n8n-core'; import { IDataObject, @@ -18,9 +19,13 @@ import { messageFields, } from './MessageDescription'; import { - conversationOperations, - conversationFields, -} from './ConversationDescription'; + starOperations, + starFields, +} from './StarDescription'; +import { + fileOperations, + fileFields, +} from './FileDescription'; import { slackApiRequest, salckApiRequestAllItems, @@ -37,7 +42,7 @@ export class Slack implements INodeType { group: ['output'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - description: 'Sends data to Slack', + description: 'Consume Slack API', defaults: { name: 'Slack', color: '#BB2244', @@ -66,7 +71,7 @@ export class Slack implements INodeType { ], }, }, - } + }, ], properties: [ { @@ -95,10 +100,18 @@ export class Slack implements INodeType { name: 'Channel', value: 'channel', }, + { + name: 'File', + value: 'file', + }, { name: 'Message', value: 'message', }, + { + name: 'Star', + value: 'star', + }, ], default: 'message', description: 'The resource to operate on.', @@ -107,6 +120,10 @@ export class Slack implements INodeType { ...channelFields, ...messageOperations, ...messageFields, + ...starOperations, + ...starFields, + ...fileOperations, + ...fileFields, ], }; @@ -125,7 +142,21 @@ export class Slack implements INodeType { value: userId, }); } - console.log(users) + return returnData; + }, + // Get all the users to display them to user so that he can + // select them easily + async getChannels(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const channels = await salckApiRequestAllItems.call(this, 'channels', 'GET', '/conversations.list'); + for (const channel of channels) { + const channelName = channel.name; + const channelId = channel.id; + returnData.push({ + name: channelName, + value: channelId, + }); + } return returnData; }, } @@ -142,25 +173,208 @@ export class Slack implements INodeType { const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; if (resource === 'channel') { + //https://api.slack.com/methods/conversations.archive + if (operation === 'archive') { + const channel = this.getNodeParameter('channelId', i) as string; + const body: IDataObject = { + channel, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.archive', body, qs); + } + //https://api.slack.com/methods/conversations.close + if (operation === 'close') { + const channel = this.getNodeParameter('channelId', i) as string; + const body: IDataObject = { + channel, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.close', body, qs); + } //https://api.slack.com/methods/conversations.create if (operation === 'create') { - const channel = this.getNodeParameter('channel', i) as string; + const channel = this.getNodeParameter('channelId', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; const body: IDataObject = { name: channel, }; - responseData = await slackApiRequest.call(this, 'POST', '/channels.create', body, qs); + if (additionalFields.isPrivate) { + body.is_private = additionalFields.isPrivate as boolean; + } + if (additionalFields.users) { + body.user_ids = (additionalFields.users as string[]).join(','); + } + responseData = await slackApiRequest.call(this, 'POST', '/conversations.create', body, qs); } - if (operation === 'invite') { - const channel = this.getNodeParameter('channel', i) as string; - const user = this.getNodeParameter('username', i) as string; + //https://api.slack.com/methods/conversations.kick + if (operation === 'kick') { + const channel = this.getNodeParameter('channelId', i) as string; + const userId = this.getNodeParameter('userId', i) as string; + const body: IDataObject = { + name: channel, + user: userId, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.kick', body, qs); + } + //https://api.slack.com/methods/conversations.join + if (operation === 'join') { + const channel = this.getNodeParameter('channelId', i) as string; const body: IDataObject = { channel, - user, }; - responseData = await slackApiRequest.call(this, 'POST', '/channels.invite', body, qs); + responseData = await slackApiRequest.call(this, 'POST', '/conversations.join', body, qs); + responseData = responseData.channel; + } + //https://api.slack.com/methods/conversations.info + if (operation === 'get') { + const channel = this.getNodeParameter('channelId', i) as string; + qs.channel = channel, + responseData = await slackApiRequest.call(this, 'POST', '/conversations.info', {}, qs); + responseData = responseData.channel; + } + //https://api.slack.com/methods/conversations.list + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + if (filters.types) { + qs.types = (filters.types as string[]).join(','); + } + if (filters.excludeArchived) { + qs.exclude_archived = filters.excludeArchived as boolean; + } + if (returnAll === true) { + responseData = await salckApiRequestAllItems.call(this, 'channels', 'GET', '/conversations.list', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await slackApiRequest.call(this, 'GET', '/conversations.list', {}, qs); + responseData = responseData.channels; + } + } + //https://api.slack.com/methods/conversations.history + if (operation === 'history') { + const channel = this.getNodeParameter('channelId', i) as string; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + qs.channel = channel; + if (filters.inclusive) { + qs.inclusive = filters.inclusive as boolean; + } + if (filters.latest) { + qs.latest = filters.latest as string; + } + if (filters.oldest) { + qs.oldest = filters.oldest as string; + } + if (returnAll === true) { + responseData = await salckApiRequestAllItems.call(this, 'messages', 'GET', '/conversations.history', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await slackApiRequest.call(this, 'GET', '/conversations.history', {}, qs); + responseData = responseData.messages; + } + } + //https://api.slack.com/methods/conversations.invite + if (operation === 'invite') { + const channel = this.getNodeParameter('channelId', i) as string; + const userId = this.getNodeParameter('userId', i) as string; + const body: IDataObject = { + channel, + user: userId, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.invite', body, qs); + responseData = responseData.channel; + } + //https://api.slack.com/methods/conversations.leave + if (operation === 'leave') { + const channel = this.getNodeParameter('channelId', i) as string; + const body: IDataObject = { + channel, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.leave', body, qs); + } + //https://api.slack.com/methods/conversations.open + if (operation === 'open') { + const options = this.getNodeParameter('options', i) as IDataObject; + const body: IDataObject = {}; + if (options.channelId) { + body.channel = options.channelId as string; + } + if (options.returnIm) { + body.return_im = options.returnIm as boolean; + } + if (options.users) { + body.users = (options.users as string[]).join(','); + } + responseData = await slackApiRequest.call(this, 'POST', '/conversations.open', body, qs); + responseData = responseData.channel; + } + //https://api.slack.com/methods/conversations.rename + if (operation === 'rename') { + const channel = this.getNodeParameter('channelId', i) as IDataObject; + const name = this.getNodeParameter('name', i) as IDataObject; + const body: IDataObject = { + channel, + name, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.rename', body, qs); + responseData = responseData.channel; + } + //https://api.slack.com/methods/conversations.replies + if (operation === 'replies') { + const channel = this.getNodeParameter('channelId', i) as string; + const ts = this.getNodeParameter('ts', i) as string; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + qs.channel = channel; + qs.ts = ts; + if (filters.inclusive) { + qs.inclusive = filters.inclusive as boolean; + } + if (filters.latest) { + qs.latest = filters.latest as string; + } + if (filters.oldest) { + qs.oldest = filters.oldest as string; + } + if (returnAll === true) { + responseData = await salckApiRequestAllItems.call(this, 'messages', 'GET', '/conversations.replies', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await slackApiRequest.call(this, 'GET', '/conversations.replies', {}, qs); + responseData = responseData.messages; + } + } + //https://api.slack.com/methods/conversations.setPurpose + if (operation === 'setPurpose') { + const channel = this.getNodeParameter('channelId', i) as IDataObject; + const purpose = this.getNodeParameter('purpose', i) as IDataObject; + const body: IDataObject = { + channel, + purpose, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.setPurpose', body, qs); + responseData = responseData.channel; + } + //https://api.slack.com/methods/conversations.setTopic + if (operation === 'setTopic') { + const channel = this.getNodeParameter('channelId', i) as IDataObject; + const topic = this.getNodeParameter('topic', i) as IDataObject; + const body: IDataObject = { + channel, + topic, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.setTopic', body, qs); + responseData = responseData.channel; + } + //https://api.slack.com/methods/conversations.unarchive + if (operation === 'unarchive') { + const channel = this.getNodeParameter('channelId', i) as string; + const body: IDataObject = { + channel, + }; + responseData = await slackApiRequest.call(this, 'POST', '/conversations.unarchive', body, qs); } } if (resource === 'message') { + //https://api.slack.com/methods/chat.postMessage if (operation === 'post') { const channel = this.getNodeParameter('channel', i) as string; const text = this.getNodeParameter('text', i) as string; @@ -195,6 +409,174 @@ export class Slack implements INodeType { Object.assign(body, otherOptions); responseData = await slackApiRequest.call(this, 'POST', '/chat.postMessage', body, qs); } + //https://api.slack.com/methods/chat.update + if (operation === 'update') { + const channel = this.getNodeParameter('channel', i) as string; + const text = this.getNodeParameter('text', i) as string; + const ts = this.getNodeParameter('ts', i) as string; + const as_user = this.getNodeParameter('as_user', i) as boolean; + const attachments = this.getNodeParameter('attachments', i, []) as unknown as IAttachment[]; + const body: IDataObject = { + channel, + text, + ts, + as_user, + }; + // The node does save the fields data differently than the API + // expects so fix the data befre we send the request + for (const attachment of attachments) { + if (attachment.fields !== undefined) { + if (attachment.fields.item !== undefined) { + // Move the field-content up + // @ts-ignore + attachment.fields = attachment.fields.item; + } else { + // If it does not have any items set remove it + delete attachment.fields; + } + } + } + body['attachments'] = attachments; + + // Add all the other options to the request + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + Object.assign(body, updateFields); + responseData = await slackApiRequest.call(this, 'POST', '/chat.update', body, qs); + } + } + if (resource === 'star') { + //https://api.slack.com/methods/stars.add + if (operation === 'add') { + const options = this.getNodeParameter('options', i) as IDataObject; + const body: IDataObject = {}; + if (options.channelId) { + body.channel = options.channelId as string; + } + if (options.fileId) { + body.file = options.fileId as string; + } + if (options.fileComment) { + body.file_comment = options.fileComment as string; + } + if (options.timestamp) { + body.timestamp = options.timestamp as string; + } + responseData = await slackApiRequest.call(this, 'POST', '/stars.add', body, qs); + } + //https://api.slack.com/methods/stars.remove + if (operation === 'delete') { + const options = this.getNodeParameter('options', i) as IDataObject; + const body: IDataObject = {}; + if (options.channelId) { + body.channel = options.channelId as string; + } + if (options.fileId) { + body.file = options.fileId as string; + } + if (options.fileComment) { + body.file_comment = options.fileComment as string; + } + if (options.timestamp) { + body.timestamp = options.timestamp as string; + } + responseData = await slackApiRequest.call(this, 'POST', '/stars.remove', body, qs); + } + //https://api.slack.com/methods/stars.list + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + if (returnAll === true) { + responseData = await salckApiRequestAllItems.call(this, 'items', 'GET', '/stars.list', {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await slackApiRequest.call(this, 'GET', '/stars.list', {}, qs); + responseData = responseData.items; + } + } + } + if (resource === 'file') { + //https://api.slack.com/methods/files.upload + if (operation === 'upload') { + const options = this.getNodeParameter('options', i) as IDataObject; + const binaryData = this.getNodeParameter('binaryData', i) as boolean; + const body: IDataObject = {}; + if (options.channelIds) { + body.channels = (options.channelIds as string[]).join(','); + } + if (options.fileName) { + body.filename = options.fileName as string; + } + if (options.initialComment) { + body.initial_comment = options.initialComment as string; + } + if (options.threadTs) { + body.thread_ts = options.threadTs as string; + } + if (options.title) { + body.title = options.title as string; + } + if (binaryData) { + const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string; + if (items[i].binary === undefined + //@ts-ignore + || items[i].binary[binaryPropertyName] === undefined) { + throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`); + } + body.file = { + //@ts-ignore + value: Buffer.from(items[i].binary[binaryPropertyName].data, BINARY_ENCODING), + options: { + //@ts-ignore + filename: items[i].binary[binaryPropertyName].fileName, + //@ts-ignore + contentType: items[i].binary[binaryPropertyName].mimeType, + } + } + responseData = await slackApiRequest.call(this, 'POST', '/files.upload', {}, qs, { 'Content-Type': 'multipart/form-data' }, { formData: body }); + responseData = responseData.file; + } else { + const fileContent = this.getNodeParameter('fileContent', i) as string; + body.content = fileContent; + responseData = await slackApiRequest.call(this, 'POST', '/files.upload', body, qs, { 'Content-Type': 'application/x-www-form-urlencoded' }, { form: body }); + responseData = responseData.file; + } + } + //https://api.slack.com/methods/files.list + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + if (filters.channelId) { + qs.channel = filters.channelId as string; + } + if (filters.showFilesHidden) { + qs.show_files_hidden_by_limit = filters.showFilesHidden as boolean; + } + if (filters.tsFrom) { + qs.ts_from = filters.tsFrom as string; + } + if (filters.tsTo) { + qs.ts_to = filters.tsTo as string; + } + if (filters.types) { + qs.types = (filters.types as string[]).join(',') as string; + } + if (filters.userId) { + qs.user = filters.userId as string; + } + if (returnAll === true) { + responseData = await salckApiRequestAllItems.call(this, 'files', 'GET', '/files.list', {}, qs); + } else { + qs.count = this.getNodeParameter('limit', i) as number; + responseData = await slackApiRequest.call(this, 'GET', '/files.list', {}, qs); + responseData = responseData.files; + } + } + //https://api.slack.com/methods/files.info + if (operation === 'get') { + const fileId = this.getNodeParameter('fileId', i) as string; + qs.file = fileId; + responseData = await slackApiRequest.call(this, 'GET', '/files.info', {}, qs); + responseData = responseData.file; + } } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); diff --git a/packages/nodes-base/nodes/Slack/StarDescription.ts b/packages/nodes-base/nodes/Slack/StarDescription.ts new file mode 100644 index 0000000000..39174b6a87 --- /dev/null +++ b/packages/nodes-base/nodes/Slack/StarDescription.ts @@ -0,0 +1,185 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const starOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'star', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: 'Add a star to an item.', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a star from an item.', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all stars for a user.', + }, + ], + default: 'add', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const starFields = [ + +/* -------------------------------------------------------------------------- */ +/* star:add */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Options', + name: 'options', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'add' + ], + resource: [ + 'star', + ], + }, + }, + default: {}, + description: 'Options to set', + placeholder: 'Add options', + options: [ + { + displayName: 'Channel ID', + name: 'channelId', + type: 'string', + default: '', + description: 'Channel to add star to, or channel where the message to add star to was posted (used with timestamp).', + }, + { + displayName: 'File ID', + name: 'fileId', + type: 'string', + default: '', + description: 'File to add star to.', + }, + { + displayName: 'File Comment', + name: 'fileComment', + type: 'string', + default: '', + description: 'File comment to add star to.', + }, + { + displayName: 'Timestamp', + name: 'timestamp', + type: 'string', + default: '', + description: 'Timestamp of the message to add star to.', + }, + ], + }, +/* ----------------------------------------------------------------------- */ +/* star:delete */ +/* ----------------------------------------------------------------------- */ + { + displayName: 'Options', + name: 'options', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'delete' + ], + resource: [ + 'star', + ], + }, + }, + default: {}, + description: 'Options to set', + placeholder: 'Add options', + options: [ + { + displayName: 'Channel ID', + name: 'channelId', + type: 'string', + default: '', + description: 'Channel to add star to, or channel where the message to add star to was posted (used with timestamp).', + }, + { + displayName: 'File ID', + name: 'fileId', + type: 'string', + default: '', + description: 'File to add star to.', + }, + { + displayName: 'File Comment', + name: 'fileComment', + type: 'string', + default: '', + description: 'File comment to add star to.', + }, + { + displayName: 'Timestamp', + name: 'timestamp', + type: 'string', + default: '', + description: 'Timestamp of the message to add star to.', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* star:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'star', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'star', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, +] as INodeProperties[];