diff --git a/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts b/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts index 3e600122f2..d9e4fb551f 100644 --- a/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/SlackOAuth2Api.credentials.ts @@ -10,6 +10,8 @@ const userScopes = [ 'files:write', 'stars:read', 'stars:write', + 'users.profile:read', + 'users.profile:write' ]; export class SlackOAuth2Api implements ICredentialType { diff --git a/packages/nodes-base/nodes/Slack/Slack.node.ts b/packages/nodes-base/nodes/Slack/Slack.node.ts index 391cb15897..5770769190 100644 --- a/packages/nodes-base/nodes/Slack/Slack.node.ts +++ b/packages/nodes-base/nodes/Slack/Slack.node.ts @@ -16,26 +16,37 @@ import { channelFields, channelOperations, } from './ChannelDescription'; + import { messageFields, messageOperations, } from './MessageDescription'; + import { starFields, starOperations, } from './StarDescription'; + import { fileFields, fileOperations, } from './FileDescription'; + +import { + userProfileFields, + userProfileOperations, +} from './UserProfileDescription'; + import { slackApiRequest, slackApiRequestAllItems, validateJSON, } from './GenericFunctions'; + import { IAttachment, } from './MessageInterface'; +import moment = require('moment'); interface Attachment { fields: { @@ -156,6 +167,10 @@ export class Slack implements INodeType { name: 'Star', value: 'star', }, + { + name: 'User Profile', + value: 'userProfile', + }, ], default: 'message', description: 'The resource to operate on.', @@ -169,6 +184,8 @@ export class Slack implements INodeType { ...starFields, ...fileOperations, ...fileFields, + ...userProfileOperations, + ...userProfileFields, ], }; @@ -218,6 +235,22 @@ export class Slack implements INodeType { return returnData; }, + // Get all the team fields to display them to user so that he can + // select them easily + async getTeamFields(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const { profile: { fields } } = await slackApiRequest.call(this, 'GET', '/team.profile.get'); + console.log(fields); + for (const field of fields) { + const fieldName = field.label; + const fieldId = field.id; + returnData.push({ + name: fieldName, + value: fieldId, + }); + } + return returnData; + }, } }; @@ -232,6 +265,7 @@ export class Slack implements INodeType { const operation = this.getNodeParameter('operation', 0) as string; for (let i = 0; i < length; i++) { + responseData = { error: 'Resource ' + resource + ' / operation ' + operation + ' not found!'}; qs = {}; if (resource === 'channel') { //https://api.slack.com/methods/conversations.archive @@ -848,6 +882,55 @@ export class Slack implements INodeType { responseData = responseData.file; } } + if (resource === 'userProfile') { + //https://api.slack.com/methods/users.profile.set + if (operation === 'update') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const timezone = this.getTimezone(); + + const body: IDataObject = {}; + + Object.assign(body, additionalFields); + + if (body.status_expiration === undefined) { + body.status_expiration = 0; + + } else { + body.status_expiration = moment.tz(body.status_expiration as string, timezone).unix(); + } + + if (body.customFieldUi) { + const customFields = (body.customFieldUi as IDataObject).customFieldValues as IDataObject[]; + + body.fields = {}; + + for (const customField of customFields) { + //@ts-ignore + body.fields[customField.id] = { + value: customField.value, + alt: customField.alt, + }; + } + } + + responseData = await slackApiRequest.call(this, 'POST', '/users.profile.set', { profile: body }, qs); + + responseData = responseData.profile; + } + //https://api.slack.com/methods/users.profile.get + if (operation === 'get') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + const body: IDataObject = {}; + + Object.assign(body, additionalFields); + + responseData = await slackApiRequest.call(this, 'POST', '/users.profile.get', body); + + responseData = responseData.profile; + } + } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); } else { diff --git a/packages/nodes-base/nodes/Slack/UserProfileDescription.ts b/packages/nodes-base/nodes/Slack/UserProfileDescription.ts new file mode 100644 index 0000000000..a3475ee993 --- /dev/null +++ b/packages/nodes-base/nodes/Slack/UserProfileDescription.ts @@ -0,0 +1,183 @@ +import { + INodeProperties, +} from 'n8n-workflow'; +import { text } from 'express'; + +export const userProfileOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'userProfile', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: `Get your user's profile`, + }, + { + name: 'Update', + value: 'update', + description: `Update user's profile`, + }, + ], + default: 'get', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const userProfileFields = [ + +/* -------------------------------------------------------------------------- */ +/* userProfile:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userProfile', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Custom Fields', + name: 'customFieldUi', + placeholder: 'Add Custom Fields', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'customFieldValues', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'id', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTeamFields', + }, + default: '', + description: 'ID of the field to set.', + }, + { + displayName: 'Field Value', + name: 'value', + type: 'string', + default: '', + description: 'Value of the field to set.', + }, + { + displayName: 'Alt', + name: 'alt', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: `This field can only be changed by admins for users on paid teams.`, + }, + { + displayName: 'First Name', + name: 'first_name', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'last_name', + type: 'string', + default: '', + }, + { + displayName: 'Status Emoji', + name: 'status_emoji', + type: 'string', + default: '', + description: `is a string referencing an emoji enabled for the Slack team, such as :mountain_railway:`, + }, + { + displayName: 'Status Expiration', + name: 'status_expiration', + type: 'dateTime', + default: '', + description: `is an integer specifying seconds since the epoch, more commonly known as "UNIX time". Providing 0 or omitting this field results in a custom status that will not expire`, + }, + { + displayName: 'Status Text', + name: 'status_text', + type: 'string', + default: '', + description: `allows up to 100 characters, though we strongly encourage brevity.`, + }, + { + displayName: 'User ID', + name: 'user', + type: 'string', + default: '', + description: `ID of user to change. This argument may only be specified by team admins on paid teams.`, + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* userProfile:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userProfile', + ], + operation: [ + 'get', + ], + }, + }, + options: [ + { + displayName: 'Include Labels', + name: 'include_labels', + type: 'boolean', + default: false, + description: `Include labels for each ID in custom profile fields`, + }, + { + displayName: 'User ID', + name: 'user', + type: 'string', + default: '', + description: `User to retrieve profile info for`, + }, + ], + }, +] as INodeProperties[];