first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.slack",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Communication", "HITL"],
|
||||
"subcategories": {
|
||||
"HITL": ["Human in the Loop"]
|
||||
},
|
||||
"alias": ["human", "form", "wait", "hitl", "approval"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/slack/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/"
|
||||
}
|
||||
],
|
||||
"generic": [
|
||||
{
|
||||
"label": "6 e-commerce workflows to power up your Shopify s",
|
||||
"icon": "store",
|
||||
"url": "https://n8n.io/blog/no-code-ecommerce-workflow-automations/"
|
||||
},
|
||||
{
|
||||
"label": "Automate your data processing pipeline in 9 steps",
|
||||
"icon": "⚙️",
|
||||
"url": "https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "How to get started with CRM automation (with 3 no-code workflow ideas",
|
||||
"icon": "👥",
|
||||
"url": "https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/"
|
||||
},
|
||||
{
|
||||
"label": "5 tasks you can automate with the new Notion API ",
|
||||
"icon": "⚡️",
|
||||
"url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/"
|
||||
},
|
||||
{
|
||||
"label": "Build your own virtual assistant with n8n: A step by step guide",
|
||||
"icon": "👦",
|
||||
"url": "https://n8n.io/blog/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/"
|
||||
},
|
||||
{
|
||||
"label": "How to automatically give kudos to contributors with GitHub, Slack, and n8n",
|
||||
"icon": "👏",
|
||||
"url": "https://n8n.io/blog/how-to-automatically-give-kudos-to-contributors-with-github-slack-and-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "How Common Knowledge use workflow automation for activism",
|
||||
"icon": "✨",
|
||||
"url": "https://n8n.io/blog/automations-for-activists/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { INodeTypeBaseDescription, IVersionedNodeType } from 'n8n-workflow';
|
||||
import { VersionedNodeType } from 'n8n-workflow';
|
||||
|
||||
import { SlackV1 } from './V1/SlackV1.node';
|
||||
import { SlackV2 } from './V2/SlackV2.node';
|
||||
|
||||
export class Slack extends VersionedNodeType {
|
||||
constructor() {
|
||||
const baseDescription: INodeTypeBaseDescription = {
|
||||
displayName: 'Slack',
|
||||
name: 'slack',
|
||||
icon: 'file:slack.svg',
|
||||
group: ['output'],
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Slack API',
|
||||
defaultVersion: 2.4,
|
||||
};
|
||||
|
||||
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
|
||||
1: new SlackV1(baseDescription),
|
||||
2: new SlackV2(baseDescription),
|
||||
2.1: new SlackV2(baseDescription),
|
||||
2.2: new SlackV2(baseDescription),
|
||||
2.3: new SlackV2(baseDescription),
|
||||
2.4: new SlackV2(baseDescription),
|
||||
};
|
||||
|
||||
super(nodeVersions, baseDescription);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.slackTrigger",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Communication"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/slack/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.slacktrigger/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
import {
|
||||
type INodeListSearchItems,
|
||||
type ILoadOptionsFunctions,
|
||||
type INodeListSearchResult,
|
||||
type INodePropertyOptions,
|
||||
type IHookFunctions,
|
||||
type IWebhookFunctions,
|
||||
type IDataObject,
|
||||
type INodeType,
|
||||
type INodeTypeDescription,
|
||||
type IWebhookResponseData,
|
||||
type IBinaryKeyData,
|
||||
NodeConnectionTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { downloadFile, getChannelInfo, getUserInfo, verifySignature } from './SlackTriggerHelpers';
|
||||
import { slackApiRequestAllItems } from './V2/GenericFunctions';
|
||||
|
||||
export class SlackTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Slack Trigger',
|
||||
name: 'slackTrigger',
|
||||
icon: 'file:slack.svg',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["eventFilter"].join(", ")}}',
|
||||
description: 'Handle Slack events via webhooks',
|
||||
defaults: {
|
||||
name: 'Slack Trigger',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
credentials: [
|
||||
{
|
||||
name: 'slackApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Authentication',
|
||||
name: 'authentication',
|
||||
type: 'hidden',
|
||||
default: 'accessToken',
|
||||
},
|
||||
{
|
||||
displayName:
|
||||
'Set up a webhook in your Slack app to enable this node. <a href="https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.slacktrigger/#configure-a-webhook-in-slack" target="_blank">More info</a>. We also recommend setting up a <a href="https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.slacktrigger/#verify-the-webhook" target="_blank">signing secret</a> to ensure the authenticity of requests.',
|
||||
name: 'notice',
|
||||
type: 'notice',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Trigger On',
|
||||
name: 'trigger',
|
||||
type: 'multiOptions',
|
||||
options: [
|
||||
{
|
||||
name: 'Any Event',
|
||||
value: 'any_event',
|
||||
description: 'Triggers on any event',
|
||||
},
|
||||
{
|
||||
name: 'Bot / App Mention',
|
||||
value: 'app_mention',
|
||||
description: 'When your bot or app is mentioned in a channel the app is added to',
|
||||
},
|
||||
{
|
||||
name: 'File Made Public',
|
||||
value: 'file_public',
|
||||
description: 'When a file is made public',
|
||||
},
|
||||
{
|
||||
name: 'File Shared',
|
||||
value: 'file_share',
|
||||
description: 'When a file is shared in a channel the app is added to',
|
||||
},
|
||||
{
|
||||
name: 'New Message Posted to Channel',
|
||||
value: 'message',
|
||||
description: 'When a message is posted to a channel the app is added to',
|
||||
},
|
||||
{
|
||||
name: 'New Public Channel Created',
|
||||
value: 'channel_created',
|
||||
description: 'When a new public channel is created',
|
||||
},
|
||||
{
|
||||
name: 'New User',
|
||||
value: 'team_join',
|
||||
description: 'When a new user is added to Slack',
|
||||
},
|
||||
{
|
||||
name: 'Reaction Added',
|
||||
value: 'reaction_added',
|
||||
description: 'When a reaction is added to a message the app is added to',
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'Watch Whole Workspace',
|
||||
name: 'watchWorkspace',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to watch for the event in the whole workspace, rather than a specific channel',
|
||||
displayOptions: {
|
||||
show: {
|
||||
trigger: ['any_event', 'message', 'reaction_added', 'file_share', 'app_mention'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName:
|
||||
'This will use one execution for every event in any channel your bot is in, use with caution',
|
||||
name: 'notice',
|
||||
type: 'notice',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
trigger: ['any_event', 'message', 'reaction_added', 'file_share', 'app_mention'],
|
||||
watchWorkspace: [true],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Channel to Watch',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
required: true,
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
description:
|
||||
'The Slack channel to listen to events from. Applies to events: Bot/App mention, File Shared, New Message Posted on Channel, Reaction Added.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
watchWorkspace: [false],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
placeholder: 'Select a channel...',
|
||||
typeOptions: {
|
||||
searchListMethod: 'getChannels',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '[a-zA-Z0-9]{2,}',
|
||||
errorMessage: 'Not a valid Slack Channel ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'C0122KQ70S7E',
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
type: 'string',
|
||||
placeholder: 'https://app.slack.com/client/TS9594PZK/B0556F47Z3A',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: 'http(s)?://app.slack.com/client/.*/([a-zA-Z0-9]{2,})',
|
||||
errorMessage: 'Not a valid Slack Channel URL',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: 'https://app.slack.com/client/.*/([a-zA-Z0-9]{2,})',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Download Files',
|
||||
name: 'downloadFiles',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to download the files and add it to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
trigger: ['any_event', 'file_share'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Resolve IDs',
|
||||
name: 'resolveIds',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to resolve the IDs to their respective names and return them',
|
||||
},
|
||||
{
|
||||
displayName: 'Usernames or IDs to Ignore',
|
||||
name: 'userIds',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'A comma-separated string of encoded user IDs. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
listSearch: {
|
||||
async getChannels(
|
||||
this: ILoadOptionsFunctions,
|
||||
filter?: string,
|
||||
): Promise<INodeListSearchResult> {
|
||||
const qs = { types: 'public_channel,private_channel' };
|
||||
const channels = (await slackApiRequestAllItems.call(
|
||||
this,
|
||||
'channels',
|
||||
'GET',
|
||||
'/conversations.list',
|
||||
{},
|
||||
qs,
|
||||
)) as Array<{ id: string; name: string }>;
|
||||
const results: INodeListSearchItems[] = channels
|
||||
.map((c) => ({
|
||||
name: c.name,
|
||||
value: c.id,
|
||||
}))
|
||||
.filter(
|
||||
(c) =>
|
||||
!filter ||
|
||||
c.name.toLowerCase().includes(filter.toLowerCase()) ||
|
||||
c.value?.toString() === filter,
|
||||
)
|
||||
.sort((a, b) => {
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
|
||||
return 0;
|
||||
});
|
||||
return { results };
|
||||
},
|
||||
},
|
||||
loadOptions: {
|
||||
async getUsers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const users = (await slackApiRequestAllItems.call(
|
||||
this,
|
||||
'members',
|
||||
'GET',
|
||||
'/users.list',
|
||||
)) as Array<{ id: string; name: string }>;
|
||||
for (const user of users) {
|
||||
returnData.push({
|
||||
name: user.name,
|
||||
value: user.id,
|
||||
});
|
||||
}
|
||||
|
||||
returnData.sort((a, b) => {
|
||||
if (a.name < b.name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
return true;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const filters = this.getNodeParameter('trigger', []) as string[];
|
||||
const req = this.getRequestObject();
|
||||
const options = this.getNodeParameter('options', {}) as IDataObject;
|
||||
const binaryData: IBinaryKeyData = {};
|
||||
const watchWorkspace = this.getNodeParameter('watchWorkspace', false) as boolean;
|
||||
let eventChannel: string = '';
|
||||
|
||||
const isSignatureValid = await verifySignature.call(this);
|
||||
if (!isSignatureValid) {
|
||||
const res = this.getResponseObject();
|
||||
res.status(401).send('Unauthorized').end();
|
||||
return {
|
||||
noWebhookResponse: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (req.body.type === 'url_verification') {
|
||||
// Check if the request is a challenge request
|
||||
const res = this.getResponseObject();
|
||||
res.status(200).json({ challenge: req.body.challenge }).end();
|
||||
|
||||
return {
|
||||
noWebhookResponse: true,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if the event type is in the filters
|
||||
const eventType = req.body.event.type as string;
|
||||
|
||||
if (
|
||||
!filters.includes('file_share') &&
|
||||
!filters.includes('any_event') &&
|
||||
!filters.includes(eventType)
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (eventType !== 'team_join') {
|
||||
eventChannel =
|
||||
req.body.event.channel ?? req.body.event.item?.channel ?? req.body.event.channel_id;
|
||||
|
||||
// Check for single channel
|
||||
if (!watchWorkspace) {
|
||||
if (
|
||||
eventChannel !==
|
||||
(this.getNodeParameter('channelId', {}, { extractValue: true }) as string)
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user should be ignored
|
||||
if (options.userIds) {
|
||||
const userIds = options.userIds as string[];
|
||||
if (userIds.includes(req.body.event.user)) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (options.resolveIds) {
|
||||
if (req.body.event.user) {
|
||||
if (req.body.event.type === 'reaction_added') {
|
||||
req.body.event.user_resolved = await getUserInfo.call(this, req.body.event.user);
|
||||
req.body.event.item_user_resolved = await getUserInfo.call(
|
||||
this,
|
||||
req.body.event.item_user,
|
||||
);
|
||||
} else if (req.body.event.type === 'team_join') {
|
||||
req.body.event.user_resolved = req.body.event.user.name;
|
||||
} else {
|
||||
req.body.event.user_resolved = await getUserInfo.call(this, req.body.event.user);
|
||||
}
|
||||
}
|
||||
|
||||
if (eventChannel) {
|
||||
const channel = await getChannelInfo.call(this, eventChannel);
|
||||
const channelResolved = channel;
|
||||
req.body.event.channel_resolved = channelResolved;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
req.body.event.subtype === 'file_share' &&
|
||||
(filters.includes('file_share') || filters.includes('any_event'))
|
||||
) {
|
||||
if (this.getNodeParameter('downloadFiles', false) as boolean) {
|
||||
for (let i = 0; i < req.body.event.files.length; i++) {
|
||||
const file = (await downloadFile.call(
|
||||
this,
|
||||
req.body.event.files[i].url_private_download,
|
||||
)) as Buffer;
|
||||
|
||||
binaryData[`file_${i}`] = await this.helpers.prepareBinaryData(
|
||||
file,
|
||||
req.body.event.files[i].name,
|
||||
req.body.event.files[i].mimetype,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
workflowData: [
|
||||
[
|
||||
{
|
||||
json: req.body.event,
|
||||
binary: Object.keys(binaryData).length ? binaryData : undefined,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { createHmac } from 'crypto';
|
||||
import type { IHttpRequestOptions, IWebhookFunctions } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { slackApiRequest } from './V2/GenericFunctions';
|
||||
import { verifySignature as verifySignatureGeneric } from '../../utils/webhook-signature-verification';
|
||||
|
||||
export async function getUserInfo(this: IWebhookFunctions, userId: string): Promise<any> {
|
||||
const user = await slackApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
'/users.info',
|
||||
{},
|
||||
{
|
||||
user: userId,
|
||||
},
|
||||
);
|
||||
|
||||
return user.user.name;
|
||||
}
|
||||
|
||||
export async function getChannelInfo(this: IWebhookFunctions, channelId: string): Promise<any> {
|
||||
const channel = await slackApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
'/conversations.info',
|
||||
{},
|
||||
{
|
||||
channel: channelId,
|
||||
},
|
||||
);
|
||||
|
||||
return channel.channel.name;
|
||||
}
|
||||
|
||||
export async function downloadFile(this: IWebhookFunctions, url: string): Promise<any> {
|
||||
let options: IHttpRequestOptions = {
|
||||
method: 'GET',
|
||||
url,
|
||||
};
|
||||
|
||||
const requestOptions = {
|
||||
encoding: 'arraybuffer',
|
||||
returnFullResponse: true,
|
||||
json: false,
|
||||
useStream: true,
|
||||
};
|
||||
|
||||
options = Object.assign({}, options, requestOptions);
|
||||
|
||||
const response = await this.helpers.requestWithAuthentication.call(this, 'slackApi', options);
|
||||
|
||||
if (response.ok === false) {
|
||||
if (response.error === 'paid_teams_only') {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`Your current Slack plan does not include the resource '${
|
||||
this.getNodeParameter('resource', 0) as string
|
||||
}'`,
|
||||
{
|
||||
description:
|
||||
'Hint: Upgrade to a Slack plan that includes the functionality you want to use.',
|
||||
level: 'warning',
|
||||
},
|
||||
);
|
||||
} else if (response.error === 'missing_scope') {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Your Slack credential is missing required Oauth Scopes',
|
||||
{
|
||||
description: `Add the following scope(s) to your Slack App: ${response.needed}`,
|
||||
level: 'warning',
|
||||
},
|
||||
);
|
||||
}
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Slack error response: ' + JSON.stringify(response.error),
|
||||
);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function verifySignature(this: IWebhookFunctions): Promise<boolean> {
|
||||
const credential = await this.getCredentials('slackApi');
|
||||
const req = this.getRequestObject();
|
||||
|
||||
const timestamp = req.header('x-slack-request-timestamp');
|
||||
if (!timestamp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const signatureSecret = credential.signatureSecret;
|
||||
try {
|
||||
const isValid = verifySignatureGeneric({
|
||||
getExpectedSignature: () => {
|
||||
if (!signatureSecret || typeof signatureSecret !== 'string' || !req.rawBody) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hmac = createHmac('sha256', signatureSecret);
|
||||
|
||||
if (Buffer.isBuffer(req.rawBody)) {
|
||||
hmac.update(`v0:${timestamp}:`);
|
||||
hmac.update(req.rawBody);
|
||||
} else {
|
||||
const rawBodyString =
|
||||
typeof req.rawBody === 'string' ? req.rawBody : JSON.stringify(req.rawBody);
|
||||
hmac.update(`v0:${timestamp}:${rawBodyString}`);
|
||||
}
|
||||
|
||||
const computedSignature = `v0=${hmac.digest('hex')}`;
|
||||
return computedSignature;
|
||||
},
|
||||
skipIfNoExpectedSignature: !signatureSecret || typeof signatureSecret !== 'string',
|
||||
getActualSignature: () => {
|
||||
const actualSignature = req.header('x-slack-signature');
|
||||
return typeof actualSignature === 'string' ? actualSignature : null;
|
||||
},
|
||||
getTimestamp: () => timestamp,
|
||||
});
|
||||
return isValid;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,893 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const channelOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Archive',
|
||||
value: 'archive',
|
||||
description: 'Archives a conversation',
|
||||
action: 'Archive a channel',
|
||||
},
|
||||
{
|
||||
name: 'Close',
|
||||
value: 'close',
|
||||
description: 'Closes a direct message or multi-person direct message',
|
||||
action: 'Close a channel',
|
||||
},
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Initiates a public or private channel-based conversation',
|
||||
action: 'Create a channel',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get information about a channel',
|
||||
action: 'Get a channel',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get many channels in a Slack team',
|
||||
action: 'Get many channels',
|
||||
},
|
||||
{
|
||||
name: 'History',
|
||||
value: 'history',
|
||||
description: "Get a conversation's history of messages and events",
|
||||
action: 'Get the history of a channel',
|
||||
},
|
||||
{
|
||||
name: 'Invite',
|
||||
value: 'invite',
|
||||
description: 'Invite a user to a channel',
|
||||
action: 'Invite a user to a channel',
|
||||
},
|
||||
{
|
||||
name: 'Join',
|
||||
value: 'join',
|
||||
description: 'Joins an existing conversation',
|
||||
action: 'Join a channel',
|
||||
},
|
||||
{
|
||||
name: 'Kick',
|
||||
value: 'kick',
|
||||
description: 'Removes a user from a channel',
|
||||
action: 'Kick a user from a channel',
|
||||
},
|
||||
{
|
||||
name: 'Leave',
|
||||
value: 'leave',
|
||||
description: 'Leaves a conversation',
|
||||
action: 'Leave a channel',
|
||||
},
|
||||
{
|
||||
name: 'Member',
|
||||
value: 'member',
|
||||
description: 'List members of a conversation',
|
||||
action: 'Get members of a channel',
|
||||
},
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
description: 'Opens or resumes a direct message or multi-person direct message',
|
||||
action: 'Open a channel',
|
||||
},
|
||||
{
|
||||
name: 'Rename',
|
||||
value: 'rename',
|
||||
description: 'Renames a conversation',
|
||||
action: 'Rename a channel',
|
||||
},
|
||||
{
|
||||
name: 'Replies',
|
||||
value: 'replies',
|
||||
description: 'Get a thread of messages posted to a channel',
|
||||
action: 'Get a thread of messages posted to a channel',
|
||||
},
|
||||
{
|
||||
name: 'Set Purpose',
|
||||
value: 'setPurpose',
|
||||
description: 'Sets the purpose for a conversation',
|
||||
action: 'Set the purpose of a channel',
|
||||
},
|
||||
{
|
||||
name: 'Set Topic',
|
||||
value: 'setTopic',
|
||||
description: 'Sets the topic for a conversation',
|
||||
action: 'Set the topic of a channel',
|
||||
},
|
||||
{
|
||||
name: 'Unarchive',
|
||||
value: 'unarchive',
|
||||
description: 'Unarchives a conversation',
|
||||
action: 'Unarchive a channel',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
export const channelFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:archive */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['archive'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description:
|
||||
'The name of the channel to archive. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:close */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['close'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description:
|
||||
'The name of the channel to close. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'Channel name',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['create'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The name of the channel to create',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Is Private',
|
||||
name: 'isPrivate',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to create a private channel instead of a public one',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:invite */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['invite'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description:
|
||||
'The ID of the channel to invite user to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'User Names or IDs',
|
||||
name: 'userIds',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
default: [],
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['invite'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description:
|
||||
'The ID of the user to invite into channel. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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:kick */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
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. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'User Name or ID',
|
||||
name: 'userId',
|
||||
type: 'options',
|
||||
description:
|
||||
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['kick'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:join */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
description:
|
||||
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'Channel name',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['join'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['getAll'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Exclude Archived',
|
||||
name: 'excludeArchived',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether 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',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'mpim',
|
||||
value: 'mpim',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'im',
|
||||
value: 'im',
|
||||
},
|
||||
],
|
||||
default: ['public_channel'],
|
||||
description: 'Mix and match channel types',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:history */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
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. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['history'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results 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: 'Max number of 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:
|
||||
'Whether to 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 or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['leave'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description:
|
||||
'The name of the channel to leave. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:member */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
description:
|
||||
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['member'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['member'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
description: 'Max number of results to return',
|
||||
default: 100,
|
||||
placeholder: 'Limit',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['member'],
|
||||
resource: ['channel'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Resolve Data',
|
||||
name: 'resolveData',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['member'],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Whether to resolve the data automatically. By default the response only contain the ID to resource.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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: 'Whether you want the full IM channel definition in the response',
|
||||
},
|
||||
{
|
||||
displayName: 'User Names or IDs',
|
||||
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. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:rename */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['rename'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description:
|
||||
'The name of the channel to rename. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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 or ID',
|
||||
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. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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: 'Whether to return all results 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: 'Max number of 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:
|
||||
'Whether to 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 or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['setPurpose'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description:
|
||||
'Conversation to set the purpose of. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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 or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['setTopic'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description:
|
||||
'Conversation to set the topic of. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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 or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['unarchive'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
description:
|
||||
'The ID of the channel to unarchive. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,290 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const fileOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a file info',
|
||||
action: 'Get a file',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get & filters team files',
|
||||
action: 'Get many files',
|
||||
},
|
||||
{
|
||||
name: 'Upload',
|
||||
value: 'upload',
|
||||
description: 'Create or upload an existing file',
|
||||
action: 'Upload a file',
|
||||
},
|
||||
],
|
||||
default: 'upload',
|
||||
},
|
||||
];
|
||||
|
||||
export const fileFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:upload */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Binary File',
|
||||
name: 'binaryData',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
description: 'Whether 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: 'Input Binary Field',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
binaryData: [true],
|
||||
},
|
||||
},
|
||||
placeholder: '',
|
||||
hint: 'The name of the input binary field containing 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 option',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Names or IDs',
|
||||
name: 'channelIds',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'The channels to send the file to. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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: 'Whether to return all results 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: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getAll'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
description:
|
||||
'Channel containing the file to be listed. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Show Files Hidden By Limit',
|
||||
name: 'showFilesHidden',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to 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: 'Timestamp From',
|
||||
name: 'tsFrom',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Filter files created after this timestamp (inclusive)',
|
||||
},
|
||||
{
|
||||
displayName: 'Timestamp 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: 'Google Docs',
|
||||
value: 'gdocs',
|
||||
},
|
||||
{
|
||||
name: 'Images',
|
||||
value: 'images',
|
||||
},
|
||||
{
|
||||
name: 'PDFs',
|
||||
value: 'pdfs',
|
||||
},
|
||||
{
|
||||
name: 'Snippets',
|
||||
value: 'snippets',
|
||||
},
|
||||
{
|
||||
name: 'Spaces',
|
||||
value: 'spaces',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'zips',
|
||||
value: 'zips',
|
||||
},
|
||||
],
|
||||
default: ['all'],
|
||||
description: 'Filter files by type',
|
||||
},
|
||||
{
|
||||
displayName: 'User Name or ID',
|
||||
name: 'userId',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
description:
|
||||
'Filter files created by a single user. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* file:get */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['file'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,128 @@
|
||||
import get from 'lodash/get';
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IDataObject,
|
||||
IOAuth2Options,
|
||||
JsonObject,
|
||||
IHttpRequestMethods,
|
||||
IRequestOptions,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
export async function slackApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
resource: string,
|
||||
body: object = {},
|
||||
query: IDataObject = {},
|
||||
headers: IDataObject | undefined = undefined,
|
||||
option: IDataObject = {},
|
||||
): Promise<any> {
|
||||
const authenticationMethod = this.getNodeParameter('authentication', 0, 'accessToken') as string;
|
||||
let options: IRequestOptions = {
|
||||
method,
|
||||
headers: headers || {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
body,
|
||||
qs: query,
|
||||
uri: `https://slack.com/api${resource}`,
|
||||
json: true,
|
||||
};
|
||||
options = Object.assign({}, options, option);
|
||||
if (Object.keys(body).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
if (Object.keys(query).length === 0) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
const oAuth2Options: IOAuth2Options = {
|
||||
tokenType: 'Bearer',
|
||||
property: 'authed_user.access_token',
|
||||
};
|
||||
|
||||
try {
|
||||
const credentialType = authenticationMethod === 'accessToken' ? 'slackApi' : 'slackOAuth2Api';
|
||||
const response = await this.helpers.requestWithAuthentication.call(
|
||||
this,
|
||||
credentialType,
|
||||
options,
|
||||
{
|
||||
oauth2: oAuth2Options,
|
||||
},
|
||||
);
|
||||
|
||||
if (response.ok === false) {
|
||||
if (response.error === 'paid_teams_only') {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`Your current Slack plan does not include the resource '${this.getNodeParameter(
|
||||
'resource',
|
||||
0,
|
||||
)}'`,
|
||||
{
|
||||
description:
|
||||
'Hint: Upgrate to the Slack plan that includes the funcionality you want to use.',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Slack error response: ' + JSON.stringify(response),
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function slackApiRequestAllItems(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
propertyName: string,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
|
||||
body: any = {},
|
||||
query: IDataObject = {},
|
||||
): Promise<any> {
|
||||
const returnData: IDataObject[] = [];
|
||||
let responseData;
|
||||
query.page = 1;
|
||||
//if the endpoint uses legacy pagination use count
|
||||
//https://api.slack.com/docs/pagination#classic
|
||||
if (endpoint.includes('files.list')) {
|
||||
query.count = 100;
|
||||
} else {
|
||||
query.limit = 100;
|
||||
}
|
||||
do {
|
||||
responseData = await slackApiRequest.call(this, method, endpoint, body as IDataObject, query);
|
||||
query.cursor = get(responseData, 'response_metadata.next_cursor');
|
||||
query.page++;
|
||||
returnData.push.apply(returnData, responseData[propertyName] as IDataObject[]);
|
||||
} while (
|
||||
(responseData.response_metadata?.next_cursor !== undefined &&
|
||||
responseData.response_metadata.next_cursor !== '' &&
|
||||
responseData.response_metadata.next_cursor !== null) ||
|
||||
(responseData.paging?.pages !== undefined &&
|
||||
responseData.paging.page !== undefined &&
|
||||
responseData.paging.page < responseData.paging.pages)
|
||||
);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export function validateJSON(json: string | undefined): any {
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(json!);
|
||||
} catch (exception) {
|
||||
result = undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
export interface IAttachment {
|
||||
fields: {
|
||||
item?: object[];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const reactionOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['reaction'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'add',
|
||||
description: 'Adds a reaction to a message',
|
||||
action: 'Add a reaction',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get the reactions of a message',
|
||||
action: 'Get a reaction',
|
||||
},
|
||||
{
|
||||
name: 'Remove',
|
||||
value: 'remove',
|
||||
description: 'Remove a reaction of a message',
|
||||
action: 'Remove a reaction',
|
||||
},
|
||||
],
|
||||
default: 'add',
|
||||
},
|
||||
];
|
||||
|
||||
export const reactionFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['reaction'],
|
||||
operation: ['add', 'get', 'remove'],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Channel containing the message. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Emoji',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['reaction'],
|
||||
operation: ['add', 'remove'],
|
||||
},
|
||||
},
|
||||
description: 'Name of emoji',
|
||||
placeholder: '+1',
|
||||
},
|
||||
{
|
||||
displayName: 'Timestamp',
|
||||
name: 'timestamp',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['reaction'],
|
||||
operation: ['add', 'get', 'remove'],
|
||||
},
|
||||
},
|
||||
description: 'Timestamp of the message',
|
||||
},
|
||||
];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const starOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['star'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'add',
|
||||
description: 'Add a star to an item',
|
||||
action: 'Add a star',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a star from an item',
|
||||
action: 'Delete a star',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get many stars of autenticated user',
|
||||
action: 'Get many stars',
|
||||
},
|
||||
],
|
||||
default: 'add',
|
||||
},
|
||||
];
|
||||
|
||||
export const starFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* star:add */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['add'],
|
||||
resource: ['star'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
description: 'Options to set',
|
||||
placeholder: 'Add option',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'Channel to add star to, or channel where the message to add star to was posted (used with timestamp). Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'File Comment',
|
||||
name: 'fileComment',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'File comment to add star to',
|
||||
},
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'File 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 option',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'Channel to add star to, or channel where the message to add star to was posted (used with timestamp). Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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: 'Whether to return all results 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: 'Max number of results to return',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,114 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const userOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Info',
|
||||
value: 'info',
|
||||
description: 'Get information about a user',
|
||||
action: 'Get information about a user',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get a list of many users',
|
||||
action: 'Get many users',
|
||||
},
|
||||
{
|
||||
name: 'Get Presence',
|
||||
value: 'getPresence',
|
||||
description: 'Get online status of a user',
|
||||
action: "Get a user's presence status",
|
||||
},
|
||||
],
|
||||
default: 'info',
|
||||
},
|
||||
];
|
||||
|
||||
export const userFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:info */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User ID',
|
||||
name: 'user',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['info'],
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The ID of the user to get information about',
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getAll'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:getPresence */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User ID',
|
||||
name: 'user',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getPresence'],
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The ID of the user to get the online status of',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,334 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const userGroupOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a user group',
|
||||
action: 'Create a user group',
|
||||
},
|
||||
{
|
||||
name: 'Disable',
|
||||
value: 'disable',
|
||||
description: 'Disable a user group',
|
||||
action: 'Disable a user group',
|
||||
},
|
||||
{
|
||||
name: 'Enable',
|
||||
value: 'enable',
|
||||
description: 'Enable a user group',
|
||||
action: 'Enable a user group',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get many user groups',
|
||||
action: 'Get many user groups',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a user group',
|
||||
action: 'Update a user group',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
export const userGroupFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* userGroup:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['create'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'A name for the User Group. Must be unique among User Groups.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Names or IDs',
|
||||
name: 'channelIds',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'A comma-separated string of encoded channel IDs for which the User Group uses as a default. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A short description of the User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Handle',
|
||||
name: 'handle',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A mention handle. Must be unique among channels, users and User Groups.',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* userGroup:disable */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User Group ID',
|
||||
name: 'userGroupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['disable'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The encoded ID of the User Group to update',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['disable'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* userGroup:enable */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User Group ID',
|
||||
name: 'userGroupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['enable'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The encoded ID of the User Group to update',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['enable'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* userGroup:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getAll'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getAll'],
|
||||
resource: ['userGroup'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 500,
|
||||
},
|
||||
default: 100,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Disabled',
|
||||
name: 'include_disabled',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include disabled User Groups',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Users',
|
||||
name: 'include_users',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the list of users for each User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* userGroup:update */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User Group ID',
|
||||
name: 'userGroupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['update'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The encoded ID of the User Group to update',
|
||||
},
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Names or IDs',
|
||||
name: 'channels',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'A comma-separated string of encoded channel IDs for which the User Group uses as a default. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A short description of the User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Handle',
|
||||
name: 'handle',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A mention handle. Must be unique among channels, users and User Groups.',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A name for the User Group. Must be unique among User Groups.',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,177 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const userProfileOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userProfile'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: "Get your user's profile",
|
||||
action: 'Get a user profile',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: "Update user's profile",
|
||||
action: 'Update a user profile',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
},
|
||||
];
|
||||
|
||||
export const userProfileFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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 Name or ID',
|
||||
name: 'id',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getTeamFields',
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'ID of the field to set. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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',
|
||||
placeholder: 'name@email.com',
|
||||
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: 'Whether to include labels for each ID in custom profile fields',
|
||||
},
|
||||
{
|
||||
displayName: 'User ID',
|
||||
name: 'user',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'User to retrieve profile info for',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,866 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { slackChannelModes } from './utils';
|
||||
|
||||
export const channelOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Archive',
|
||||
value: 'archive',
|
||||
description: 'Archives a conversation',
|
||||
action: 'Archive a channel',
|
||||
},
|
||||
{
|
||||
name: 'Close',
|
||||
value: 'close',
|
||||
description: 'Closes a direct message or multi-person direct message',
|
||||
action: 'Close a channel',
|
||||
},
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Initiates a public or private channel-based conversation',
|
||||
action: 'Create a channel',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get information about a channel',
|
||||
action: 'Get a channel',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get many channels in a Slack team',
|
||||
action: 'Get many channels',
|
||||
},
|
||||
{
|
||||
name: 'History',
|
||||
value: 'history',
|
||||
description: "Get a conversation's history of messages and events",
|
||||
action: 'Get the history of a channel',
|
||||
},
|
||||
{
|
||||
name: 'Invite',
|
||||
value: 'invite',
|
||||
description: 'Invite a user to a channel',
|
||||
action: 'Invite a user to a channel',
|
||||
},
|
||||
{
|
||||
name: 'Join',
|
||||
value: 'join',
|
||||
description: 'Joins an existing conversation',
|
||||
action: 'Join a channel',
|
||||
},
|
||||
{
|
||||
name: 'Kick',
|
||||
value: 'kick',
|
||||
description: 'Removes a user from a channel',
|
||||
action: 'Kick a user from a channel',
|
||||
},
|
||||
{
|
||||
name: 'Leave',
|
||||
value: 'leave',
|
||||
description: 'Leaves a conversation',
|
||||
action: 'Leave a channel',
|
||||
},
|
||||
{
|
||||
name: 'Member',
|
||||
value: 'member',
|
||||
description: 'List members of a conversation',
|
||||
action: 'Get members of a channel',
|
||||
},
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
description: 'Opens or resumes a direct message or multi-person direct message',
|
||||
action: 'Open a channel',
|
||||
},
|
||||
{
|
||||
name: 'Rename',
|
||||
value: 'rename',
|
||||
description: 'Renames a conversation',
|
||||
action: 'Rename a channel',
|
||||
},
|
||||
{
|
||||
name: 'Replies',
|
||||
value: 'replies',
|
||||
description: 'Get a thread of messages posted to a channel',
|
||||
action: 'Get a thread of messages posted to a channel',
|
||||
},
|
||||
{
|
||||
name: 'Set Purpose',
|
||||
value: 'setPurpose',
|
||||
description: 'Sets the purpose for a conversation',
|
||||
action: 'Set the purpose of a channel',
|
||||
},
|
||||
{
|
||||
name: 'Set Topic',
|
||||
value: 'setTopic',
|
||||
description: 'Sets the topic for a conversation',
|
||||
action: 'Set the topic of a channel',
|
||||
},
|
||||
{
|
||||
name: 'Unarchive',
|
||||
value: 'unarchive',
|
||||
description: 'Unarchives a conversation',
|
||||
action: 'Unarchive a channel',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
export const channelFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:archive */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
description: 'The Slack channel to archive',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['archive'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
modes: slackChannelModes,
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:close */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['close'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel to close',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'Channel name',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['create'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Channel Visibility',
|
||||
name: 'channelVisibility',
|
||||
type: 'options',
|
||||
default: 'public',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['create'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Public Channel',
|
||||
value: 'public',
|
||||
},
|
||||
{
|
||||
name: 'Private Channel',
|
||||
value: 'private',
|
||||
},
|
||||
],
|
||||
description:
|
||||
'Whether to create a Public or a Private Slack channel. <a href="https://slack.com/help/articles/360017938993-What-is-a-channel">More info</a>.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:invite */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['invite'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel to invite to',
|
||||
},
|
||||
{
|
||||
displayName: 'User Names or IDs',
|
||||
name: 'userIds',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
default: [],
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['invite'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description:
|
||||
'The ID of the user to invite into channel. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['get'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
description: 'The Slack channel to get',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
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:kick */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['kick'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel to kick the user from',
|
||||
},
|
||||
{
|
||||
displayName: 'User Name or ID',
|
||||
name: 'userId',
|
||||
type: 'options',
|
||||
description:
|
||||
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['kick'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:join */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
description: 'The Slack channel to join',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['join'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['getAll'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Exclude Archived',
|
||||
name: 'excludeArchived',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether 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',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'mpim',
|
||||
value: 'mpim',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'im',
|
||||
value: 'im',
|
||||
},
|
||||
],
|
||||
default: ['public_channel'],
|
||||
description: 'Mix and match channel types',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:history */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['history'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel to get the history from',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['history'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results 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: 'Max number of 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:
|
||||
'Whether to 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: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['leave'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel to leave from',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:member */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
description: 'The Slack channel to get the members from',
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['member'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['member'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
description: 'Max number of results to return',
|
||||
default: 100,
|
||||
placeholder: 'Limit',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['member'],
|
||||
resource: ['channel'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Resolve Data',
|
||||
name: 'resolveData',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['member'],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Whether to resolve the data automatically. By default the response only contain the ID to resource.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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: 'Whether you want the full IM channel definition in the response',
|
||||
},
|
||||
{
|
||||
displayName: 'User Names or IDs',
|
||||
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. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:rename */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['rename'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack 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: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['replies'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel to replies to',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Timestamp',
|
||||
name: 'ts',
|
||||
type: 'number',
|
||||
default: undefined,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['replies'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'Timestamp of the message to reply',
|
||||
placeholder: '1663233118.856619',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['channel'],
|
||||
operation: ['replies'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results 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: 'Max number of 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:
|
||||
'Whether to 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: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['setPurpose'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel 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: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['setTopic'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel to set the topic of',
|
||||
},
|
||||
{
|
||||
displayName: 'Topic',
|
||||
name: 'topic',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['setTopic'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* channel:unarchive */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['unarchive'],
|
||||
resource: ['channel'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The Slack channel to unarchive',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,326 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const fileOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
action: 'Get a file',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get & filters team files',
|
||||
action: 'Get many files',
|
||||
},
|
||||
{
|
||||
name: 'Upload',
|
||||
value: 'upload',
|
||||
description: 'Create or upload an existing file',
|
||||
action: 'Upload a file',
|
||||
},
|
||||
],
|
||||
default: 'upload',
|
||||
},
|
||||
];
|
||||
|
||||
export const fileFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:upload */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Binary File',
|
||||
name: 'binaryData',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
'@version': [2, 2.1],
|
||||
},
|
||||
},
|
||||
description: 'Whether 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],
|
||||
'@version': [2, 2.1],
|
||||
},
|
||||
},
|
||||
placeholder: '',
|
||||
},
|
||||
{
|
||||
displayName: 'File Property',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
binaryData: [true],
|
||||
'@version': [2, 2.1],
|
||||
},
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property which contains the data for the file to be uploaded',
|
||||
},
|
||||
{
|
||||
displayName: 'File Property',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
'@version': [{ _cnd: { gte: 2.2 } }],
|
||||
},
|
||||
},
|
||||
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 option',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Names or IDs',
|
||||
name: 'channelIds',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [2, 2.1],
|
||||
},
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'The channels to send the file to. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [{ _cnd: { gte: 2.2 } }],
|
||||
},
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'The channel to send the file to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'File Name',
|
||||
name: 'fileName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Initial Comment',
|
||||
name: 'initialComment',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The message text introducing the file in specified channels',
|
||||
},
|
||||
{
|
||||
displayName: 'Thread Timestamp',
|
||||
name: 'threadTs',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
"Provide another message's Timestamp value to upload this file as a reply. Never use a reply's Timestamp value; use its parent instead.",
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* file:getAll */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['file'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results 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: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getAll'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
description:
|
||||
'Channel containing the file to be listed. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Show Files Hidden By Limit',
|
||||
name: 'showFilesHidden',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to 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: 'Message Timestamp From',
|
||||
name: 'tsFrom',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Filter files created after this timestamp (inclusive)',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Timestamp 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: 'Google Docs',
|
||||
value: 'gdocs',
|
||||
},
|
||||
{
|
||||
name: 'Images',
|
||||
value: 'images',
|
||||
},
|
||||
{
|
||||
name: 'PDFs',
|
||||
value: 'pdfs',
|
||||
},
|
||||
{
|
||||
name: 'Snippets',
|
||||
value: 'snippets',
|
||||
},
|
||||
{
|
||||
name: 'Spaces',
|
||||
value: 'spaces',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'zips',
|
||||
value: 'zips',
|
||||
},
|
||||
],
|
||||
default: ['all'],
|
||||
description: 'Filter files by type',
|
||||
},
|
||||
{
|
||||
displayName: 'User Name or ID',
|
||||
name: 'userId',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
description:
|
||||
'Filter files created by a single user. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* file:get */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['file'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,474 @@
|
||||
import get from 'lodash/get';
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IOAuth2Options,
|
||||
IHttpRequestMethods,
|
||||
IRequestOptions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError, sleep } from 'n8n-workflow';
|
||||
|
||||
import type { SendAndWaitMessageBody } from './MessageInterface';
|
||||
import { getSendAndWaitConfig } from '../../../utils/sendAndWait/utils';
|
||||
import { createUtmCampaignLink } from '../../../utils/utilities';
|
||||
|
||||
interface RateLimitOptions {
|
||||
/**
|
||||
* The maximum number of times to retry the request if a rate limit error occurs.
|
||||
*/
|
||||
maxRetries?: number;
|
||||
/**
|
||||
* The delay in milliseconds to wait before retrying the request if 'retry-after' header is not present.
|
||||
*/
|
||||
fallbackDelay?: number;
|
||||
/**
|
||||
* What to do when a rate limit error occurs and maxRetries is exceeded.
|
||||
* - 'throw' will throw an error
|
||||
* - 'stop' will return the data collected so far with cursor/page info
|
||||
*/
|
||||
onFail?: 'throw' | 'stop';
|
||||
}
|
||||
|
||||
function isDefined<T>(value: T | undefined | null | ''): value is NonNullable<T> {
|
||||
return value !== undefined && value !== null && value !== '';
|
||||
}
|
||||
|
||||
export async function slackApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
resource: string,
|
||||
body: object = {},
|
||||
query: IDataObject = {},
|
||||
headers: {} | undefined = undefined,
|
||||
option: Partial<IRequestOptions> = {},
|
||||
// tslint:disable-next-line:no-any
|
||||
): Promise<any> {
|
||||
const authenticationMethod = this.getNodeParameter('authentication', 0, 'accessToken') as string;
|
||||
let options: IRequestOptions = {
|
||||
method,
|
||||
headers: headers ?? {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
},
|
||||
body,
|
||||
qs: query,
|
||||
uri: resource.startsWith('https') ? resource : `https://slack.com/api${resource}`,
|
||||
json: true,
|
||||
};
|
||||
options = Object.assign({}, options, option);
|
||||
if (Object.keys(body).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
if (Object.keys(query).length === 0) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
const oAuth2Options: IOAuth2Options = {
|
||||
tokenType: 'Bearer',
|
||||
property: 'authed_user.access_token',
|
||||
};
|
||||
|
||||
const credentialType = authenticationMethod === 'accessToken' ? 'slackApi' : 'slackOAuth2Api';
|
||||
const response = await this.helpers.requestWithAuthentication.call(
|
||||
this,
|
||||
credentialType,
|
||||
options,
|
||||
{
|
||||
oauth2: oAuth2Options,
|
||||
},
|
||||
);
|
||||
|
||||
const responseData = options.resolveWithFullResponse ? response.body : response;
|
||||
|
||||
// don't try to handle errors if simple responses are disabled
|
||||
if (responseData.ok === false && options.simple !== false) {
|
||||
if (responseData.error === 'paid_teams_only') {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`Your current Slack plan does not include the resource '${
|
||||
this.getNodeParameter('resource', 0) as string
|
||||
}'`,
|
||||
{
|
||||
description:
|
||||
'Hint: Upgrade to a Slack plan that includes the functionality you want to use.',
|
||||
level: 'warning',
|
||||
},
|
||||
);
|
||||
} else if (responseData.error === 'missing_scope') {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Your Slack credential is missing required Oauth Scopes',
|
||||
{
|
||||
description: `Add the following scope(s) to your Slack App: ${responseData.needed}`,
|
||||
level: 'warning',
|
||||
},
|
||||
);
|
||||
} else if (responseData.error === 'not_admin') {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Need higher Role Level for this Operation (e.g. Owner or Admin Rights)',
|
||||
{
|
||||
description:
|
||||
'Hint: Check the Role of your Slack App Integration. For more information see the Slack Documentation - https://slack.com/help/articles/360018112273-Types-of-roles-in-Slack',
|
||||
level: 'warning',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'Slack error response: ' + JSON.stringify(responseData.error),
|
||||
);
|
||||
}
|
||||
|
||||
if (responseData.ts !== undefined) {
|
||||
Object.assign(responseData, { message_timestamp: responseData.ts });
|
||||
delete responseData.ts;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
function hasNextPage(responseData: any, propertyName: string): boolean {
|
||||
const nextCursorDefined = isDefined(responseData.response_metadata?.next_cursor);
|
||||
const morePagesAvailable =
|
||||
isDefined(responseData.paging?.pages) &&
|
||||
isDefined(responseData.paging.page) &&
|
||||
responseData.paging.page < responseData.paging.pages;
|
||||
const morePropertyPagesAvailable =
|
||||
isDefined(responseData[propertyName].paging?.pages) &&
|
||||
isDefined(responseData[propertyName].paging.page) &&
|
||||
responseData[propertyName].paging.page < responseData[propertyName].paging.pages;
|
||||
return nextCursorDefined || morePagesAvailable || morePropertyPagesAvailable;
|
||||
}
|
||||
|
||||
export async function slackApiRequestAllItemsWithRateLimit<TResponseData>(
|
||||
context: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
propertyName: string,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
body: any = {},
|
||||
query: IDataObject = {},
|
||||
options: RateLimitOptions = {},
|
||||
): Promise<{ data: TResponseData[]; cursor?: string; page?: string }> {
|
||||
const { maxRetries = 3, fallbackDelay = 30_000, onFail = 'throw' } = options;
|
||||
|
||||
const returnData: TResponseData[] = [];
|
||||
let responseData;
|
||||
query.page = 1;
|
||||
//if the endpoint uses legacy pagination use count
|
||||
//https://api.slack.com/docs/pagination#classic
|
||||
if (endpoint.includes('files.list')) {
|
||||
query.count = 100;
|
||||
} else {
|
||||
query.limit = query.limit ?? 100;
|
||||
}
|
||||
do {
|
||||
let retryCount = 0;
|
||||
let requestSuccessful = false;
|
||||
|
||||
while (!requestSuccessful) {
|
||||
const response = await slackApiRequest.call(
|
||||
context,
|
||||
method,
|
||||
endpoint,
|
||||
body as IDataObject,
|
||||
query,
|
||||
{},
|
||||
{ resolveWithFullResponse: true, simple: false },
|
||||
);
|
||||
|
||||
const getErrMsg = () =>
|
||||
'Slack error response: ' +
|
||||
JSON.stringify(response.body?.error ?? response.statusMessage ?? 'Unknown error');
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
retryCount = 0;
|
||||
responseData = response.body;
|
||||
requestSuccessful = true;
|
||||
} else if (response.statusCode === 429) {
|
||||
const shouldRetry = retryCount < maxRetries;
|
||||
// if onFail='stop' we should wait, so that user don't hit rate limit when scrolling through results
|
||||
if (shouldRetry || onFail === 'stop') {
|
||||
// Extract Retry-After header (in seconds) and convert to milliseconds
|
||||
const retryAfterHeader =
|
||||
response.headers?.['retry-after'] ?? response.headers?.['Retry-After'];
|
||||
const waitTime = retryAfterHeader ? parseInt(retryAfterHeader, 10) * 1000 : fallbackDelay;
|
||||
await sleep(waitTime);
|
||||
retryCount++;
|
||||
}
|
||||
|
||||
if (shouldRetry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (onFail === 'stop') {
|
||||
// Return the data collected so far with cursor/page info
|
||||
const result: { data: TResponseData[]; cursor?: string; page?: string } = {
|
||||
data: returnData,
|
||||
};
|
||||
|
||||
// Add cursor if available
|
||||
if (query.cursor) {
|
||||
result.cursor = query.cursor as string;
|
||||
}
|
||||
// Add nextPage if using legacy pagination
|
||||
if (responseData?.paging?.page) {
|
||||
result.page = String(responseData.paging.page);
|
||||
} else if (responseData?.[propertyName]?.paging?.page) {
|
||||
result.page = String(responseData[propertyName].paging.page);
|
||||
} else if (query.page) {
|
||||
result.page = String(query.page);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
throw new NodeOperationError(context.getNode(), getErrMsg());
|
||||
} else {
|
||||
throw new NodeOperationError(context.getNode(), getErrMsg());
|
||||
}
|
||||
}
|
||||
|
||||
query.cursor = get(responseData, 'response_metadata.next_cursor');
|
||||
query.page++;
|
||||
returnData.push.apply(
|
||||
returnData,
|
||||
(responseData[propertyName].matches as TResponseData[]) ?? responseData[propertyName],
|
||||
);
|
||||
} while (hasNextPage(responseData, propertyName));
|
||||
|
||||
return { data: returnData };
|
||||
}
|
||||
|
||||
export async function slackApiRequestAllItems(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
propertyName: string,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
// tslint:disable-next-line:no-any
|
||||
body: any = {},
|
||||
query: IDataObject = {},
|
||||
// tslint:disable-next-line:no-any
|
||||
): Promise<any> {
|
||||
const returnData: IDataObject[] = [];
|
||||
let responseData;
|
||||
query.page = 1;
|
||||
//if the endpoint uses legacy pagination use count
|
||||
//https://api.slack.com/docs/pagination#classic
|
||||
if (endpoint.includes('files.list')) {
|
||||
query.count = 100;
|
||||
} else {
|
||||
query.limit = query.limit ?? 100;
|
||||
}
|
||||
do {
|
||||
responseData = await slackApiRequest.call(this, method, endpoint, body as IDataObject, query);
|
||||
query.cursor = get(responseData, 'response_metadata.next_cursor');
|
||||
query.page++;
|
||||
returnData.push.apply(
|
||||
returnData,
|
||||
(responseData[propertyName].matches as IDataObject[]) ?? responseData[propertyName],
|
||||
);
|
||||
} while (hasNextPage(responseData, propertyName));
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export function getMessageContent(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
i: number,
|
||||
nodeVersion: number,
|
||||
instanceId?: string,
|
||||
) {
|
||||
const includeLinkToWorkflow = this.getNodeParameter(
|
||||
'otherOptions.includeLinkToWorkflow',
|
||||
i,
|
||||
nodeVersion >= 2.1 ? true : false,
|
||||
) as IDataObject;
|
||||
|
||||
const { id } = this.getWorkflow();
|
||||
const automatedMessage = `_Automated with this <${this.getInstanceBaseUrl()}workflow/${id}?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=${encodeURIComponent(
|
||||
'n8n-nodes-base.slack',
|
||||
)}${instanceId ? '_' + instanceId : ''}|n8n workflow>_`;
|
||||
const messageType = this.getNodeParameter('messageType', i) as string;
|
||||
|
||||
let content: IDataObject = {};
|
||||
const text = this.getNodeParameter('text', i, '') as string;
|
||||
switch (messageType) {
|
||||
case 'text':
|
||||
content = {
|
||||
text: includeLinkToWorkflow ? `${text}\n${automatedMessage}` : text,
|
||||
};
|
||||
break;
|
||||
case 'block':
|
||||
content = this.getNodeParameter('blocksUi', i, {}, { ensureType: 'object' }) as IDataObject;
|
||||
|
||||
if (includeLinkToWorkflow && Array.isArray(content.blocks)) {
|
||||
content.blocks.push({
|
||||
type: 'section',
|
||||
text: {
|
||||
type: 'mrkdwn',
|
||||
text: automatedMessage,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (text) {
|
||||
content.text = text;
|
||||
}
|
||||
break;
|
||||
case 'attachment':
|
||||
const attachmentsUI = this.getNodeParameter('attachments', i) as IDataObject[];
|
||||
|
||||
const attachments: IDataObject[] = [];
|
||||
|
||||
for (const attachment of attachmentsUI) {
|
||||
if (attachment.fields !== undefined) {
|
||||
if ((attachment?.fields as IDataObject)?.item) {
|
||||
attachment.fields = (attachment?.fields as IDataObject)?.item as IDataObject[];
|
||||
}
|
||||
}
|
||||
attachments.push(attachment);
|
||||
}
|
||||
|
||||
content = { attachments } as IDataObject;
|
||||
|
||||
if (includeLinkToWorkflow && Array.isArray(content.attachments)) {
|
||||
content.attachments.push({
|
||||
text: automatedMessage,
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The message type "${messageType}" is not known!`,
|
||||
);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:no-any
|
||||
export function validateJSON(json: string | undefined): any {
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(json!);
|
||||
} catch (exception) {
|
||||
result = undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getTarget(
|
||||
context: IExecuteFunctions,
|
||||
itemIndex: number,
|
||||
idType: 'user' | 'channel',
|
||||
): string {
|
||||
let target = '';
|
||||
|
||||
if (idType === 'channel') {
|
||||
target = context.getNodeParameter('channelId', itemIndex, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
} else {
|
||||
target = context.getNodeParameter('user', itemIndex, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
}
|
||||
|
||||
if (
|
||||
idType === 'user' &&
|
||||
(context.getNodeParameter('user', itemIndex) as IDataObject).mode === 'username'
|
||||
) {
|
||||
target = target.slice(0, 1) === '@' ? target : `@${target}`;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
export function processThreadOptions(threadOptions: IDataObject | undefined): IDataObject {
|
||||
const result: IDataObject = {};
|
||||
|
||||
if (threadOptions?.replyValues) {
|
||||
const replyValues = threadOptions.replyValues as IDataObject;
|
||||
if (replyValues.thread_ts) {
|
||||
result.thread_ts = String(replyValues.thread_ts);
|
||||
}
|
||||
if (replyValues.reply_broadcast !== undefined) {
|
||||
result.reply_broadcast = replyValues.reply_broadcast;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createSendAndWaitMessageBody(context: IExecuteFunctions) {
|
||||
const select = context.getNodeParameter('select', 0) as 'user' | 'channel';
|
||||
const target = getTarget(context, 0, select);
|
||||
|
||||
const config = getSendAndWaitConfig(context);
|
||||
|
||||
const body: SendAndWaitMessageBody = {
|
||||
channel: target,
|
||||
blocks: [
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
type: 'section',
|
||||
text: {
|
||||
type: context.getNode().typeVersion > 2.2 ? 'mrkdwn' : 'plain_text',
|
||||
text: config.message,
|
||||
emoji: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'section',
|
||||
text: {
|
||||
type: 'plain_text',
|
||||
text: ' ',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
type: 'actions',
|
||||
elements: config.options.map((option) => {
|
||||
return {
|
||||
type: 'button',
|
||||
style: option.style === 'primary' ? 'primary' : undefined,
|
||||
text: {
|
||||
type: 'plain_text',
|
||||
text: option.label,
|
||||
emoji: true,
|
||||
},
|
||||
url: option.url,
|
||||
};
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const otherOptions = context.getNodeParameter('options', 0, {});
|
||||
const threadParams = processThreadOptions(otherOptions?.thread_ts as IDataObject);
|
||||
Object.assign(body, threadParams);
|
||||
|
||||
if (config.appendAttribution) {
|
||||
const instanceId = context.getInstanceId();
|
||||
const attributionText = 'This message was sent automatically with ';
|
||||
const link = createUtmCampaignLink('n8n-nodes-base.slack', instanceId);
|
||||
body.blocks.push({
|
||||
type: 'section',
|
||||
text: {
|
||||
type: 'mrkdwn',
|
||||
text: `${attributionText} _<${link}|n8n>_`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (context.getNode().typeVersion > 2.2 && body.blocks?.[1]?.type === 'section') {
|
||||
delete body.blocks[1].text.emoji;
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
export interface IAttachment {
|
||||
fields: {
|
||||
item?: object[];
|
||||
};
|
||||
}
|
||||
|
||||
// Used for SendAndWaitMessage
|
||||
export interface TextBlock {
|
||||
type: string;
|
||||
text: string;
|
||||
emoji?: boolean;
|
||||
}
|
||||
|
||||
export interface SectionBlock {
|
||||
type: 'section';
|
||||
text: TextBlock;
|
||||
}
|
||||
|
||||
export interface DividerBlock {
|
||||
type: 'divider';
|
||||
}
|
||||
|
||||
export interface ButtonElement {
|
||||
type: 'button';
|
||||
style?: 'primary';
|
||||
text: TextBlock;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface ActionsBlock {
|
||||
type: 'actions';
|
||||
elements: ButtonElement[];
|
||||
}
|
||||
|
||||
export interface SendAndWaitMessageBody {
|
||||
channel: string;
|
||||
blocks: Array<DividerBlock | SectionBlock | ActionsBlock>;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { slackChannelModes } from './utils';
|
||||
|
||||
export const reactionOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['reaction'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'add',
|
||||
description: 'Adds a reaction to a message',
|
||||
action: 'Add a reaction',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get the reactions of a message',
|
||||
action: 'Get a reaction',
|
||||
},
|
||||
{
|
||||
name: 'Remove',
|
||||
value: 'remove',
|
||||
description: 'Remove a reaction of a message',
|
||||
action: 'Remove a reaction',
|
||||
},
|
||||
],
|
||||
default: 'add',
|
||||
},
|
||||
];
|
||||
|
||||
export const reactionFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
modes: slackChannelModes,
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['reaction'],
|
||||
operation: ['add', 'get', 'remove'],
|
||||
},
|
||||
},
|
||||
description: 'The Slack channel to get the reactions from',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Timestamp',
|
||||
name: 'timestamp',
|
||||
required: true,
|
||||
type: 'number',
|
||||
default: undefined,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['reaction'],
|
||||
operation: ['add', 'get', 'remove'],
|
||||
},
|
||||
},
|
||||
description: 'Timestamp of the message to add, get or remove',
|
||||
placeholder: '1663233118.856619',
|
||||
},
|
||||
{
|
||||
displayName: 'Emoji Code',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['reaction'],
|
||||
operation: ['add', 'remove'],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Emoji code to use for the message reaction. Use emoji codes like +1, not an actual emoji like 👍. <a target="_blank" href=" https://www.webfx.com/tools/emoji-cheat-sheet/">List of common emoji codes</a>',
|
||||
placeholder: '+1',
|
||||
},
|
||||
];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,224 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
import { slackChannelModes } from './utils';
|
||||
|
||||
export const starOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['star'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'add',
|
||||
description: 'Add a star to an item',
|
||||
action: 'Add a star',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a star from an item',
|
||||
action: 'Delete a star',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get many stars of autenticated user',
|
||||
action: 'Get many stars',
|
||||
},
|
||||
],
|
||||
default: 'add',
|
||||
},
|
||||
];
|
||||
|
||||
export const starFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* star:add */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Item to Add Star',
|
||||
name: 'target',
|
||||
type: 'options',
|
||||
required: true,
|
||||
description: 'Choose whether to add a star to a message or a file',
|
||||
default: '',
|
||||
placeholder: 'Select...',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['add'],
|
||||
resource: ['star'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Message',
|
||||
value: 'message',
|
||||
},
|
||||
{
|
||||
name: 'File',
|
||||
value: 'file',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Channel',
|
||||
name: 'channelId',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a channel...',
|
||||
description: 'The Slack channel to add a star to',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['star'],
|
||||
operation: ['add'],
|
||||
target: ['message', 'file'],
|
||||
},
|
||||
},
|
||||
modes: slackChannelModes,
|
||||
},
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['star'],
|
||||
operation: ['add'],
|
||||
target: ['file'],
|
||||
},
|
||||
},
|
||||
description: 'File to add star to',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Timestamp',
|
||||
name: 'timestamp',
|
||||
type: 'number',
|
||||
default: undefined,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['star'],
|
||||
operation: ['add'],
|
||||
target: ['message'],
|
||||
},
|
||||
},
|
||||
description: 'Timestamp of the message to add',
|
||||
placeholder: '1663233118.856619',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['add'],
|
||||
resource: ['star'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
description: 'Options to set',
|
||||
placeholder: 'Add option',
|
||||
options: [
|
||||
{
|
||||
displayName: 'File Comment',
|
||||
name: 'fileComment',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'File comment 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 option',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Name or ID',
|
||||
name: 'channelId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'Channel to add star to, or channel where the message to add star to was posted (used with timestamp). Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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: 'Message Timestamp',
|
||||
name: 'timestamp',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
description: 'Timestamp of the message to delete',
|
||||
placeholder: '1663233118.856619',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* star:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['star'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results 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: 'Max number of results to return',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,303 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const userOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'info',
|
||||
description: 'Get information about a user',
|
||||
action: 'Get information about a user',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get a list of many users',
|
||||
action: 'Get many users',
|
||||
},
|
||||
{
|
||||
name: "Get User's Profile",
|
||||
value: 'getProfile',
|
||||
description: "Get a user's profile",
|
||||
action: "Get a user's profile",
|
||||
},
|
||||
{
|
||||
name: "Get User's Status",
|
||||
value: 'getPresence',
|
||||
description: 'Get online status of a user',
|
||||
action: "Get a user's presence status",
|
||||
},
|
||||
{
|
||||
name: "Update User's Profile",
|
||||
value: 'updateProfile',
|
||||
description: "Update a user's profile",
|
||||
action: "Update a user's profile",
|
||||
},
|
||||
],
|
||||
default: 'info',
|
||||
},
|
||||
];
|
||||
|
||||
export const userFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:info */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User',
|
||||
name: 'user',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a user...',
|
||||
description: 'The ID of the user to get information about',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['info', 'getProfile'],
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
placeholder: 'Select a user...',
|
||||
typeOptions: {
|
||||
searchListMethod: 'getUsers',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '[a-zA-Z0-9]{2,}',
|
||||
errorMessage: 'Not a valid Slack User ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'U123AB45JGM',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getAll'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:getPresence */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User',
|
||||
name: 'user',
|
||||
type: 'resourceLocator',
|
||||
default: { mode: 'list', value: '' },
|
||||
placeholder: 'Select a user...',
|
||||
description: 'The ID of the user to get the online status of',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getPresence'],
|
||||
resource: ['user'],
|
||||
},
|
||||
},
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
placeholder: 'Select a user...',
|
||||
typeOptions: {
|
||||
searchListMethod: 'getUsers',
|
||||
searchable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '[a-zA-Z0-9]{2,}',
|
||||
errorMessage: 'Not a valid Slack User ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'U123AB45JGM',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:update user profile */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['updateProfile'],
|
||||
},
|
||||
},
|
||||
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 Name or ID',
|
||||
name: 'id',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getTeamFields',
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'ID of the field to set. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
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',
|
||||
placeholder: 'name@email.com',
|
||||
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: 'Set Status',
|
||||
name: 'status',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Set Status',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Set Status',
|
||||
name: 'set_status',
|
||||
values: [
|
||||
{
|
||||
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:
|
||||
'The number of minutes to wait until this status expires and is cleared. Optional.',
|
||||
},
|
||||
{
|
||||
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.',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,435 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const userGroupOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add Users',
|
||||
value: 'updateUsers',
|
||||
action: 'Add users to a user group',
|
||||
},
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
action: 'Create a user group',
|
||||
},
|
||||
{
|
||||
name: 'Disable',
|
||||
value: 'disable',
|
||||
action: 'Disable a user group',
|
||||
},
|
||||
{
|
||||
name: 'Enable',
|
||||
value: 'enable',
|
||||
action: 'Enable a user group',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
action: 'Get many user groups',
|
||||
},
|
||||
{
|
||||
name: 'Get Users',
|
||||
value: 'getUsers',
|
||||
action: 'Get users from a user group',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
action: 'Update a user group',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
export const userGroupFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* userGroup:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['create'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'A name for the User Group. Must be unique among User Groups.',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'Options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['create'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Names or IDs',
|
||||
name: 'channelIds',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'A comma-separated string of encoded channel IDs for which the User Group uses as a default. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A short description of the User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Handle',
|
||||
name: 'handle',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A mention handle. Must be unique among channels, users and User Groups.',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* userGroup:disable */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User Group ID',
|
||||
name: 'userGroupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['disable'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The encoded ID of the User Group to update',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['disable'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* userGroup:enable */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User Group ID',
|
||||
name: 'userGroupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['enable'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The encoded ID of the User Group to update',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'option',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['enable'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* userGroup:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getAll'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getAll'],
|
||||
resource: ['userGroup'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 500,
|
||||
},
|
||||
default: 100,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Disabled',
|
||||
name: 'include_disabled',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include disabled User Groups',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Users',
|
||||
name: 'include_users',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the list of users for each User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* userGroup:update */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User Group ID',
|
||||
name: 'userGroupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['update'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The encoded ID of the User Group to update',
|
||||
},
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['update'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Channel Names or IDs',
|
||||
name: 'channels',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChannels',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'A comma-separated string of encoded channel IDs for which the User Group uses as a default. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A short description of the User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Handle',
|
||||
name: 'handle',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A mention handle. Must be unique among channels, users and User Groups.',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in each User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'A name for the User Group. Must be unique among User Groups.',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* userGroup:updateUsers */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User Group ID',
|
||||
name: 'userGroupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['updateUsers'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The encoded ID of the User Group to update',
|
||||
},
|
||||
{
|
||||
displayName: 'User Names or IDs',
|
||||
name: 'users',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
default: [],
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['updateUsers'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description:
|
||||
'Users to add to the User Group. Existing users will be preserved. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['updateUsers'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Include Count',
|
||||
name: 'include_count',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to include the number of users in the User Group',
|
||||
},
|
||||
],
|
||||
},
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* userGroup:getUsers */
|
||||
/* ----------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'User Group ID',
|
||||
name: 'userGroupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['getUsers'],
|
||||
resource: ['userGroup'],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The encoded ID of the User Group',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['userGroup'],
|
||||
operation: ['getUsers'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Resolve Data',
|
||||
name: 'resolveData',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to return full user objects instead of just user IDs',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,53 @@
|
||||
/* eslint-disable n8n-nodes-base/node-param-default-missing */
|
||||
import type { INodePropertyMode } from 'n8n-workflow';
|
||||
|
||||
export const slackChannelModes: INodePropertyMode[] = [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
placeholder: 'Select a channel...',
|
||||
typeOptions: {
|
||||
searchListMethod: 'getChannels',
|
||||
searchable: true,
|
||||
slowLoadNotice: {
|
||||
message: 'If loading is slow, try selecting a channel using "By ID" or "By URL"',
|
||||
timeout: 15000,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'By ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: '[a-zA-Z0-9]{2,}',
|
||||
errorMessage: 'Not a valid Slack Channel ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
placeholder: 'C0122KQ70S7E',
|
||||
},
|
||||
{
|
||||
displayName: 'By URL',
|
||||
name: 'url',
|
||||
type: 'string',
|
||||
placeholder: 'https://app.slack.com/client/TS9594PZK/B0556F47Z3A',
|
||||
validation: [
|
||||
{
|
||||
type: 'regex',
|
||||
properties: {
|
||||
regex: 'http(s)?://app.slack.com/client/.*/([a-zA-Z0-9]{2,})',
|
||||
errorMessage: 'Not a valid Slack Channel URL',
|
||||
},
|
||||
},
|
||||
],
|
||||
extractValue: {
|
||||
type: 'regex',
|
||||
regex: 'https://app.slack.com/client/.*/([a-zA-Z0-9]{2,})',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"context_team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer"
|
||||
},
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_channel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_general": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_group": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_im": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_member": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_mpim": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_org_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_pending_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_private": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"last_read": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent_conversation": {
|
||||
"type": "null"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer"
|
||||
},
|
||||
"purpose": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shared_team_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinked": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"version": 2
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"context_team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer"
|
||||
},
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_channel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_general": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_group": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_im": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_member": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_mpim": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_org_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_pending_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_private": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"last_read": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent_conversation": {
|
||||
"type": "null"
|
||||
},
|
||||
"previous_names": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canvas": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_empty": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"quip_thread_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabz": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"purpose": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shared_team_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinked": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"context_team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer"
|
||||
},
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_channel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_general": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_group": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_im": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_member": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_mpim": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_org_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_pending_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_private": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"num_members": {
|
||||
"type": "integer"
|
||||
},
|
||||
"parent_conversation": {
|
||||
"type": "null"
|
||||
},
|
||||
"previous_names": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canvas": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_empty": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"quip_thread_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabz": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"use_case": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"purpose": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shared_team_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinked": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"blocks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"style": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bold": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"italic": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bot_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"client_msg_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"team": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"ts": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 8
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"context_team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer"
|
||||
},
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_channel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_general": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_group": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_im": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_member": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_mpim": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_org_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_pending_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_private": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent_conversation": {
|
||||
"type": "null"
|
||||
},
|
||||
"previous_names": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"purpose": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shared_team_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinked": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"version": 3
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"member": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 3
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"blocks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bot_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot_profile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"icons": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image_36": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_48": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_72": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"client_msg_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_locked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"latest_reply": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent_user_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"reply_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"reply_users": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"reply_users_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"subscribed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"team": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"thread_ts": {
|
||||
"type": "string"
|
||||
},
|
||||
"ts": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 3
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"context_team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer"
|
||||
},
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_channel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_general": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_group": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_im": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_member": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_mpim": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_org_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_pending_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_private": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent_conversation": {
|
||||
"type": "null"
|
||||
},
|
||||
"previous_names": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"purpose": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shared_team_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"last_set": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlinked": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aac": {
|
||||
"type": "string"
|
||||
},
|
||||
"audio_wave_samples": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"channels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"comments_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer"
|
||||
},
|
||||
"display_as_bot": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"duration_ms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"editable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"external_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"file_access": {
|
||||
"type": "string"
|
||||
},
|
||||
"filetype": {
|
||||
"type": "string"
|
||||
},
|
||||
"groups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"has_more_shares": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"has_rich_preview": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"ims": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"is_external": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_public": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_starred": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"media_display_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"mimetype": {
|
||||
"type": "string"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"permalink": {
|
||||
"type": "string"
|
||||
},
|
||||
"permalink_public": {
|
||||
"type": "string"
|
||||
},
|
||||
"pretty_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_url_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"subtype": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"transcription": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url_private": {
|
||||
"type": "string"
|
||||
},
|
||||
"url_private_download": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_team": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"comments_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer"
|
||||
},
|
||||
"display_as_bot": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"editable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"external_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"filetype": {
|
||||
"type": "string"
|
||||
},
|
||||
"groups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"ims": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"is_external": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_public": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"media_display_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"mimetype": {
|
||||
"type": "string"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"permalink": {
|
||||
"type": "string"
|
||||
},
|
||||
"pretty_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_url_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"url_private": {
|
||||
"type": "string"
|
||||
},
|
||||
"url_private_download": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_team": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"comments_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"created": {
|
||||
"type": "integer"
|
||||
},
|
||||
"display_as_bot": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"editable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"external_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"file_access": {
|
||||
"type": "string"
|
||||
},
|
||||
"filetype": {
|
||||
"type": "string"
|
||||
},
|
||||
"groups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"has_more_shares": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"has_rich_preview": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_external": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_public": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_starred": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"media_display_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"mimetype": {
|
||||
"type": "string"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"permalink": {
|
||||
"type": "string"
|
||||
},
|
||||
"permalink_public": {
|
||||
"type": "string"
|
||||
},
|
||||
"pretty_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_url_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"url_private": {
|
||||
"type": "string"
|
||||
},
|
||||
"url_private_download": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_team": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"message_timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"permalink": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"blocks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"style": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"italic": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bot_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot_profile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"icons": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image_36": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_48": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_72": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"team": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"ts": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"message_timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"version": 3
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"blocks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"channel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_channel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_group": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_im": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_mpim": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_org_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_pending_ext_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_private": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"iid": {
|
||||
"type": "string"
|
||||
},
|
||||
"no_reactions": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"permalink": {
|
||||
"type": "string"
|
||||
},
|
||||
"team": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"ts": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"approved": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"blocks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"style": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bold": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"italic": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"verbatim": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bot_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot_profile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"icons": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image_36": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_48": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_72": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edited": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ts": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"team": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"message_timestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"blocks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"block_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"client_msg_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"permalink": {
|
||||
"type": "string"
|
||||
},
|
||||
"reactions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"users": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"team": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"ts": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_admin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_app_user": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_bot": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_email_confirmed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_owner": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_primary_owner": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_restricted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_ultra_restricted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"profile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"always_active": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"avatar_hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"huddle_state": {
|
||||
"type": "string"
|
||||
},
|
||||
"huddle_state_expiration_ts": {
|
||||
"type": "integer"
|
||||
},
|
||||
"image_1024": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_192": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_24": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_32": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_48": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_512": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_72": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_original": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_custom_image": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone": {
|
||||
"type": "string"
|
||||
},
|
||||
"real_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"real_name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"skype": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_emoji": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_emoji_display_info": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"emoji_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"unicode": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"status_expiration": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status_text": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_text_canonical": {
|
||||
"type": "string"
|
||||
},
|
||||
"team": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"real_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"tz": {
|
||||
"type": "string"
|
||||
},
|
||||
"tz_label": {
|
||||
"type": "string"
|
||||
},
|
||||
"tz_offset": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
},
|
||||
"who_can_share_contact_card": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"presence": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatar_hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_1024": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_192": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_24": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_32": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_48": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_512": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_72": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_original": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_custom_image": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone": {
|
||||
"type": "string"
|
||||
},
|
||||
"real_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"real_name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"skype": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_emoji": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_emoji_display_info": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"emoji_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"unicode": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"status_expiration": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status_text": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_text_canonical": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_admin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_app_user": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_bot": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_email_confirmed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_owner": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_primary_owner": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_restricted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_ultra_restricted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"profile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatar_hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"huddle_state": {
|
||||
"type": "string"
|
||||
},
|
||||
"huddle_state_expiration_ts": {
|
||||
"type": "integer"
|
||||
},
|
||||
"image_1024": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_192": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_24": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_32": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_48": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_512": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_72": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_original": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_custom_image": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone": {
|
||||
"type": "string"
|
||||
},
|
||||
"real_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"real_name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"skype": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_emoji": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_emoji_display_info": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"emoji_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"unicode": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"status_expiration": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status_text": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_text_canonical": {
|
||||
"type": "string"
|
||||
},
|
||||
"team": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"real_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"team_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"tz": {
|
||||
"type": "string"
|
||||
},
|
||||
"tz_label": {
|
||||
"type": "string"
|
||||
},
|
||||
"tz_offset": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updated": {
|
||||
"type": "integer"
|
||||
},
|
||||
"who_can_share_contact_card": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatar_hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"display_name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Xf03UMDX9738": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf03UMDX98DC": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf03UR35L6MT": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf03UR35L7K7": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf03UTUL86LB": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf03UU2E8W4S": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf03UWFR6VJQ": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf03V6LREDCZ": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf0403NW3YCQ": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf05GWG5K0S0": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf08BT55JWBG": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf08BWUCR1LM": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Xf08FLSF1456": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"huddle_state": {
|
||||
"type": "string"
|
||||
},
|
||||
"huddle_state_expiration_ts": {
|
||||
"type": "integer"
|
||||
},
|
||||
"image_1024": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_192": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_24": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_32": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_48": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_512": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_72": {
|
||||
"type": "string"
|
||||
},
|
||||
"image_original": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_custom_image": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone": {
|
||||
"type": "string"
|
||||
},
|
||||
"real_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"real_name_normalized": {
|
||||
"type": "string"
|
||||
},
|
||||
"skype": {
|
||||
"type": "string"
|
||||
},
|
||||
"start_date": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_emoji": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_emoji_display_info": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"display_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"emoji_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"unicode": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"status_expiration": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status_text": {
|
||||
"type": "string"
|
||||
},
|
||||
"status_text_canonical": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 150.852 150.852"><use xlink:href="#a" x=".926" y=".926"/><symbol id="a" overflow="visible"><g stroke-width="1.852"><path fill="#e01e5a" stroke="#e01e5a" d="M40.741 93.55c0-8.735 6.607-15.772 14.815-15.772s14.815 7.037 14.815 15.772v38.824c0 8.737-6.607 15.774-14.815 15.774s-14.815-7.037-14.815-15.772z"/><path fill="#ecb22d" stroke="#ecb22d" d="M93.55 107.408c-8.735 0-15.772-6.607-15.772-14.815s7.037-14.815 15.772-14.815h38.826c8.735 0 15.772 6.607 15.772 14.815s-7.037 14.815-15.772 14.815z"/><path fill="#2fb67c" stroke="#2fb67c" d="M77.778 15.772C77.778 7.037 84.385 0 92.593 0s14.815 7.037 14.815 15.772v38.826c0 8.735-6.607 15.772-14.815 15.772s-14.815-7.037-14.815-15.772z"/><path fill="#36c5f1" stroke="#36c5f1" d="M15.772 70.371C7.037 70.371 0 63.763 0 55.556s7.037-14.815 15.772-14.815h38.826c8.735 0 15.772 6.607 15.772 14.815s-7.037 14.815-15.772 14.815z"/><g stroke-linejoin="miter"><path fill="#ecb22d" stroke="#ecb22d" d="M77.778 133.333c0 8.208 6.607 14.815 14.815 14.815s14.815-6.607 14.815-14.815-6.607-14.815-14.815-14.815H77.778z"/><path fill="#2fb67c" stroke="#2fb67c" d="M133.334 70.371h-14.815V55.556c0-8.207 6.607-14.815 14.815-14.815s14.815 6.607 14.815 14.815-6.607 14.815-14.815 14.815z"/><path fill="#e01e5a" stroke="#e01e5a" d="M14.815 77.778H29.63v14.815c0 8.207-6.607 14.815-14.815 14.815S0 100.8 0 92.593s6.607-14.815 14.815-14.815z"/><path fill="#36c5f1" stroke="#36c5f1" d="M70.371 14.815V29.63H55.556c-8.207 0-14.815-6.607-14.815-14.815S47.348 0 55.556 0s14.815 6.607 14.815 14.815z"/></g></g></symbol></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,433 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { IWebhookFunctions, INodeType } from 'n8n-workflow';
|
||||
|
||||
import { SlackTrigger } from '../SlackTrigger.node';
|
||||
|
||||
// Mock the helper functions
|
||||
jest.mock('../SlackTriggerHelpers', () => ({
|
||||
verifySignature: jest.fn().mockResolvedValue(true),
|
||||
getChannelInfo: jest.fn().mockResolvedValue({ id: 'C123', name: 'test-channel' }),
|
||||
getUserInfo: jest.fn().mockResolvedValue({ id: 'U123', name: 'test-user' }),
|
||||
downloadFile: jest.fn().mockResolvedValue(Buffer.from('test file content')),
|
||||
}));
|
||||
|
||||
describe('SlackTrigger Node', () => {
|
||||
let slackTrigger: INodeType;
|
||||
let mockWebhookFunctions: ReturnType<typeof mock<IWebhookFunctions>>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
slackTrigger = new SlackTrigger();
|
||||
mockWebhookFunctions = mock<IWebhookFunctions>();
|
||||
|
||||
// Mock helpers
|
||||
mockWebhookFunctions.helpers = {
|
||||
prepareBinaryData: jest.fn().mockResolvedValue({
|
||||
data: 'binary-data',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'test.txt',
|
||||
}),
|
||||
} as any;
|
||||
|
||||
// Default mock setup
|
||||
mockWebhookFunctions.getNodeParameter.mockImplementation(
|
||||
(paramName: string, defaultValue?: any) => {
|
||||
switch (paramName) {
|
||||
case 'trigger':
|
||||
return ['file_share'];
|
||||
case 'watchWorkspace':
|
||||
return false;
|
||||
case 'channelId':
|
||||
return 'C123';
|
||||
case 'downloadFiles':
|
||||
return false;
|
||||
case 'options':
|
||||
return {};
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue({
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
json: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
} as any);
|
||||
});
|
||||
|
||||
describe('webhook method - eventChannel extraction', () => {
|
||||
it('should extract eventChannel from req.body.event.channel when available', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'message',
|
||||
channel: 'C123',
|
||||
user: 'U456',
|
||||
text: 'Hello world',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
});
|
||||
|
||||
it('should extract eventChannel from req.body.event.item.channel when event.channel is not available', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'reaction_added',
|
||||
user: 'U456',
|
||||
item: {
|
||||
channel: 'C123',
|
||||
ts: '1234567890.123456',
|
||||
},
|
||||
reaction: 'thumbsup',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'trigger':
|
||||
return ['reaction_added'];
|
||||
case 'watchWorkspace':
|
||||
return false;
|
||||
case 'channelId':
|
||||
return 'C123';
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
});
|
||||
|
||||
it('should handle when req.body.event.item is undefined/null without throwing error', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'reaction_added',
|
||||
user: 'U456',
|
||||
item: null, // This could cause the original error
|
||||
reaction: 'thumbsup',
|
||||
channel_id: 'C123',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'trigger':
|
||||
return ['reaction_added'];
|
||||
case 'watchWorkspace':
|
||||
return false;
|
||||
case 'channelId':
|
||||
return 'C123';
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
// This should not throw an error
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
});
|
||||
|
||||
it('should fallback to req.body.event.channel_id when channel and item.channel are not available', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'message',
|
||||
user: 'U456',
|
||||
text: 'Hello world',
|
||||
channel_id: 'C123', // Fallback value
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
});
|
||||
|
||||
it('should handle file_share event with undefined item gracefully', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'message',
|
||||
subtype: 'file_share',
|
||||
user: 'U456',
|
||||
channel: 'C123',
|
||||
files: [
|
||||
{
|
||||
id: 'F123',
|
||||
name: 'test.txt',
|
||||
url_private_download: 'https://files.slack.com/test.txt',
|
||||
mimetype: 'text/plain',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
});
|
||||
|
||||
it('should handle complex event structure without throwing errors', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'app_mention',
|
||||
user: 'U456',
|
||||
text: '<@U123> hello there',
|
||||
// No channel, item, or channel_id - edge case
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'trigger':
|
||||
return ['app_mention'];
|
||||
case 'watchWorkspace':
|
||||
return true; // Watch whole workspace, so channel check should be skipped
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
});
|
||||
});
|
||||
|
||||
describe('webhook method - file_share event handling', () => {
|
||||
it('should handle file_share event when item is undefined', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'message',
|
||||
subtype: 'file_share',
|
||||
user: 'U456',
|
||||
channel: 'C123',
|
||||
files: [
|
||||
{
|
||||
id: 'F123',
|
||||
name: 'test.txt',
|
||||
url_private_download: 'https://files.slack.com/test.txt',
|
||||
mimetype: 'text/plain',
|
||||
},
|
||||
],
|
||||
item: undefined, // This was causing the original error
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'trigger':
|
||||
return ['file_share'];
|
||||
case 'downloadFiles':
|
||||
return true;
|
||||
case 'watchWorkspace':
|
||||
return false;
|
||||
case 'channelId':
|
||||
return 'C123';
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
expect(result.workflowData![0][0].binary).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle file_share event when item is null', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'message',
|
||||
subtype: 'file_share',
|
||||
user: 'U456',
|
||||
channel: 'C123',
|
||||
files: [
|
||||
{
|
||||
id: 'F123',
|
||||
name: 'test.txt',
|
||||
url_private_download: 'https://files.slack.com/test.txt',
|
||||
mimetype: 'text/plain',
|
||||
},
|
||||
],
|
||||
item: null, // Another potential error case
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'trigger':
|
||||
return ['file_share'];
|
||||
case 'downloadFiles':
|
||||
return false;
|
||||
case 'watchWorkspace':
|
||||
return false;
|
||||
case 'channelId':
|
||||
return 'C123';
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
});
|
||||
|
||||
it('should handle file_share event with valid item.channel', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'message',
|
||||
subtype: 'file_share',
|
||||
user: 'U456',
|
||||
files: [
|
||||
{
|
||||
id: 'F123',
|
||||
name: 'test.txt',
|
||||
url_private_download: 'https://files.slack.com/test.txt',
|
||||
mimetype: 'text/plain',
|
||||
},
|
||||
],
|
||||
item: {
|
||||
channel: 'C123',
|
||||
ts: '1234567890.123456',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'trigger':
|
||||
return ['file_share'];
|
||||
case 'downloadFiles':
|
||||
return false;
|
||||
case 'watchWorkspace':
|
||||
return false;
|
||||
case 'channelId':
|
||||
return 'C123';
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.workflowData).toBeDefined();
|
||||
expect(result.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
});
|
||||
});
|
||||
|
||||
describe('webhook method - other event scenarios', () => {
|
||||
it('should handle team_join event (no channel extraction needed)', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'event_callback',
|
||||
event: {
|
||||
type: 'team_join',
|
||||
user: {
|
||||
id: 'U789',
|
||||
name: 'newuser',
|
||||
real_name: 'New User',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
|
||||
let resolveIds = false;
|
||||
mockWebhookFunctions.getNodeParameter.mockImplementation((paramName: string) => {
|
||||
switch (paramName) {
|
||||
case 'trigger':
|
||||
return ['team_join'];
|
||||
case 'options':
|
||||
return resolveIds ? { resolveIds: true } : {};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
resolveIds = false;
|
||||
const resultNoResolve = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
expect(resultNoResolve.workflowData).toBeDefined();
|
||||
expect(resultNoResolve.workflowData![0][0].json).toEqual(mockRequest.body.event);
|
||||
|
||||
resolveIds = true;
|
||||
const resultResolve = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
expect(resultResolve.workflowData).toBeDefined();
|
||||
const out = resultResolve.workflowData![0][0].json as { user_resolved?: string };
|
||||
expect(out.user_resolved).toBe(mockRequest.body.event.user.name);
|
||||
});
|
||||
|
||||
it('should handle url_verification challenge', async () => {
|
||||
const mockRequest = {
|
||||
body: {
|
||||
type: 'url_verification',
|
||||
challenge: 'test_challenge_123',
|
||||
},
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest as any);
|
||||
|
||||
const result = await slackTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
expect(result.noWebhookResponse).toBe(true);
|
||||
expect(mockWebhookFunctions.getResponseObject().status).toHaveBeenCalledWith(200);
|
||||
expect(mockWebhookFunctions.getResponseObject().json).toHaveBeenCalledWith({
|
||||
challenge: 'test_challenge_123',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,196 @@
|
||||
import { createHmac, timingSafeEqual } from 'crypto';
|
||||
import { verifySignature } from '../SlackTriggerHelpers';
|
||||
|
||||
jest.mock('crypto', () => ({
|
||||
...jest.requireActual('crypto'),
|
||||
createHmac: jest.fn().mockReturnValue({
|
||||
update: jest.fn().mockReturnThis(),
|
||||
digest: jest
|
||||
.fn()
|
||||
.mockReturnValue('a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503'),
|
||||
}),
|
||||
timingSafeEqual: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('SlackTriggerHelpers', () => {
|
||||
let mockWebhookFunctions: any;
|
||||
const testSignatureSecret = 'xyzz0WbapA4vBCDEFasx0q6G';
|
||||
const testTimestamp = '1531420618';
|
||||
const testBody =
|
||||
'token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c';
|
||||
const testSignature = 'v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Mock Date.now() to return a fixed timestamp
|
||||
const fixedDate = new Date(parseInt(testTimestamp, 10) * 1000);
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => fixedDate.getTime());
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getCredentials: jest.fn(),
|
||||
getRequestObject: jest.fn(),
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Slack Trigger' }),
|
||||
};
|
||||
|
||||
// Default mock return values
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
if (header === 'x-slack-signature') return testSignature;
|
||||
if (header === 'x-slack-request-timestamp') return testTimestamp;
|
||||
return null;
|
||||
}),
|
||||
rawBody: testBody,
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifySignature', () => {
|
||||
it('should return true when no credentials are provided', async () => {
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({});
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockWebhookFunctions.getCredentials).toHaveBeenCalledWith('slackApi');
|
||||
});
|
||||
|
||||
it('should return true when no signature secret is provided', async () => {
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
apiToken: 'test-token',
|
||||
});
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockWebhookFunctions.getCredentials).toHaveBeenCalledWith('slackApi');
|
||||
});
|
||||
|
||||
it('should return false when signature header is missing', async () => {
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
signatureSecret: testSignatureSecret,
|
||||
});
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
if (header === 'x-slack-request-timestamp') return testTimestamp;
|
||||
return null;
|
||||
}),
|
||||
rawBody: testBody,
|
||||
});
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when timestamp header is missing', async () => {
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
signatureSecret: testSignatureSecret,
|
||||
});
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
if (header === 'x-slack-signature') return testSignature;
|
||||
return null;
|
||||
}),
|
||||
rawBody: testBody,
|
||||
});
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when timestamp is too old', async () => {
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
signatureSecret: testSignatureSecret,
|
||||
});
|
||||
|
||||
// Mock Date.now() to return a timestamp that's more than 5 minutes after the request timestamp
|
||||
const futureDate = new Date((parseInt(testTimestamp, 10) + 301) * 1000);
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => futureDate.getTime());
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when signature is valid', async () => {
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
signatureSecret: testSignatureSecret,
|
||||
});
|
||||
|
||||
// Mock the timingSafeEqual to return true for valid signature
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(true);
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(createHmac).toHaveBeenCalledWith('sha256', testSignatureSecret);
|
||||
expect(timingSafeEqual).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return false when signature is invalid', async () => {
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
signatureSecret: testSignatureSecret,
|
||||
});
|
||||
|
||||
// Mock the timingSafeEqual to return false for invalid signature
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(false);
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(createHmac).toHaveBeenCalledWith('sha256', testSignatureSecret);
|
||||
expect(timingSafeEqual).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should correctly build the signature string with the provided timestamp', async () => {
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
signatureSecret: testSignatureSecret,
|
||||
});
|
||||
|
||||
await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
expect(createHmac).toHaveBeenCalledWith('sha256', testSignatureSecret);
|
||||
const mockHmac = createHmac('sha256', testSignatureSecret);
|
||||
|
||||
// Verify that update was called with the expected string (using the timestamp from the request)
|
||||
expect(mockHmac.update).toHaveBeenCalledWith(`v0:${testTimestamp}:${testBody}`);
|
||||
});
|
||||
|
||||
it('should verify timestamp even if signature secret is not set', async () => {
|
||||
// No signature secret in credentials
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
apiToken: 'test-token',
|
||||
});
|
||||
|
||||
// Mock Date.now() to return a timestamp that's more than 5 minutes after the request timestamp
|
||||
const futureDate = new Date((parseInt(testTimestamp, 10) + 301) * 1000);
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => futureDate.getTime());
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
// Should return false because timestamp is too old, even though signature secret is not set
|
||||
expect(result).toBe(false);
|
||||
expect(mockWebhookFunctions.getCredentials).toHaveBeenCalledWith('slackApi');
|
||||
});
|
||||
|
||||
it('should return true when timestamp is valid even if signature secret is not set', async () => {
|
||||
// No signature secret in credentials
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
apiToken: 'test-token',
|
||||
});
|
||||
|
||||
// Keep Date.now() at the same time as the request timestamp (within 5 minute window)
|
||||
const fixedDate = new Date(parseInt(testTimestamp, 10) * 1000);
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => fixedDate.getTime());
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
// Should return true because timestamp is valid and signature secret is not required
|
||||
expect(result).toBe(true);
|
||||
expect(mockWebhookFunctions.getCredentials).toHaveBeenCalledWith('slackApi');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,161 @@
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
|
||||
import { slackApiRequest, slackApiRequestAllItems, validateJSON } from '../../V1/GenericFunctions';
|
||||
|
||||
jest.mock('n8n-workflow', () => ({
|
||||
...jest.requireActual('n8n-workflow'),
|
||||
NodeApiError: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('Slack V1 > GenericFunctions', () => {
|
||||
let mockExecuteFunctions: IExecuteFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockExecuteFunctions = {
|
||||
helpers: {
|
||||
requestWithAuthentication: jest.fn(),
|
||||
},
|
||||
getNode: jest.fn().mockReturnValue({ type: 'n8n-nodes-base.slack', typeVersion: 1 }),
|
||||
getNodeParameter: jest.fn().mockReturnValue('accessToken'),
|
||||
} as unknown as IExecuteFunctions;
|
||||
});
|
||||
|
||||
describe('slackApiRequest', () => {
|
||||
it('should handle successful response', async () => {
|
||||
const mockResponse = { ok: true, data: 'testData' };
|
||||
mockExecuteFunctions.helpers.requestWithAuthentication = jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await slackApiRequest.call(mockExecuteFunctions, 'GET', '/test');
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
it('should use OAuth2 credentials when authentication is oAuth2', async () => {
|
||||
mockExecuteFunctions.getNodeParameter = jest.fn().mockReturnValue('oAuth2');
|
||||
const mockResponse = { ok: true };
|
||||
mockExecuteFunctions.helpers.requestWithAuthentication = jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockResponse);
|
||||
|
||||
await slackApiRequest.call(mockExecuteFunctions, 'GET', '/test');
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'slackOAuth2Api',
|
||||
expect.any(Object),
|
||||
expect.objectContaining({
|
||||
oauth2: expect.any(Object),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('slackApiRequestAllItems', () => {
|
||||
it('should use default limit of 100 when no limit is provided', async () => {
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
channels: [{ id: 'ch1' }],
|
||||
response_metadata: { next_cursor: undefined },
|
||||
};
|
||||
|
||||
mockExecuteFunctions.helpers.requestWithAuthentication = jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockResponse);
|
||||
|
||||
await slackApiRequestAllItems.bind(mockExecuteFunctions)(
|
||||
'channels',
|
||||
'GET',
|
||||
'conversations.list',
|
||||
);
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'slackApi',
|
||||
expect.objectContaining({
|
||||
headers: expect.any(Object),
|
||||
qs: {
|
||||
limit: 100,
|
||||
page: 2,
|
||||
cursor: undefined,
|
||||
},
|
||||
json: true,
|
||||
method: 'GET',
|
||||
uri: expect.any(String),
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('should use count instead of limit for files.list endpoint', async () => {
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
files: [{ id: 'file1' }],
|
||||
response_metadata: { next_cursor: undefined },
|
||||
};
|
||||
|
||||
mockExecuteFunctions.helpers.requestWithAuthentication = jest
|
||||
.fn()
|
||||
.mockResolvedValue(mockResponse);
|
||||
|
||||
await slackApiRequestAllItems.call(mockExecuteFunctions, 'files', 'GET', 'files.list');
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'slackApi',
|
||||
expect.objectContaining({
|
||||
headers: expect.any(Object),
|
||||
qs: {
|
||||
count: 100,
|
||||
page: 2,
|
||||
cursor: undefined,
|
||||
},
|
||||
json: true,
|
||||
method: 'GET',
|
||||
uri: expect.any(String),
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle pagination with response_metadata next_cursor', async () => {
|
||||
const responses = [
|
||||
{
|
||||
ok: true,
|
||||
channels: [{ id: '1' }],
|
||||
response_metadata: { next_cursor: 'cursor1' },
|
||||
},
|
||||
{
|
||||
ok: true,
|
||||
channels: [{ id: '2' }],
|
||||
response_metadata: { next_cursor: '' },
|
||||
},
|
||||
];
|
||||
|
||||
mockExecuteFunctions.helpers.requestWithAuthentication = jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => responses[0])
|
||||
.mockImplementationOnce(() => responses[1]);
|
||||
|
||||
const result = await slackApiRequestAllItems.call(
|
||||
mockExecuteFunctions,
|
||||
'channels',
|
||||
'GET',
|
||||
'conversations.list',
|
||||
);
|
||||
|
||||
expect(result).toEqual([{ id: '1' }, { id: '2' }]);
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateJSON', () => {
|
||||
it('should return undefined for invalid JSON', () => {
|
||||
const result = validateJSON('{invalid:json}');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return parsed object for valid JSON', () => {
|
||||
const result = validateJSON('{"key":"value"}');
|
||||
expect(result).toEqual({ key: 'value' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
channel: {
|
||||
id: 'C085WNEHP4Y',
|
||||
name: 'test-channel',
|
||||
is_channel: true,
|
||||
is_group: false,
|
||||
is_im: false,
|
||||
is_mpim: false,
|
||||
is_private: false,
|
||||
created: 1734325731,
|
||||
is_archived: false,
|
||||
is_general: false,
|
||||
unlinked: 0,
|
||||
name_normalized: 'test-channel',
|
||||
is_shared: false,
|
||||
is_org_shared: false,
|
||||
is_pending_ext_shared: false,
|
||||
pending_shared: [],
|
||||
context_team_id: 'T0364MSFHV2',
|
||||
creator: 'U0362BXQYJW',
|
||||
is_ext_shared: false,
|
||||
shared_team_ids: ['T0364MSFHV2'],
|
||||
pending_connected_team_ids: [],
|
||||
is_member: true,
|
||||
topic: {
|
||||
value: '',
|
||||
creator: '',
|
||||
last_set: 0,
|
||||
},
|
||||
purpose: {
|
||||
value: '',
|
||||
creator: '',
|
||||
last_set: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV1, channel => create', () => {
|
||||
const slackNock = nock('https://slack.com')
|
||||
.post('/api/conversations.create', {
|
||||
name: 'test-channel',
|
||||
})
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
afterAll(() => slackNock.done());
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['create.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking 'Test workflow'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"authentication": "accessToken",
|
||||
"resource": "channel",
|
||||
"operation": "create",
|
||||
"channelId": "test-channel",
|
||||
"additionalFields": {
|
||||
"isPrivate": false
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 1,
|
||||
"position": [1040, 360],
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": "C085WNEHP4Y",
|
||||
"name": "test-channel",
|
||||
"is_channel": true,
|
||||
"is_group": false,
|
||||
"is_im": false,
|
||||
"is_mpim": false,
|
||||
"is_private": false,
|
||||
"created": 1734325731,
|
||||
"is_archived": false,
|
||||
"is_general": false,
|
||||
"unlinked": 0,
|
||||
"name_normalized": "test-channel",
|
||||
"is_shared": false,
|
||||
"is_org_shared": false,
|
||||
"is_pending_ext_shared": false,
|
||||
"pending_shared": [],
|
||||
"context_team_id": "T0364MSFHV2",
|
||||
"creator": "U0362BXQYJW",
|
||||
"is_ext_shared": false,
|
||||
"shared_team_ids": ["T0364MSFHV2"],
|
||||
"pending_connected_team_ids": [],
|
||||
"is_member": true,
|
||||
"topic": {
|
||||
"value": "",
|
||||
"creator": "",
|
||||
"last_set": 0
|
||||
},
|
||||
"purpose": {
|
||||
"value": "",
|
||||
"creator": "",
|
||||
"last_set": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Test workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
channel: 'C08514ZPKB8',
|
||||
message: {
|
||||
user: 'U0362BXQYJW',
|
||||
type: 'message',
|
||||
ts: '1734322671.726339',
|
||||
bot_id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
text: 'test message',
|
||||
team: 'T0364MSFHV2',
|
||||
bot_profile: {
|
||||
id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
name: 'blocks-test',
|
||||
icons: {
|
||||
image_36: 'https://a.slack-edge.com/80588/img/plugins/app/bot_36.png',
|
||||
image_48: 'https://a.slack-edge.com/80588/img/plugins/app/bot_48.png',
|
||||
image_72: 'https://a.slack-edge.com/80588/img/plugins/app/service_72.png',
|
||||
},
|
||||
deleted: false,
|
||||
updated: 1648028754,
|
||||
team_id: 'T0364MSFHV2',
|
||||
},
|
||||
},
|
||||
message_timestamp: '1734322671.726339',
|
||||
};
|
||||
|
||||
describe('Test SlackV1, message => post', () => {
|
||||
const slackNock = nock('https://slack.com')
|
||||
.post('/api/chat.postMessage', {
|
||||
channel: 'C08514ZPKB8',
|
||||
text: 'test message',
|
||||
attachments: [],
|
||||
icon_emoji: '😁',
|
||||
link_names: true,
|
||||
mrkdwn: true,
|
||||
unfurl_links: true,
|
||||
unfurl_media: true,
|
||||
})
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
afterAll(() => slackNock.done());
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['post.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking 'Test workflow'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"authentication": "accessToken",
|
||||
"resource": "message",
|
||||
"operation": "post",
|
||||
"channel": "C08514ZPKB8",
|
||||
"text": "test message",
|
||||
"otherOptions": {
|
||||
"icon_emoji": "😁",
|
||||
"link_names": true,
|
||||
"mrkdwn": true,
|
||||
"unfurl_links": true,
|
||||
"unfurl_media": true
|
||||
},
|
||||
"jsonParameters": false
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 1,
|
||||
"position": [1040, 360],
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"ok": true,
|
||||
"channel": "C08514ZPKB8",
|
||||
"message": {
|
||||
"user": "U0362BXQYJW",
|
||||
"type": "message",
|
||||
"ts": "1734322671.726339",
|
||||
"bot_id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"text": "test message",
|
||||
"team": "T0364MSFHV2",
|
||||
"bot_profile": {
|
||||
"id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"name": "blocks-test",
|
||||
"icons": {
|
||||
"image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png",
|
||||
"image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png",
|
||||
"image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png"
|
||||
},
|
||||
"deleted": false,
|
||||
"updated": 1648028754,
|
||||
"team_id": "T0364MSFHV2"
|
||||
}
|
||||
},
|
||||
"message_timestamp": "1734322671.726339"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Test workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
};
|
||||
|
||||
describe('Test SlackV2, channel => append', () => {
|
||||
nock('https://slack.com').post('/api/conversations.archive').reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['archive.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "channel",
|
||||
"operation": "archive",
|
||||
"channelId": {
|
||||
"__rl": true,
|
||||
"value": "C084ZURRQPL",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-001"
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "13de7d28-7e93-4602-a4d1-5389e8ccc4dc",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
channel: {
|
||||
id: 'C085WNEHP4Y',
|
||||
name: 'test-003',
|
||||
is_channel: true,
|
||||
is_group: false,
|
||||
is_im: false,
|
||||
is_mpim: false,
|
||||
is_private: false,
|
||||
created: 1734325731,
|
||||
is_archived: false,
|
||||
is_general: false,
|
||||
unlinked: 0,
|
||||
name_normalized: 'test-003',
|
||||
is_shared: false,
|
||||
is_org_shared: false,
|
||||
is_pending_ext_shared: false,
|
||||
pending_shared: [],
|
||||
context_team_id: 'T0364MSFHV2',
|
||||
updated: 1734325731240,
|
||||
parent_conversation: null,
|
||||
creator: 'U0362BXQYJW',
|
||||
is_ext_shared: false,
|
||||
shared_team_ids: ['T0364MSFHV2'],
|
||||
pending_connected_team_ids: [],
|
||||
is_member: true,
|
||||
last_read: '0000000000.000000',
|
||||
topic: {
|
||||
value: '',
|
||||
creator: '',
|
||||
last_set: 0,
|
||||
},
|
||||
purpose: {
|
||||
value: '',
|
||||
creator: '',
|
||||
last_set: 0,
|
||||
},
|
||||
previous_names: [],
|
||||
priority: 0,
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV2, channel => create', () => {
|
||||
nock('https://slack.com').post('/api/conversations.create').reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['create.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "channel",
|
||||
"channelId": "test-003"
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": "C085WNEHP4Y",
|
||||
"name": "test-003",
|
||||
"is_channel": true,
|
||||
"is_group": false,
|
||||
"is_im": false,
|
||||
"is_mpim": false,
|
||||
"is_private": false,
|
||||
"created": 1734325731,
|
||||
"is_archived": false,
|
||||
"is_general": false,
|
||||
"unlinked": 0,
|
||||
"name_normalized": "test-003",
|
||||
"is_shared": false,
|
||||
"is_org_shared": false,
|
||||
"is_pending_ext_shared": false,
|
||||
"pending_shared": [],
|
||||
"context_team_id": "T0364MSFHV2",
|
||||
"updated": 1734325731240,
|
||||
"parent_conversation": null,
|
||||
"creator": "U0362BXQYJW",
|
||||
"is_ext_shared": false,
|
||||
"shared_team_ids": ["T0364MSFHV2"],
|
||||
"pending_connected_team_ids": [],
|
||||
"is_member": true,
|
||||
"last_read": "0000000000.000000",
|
||||
"topic": {
|
||||
"value": "",
|
||||
"creator": "",
|
||||
"last_set": 0
|
||||
},
|
||||
"purpose": {
|
||||
"value": "",
|
||||
"creator": "",
|
||||
"last_set": 0
|
||||
},
|
||||
"previous_names": [],
|
||||
"priority": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "297d0c4b-32e6-4b08-8818-af5e64762521",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
channel: {
|
||||
id: 'C085WNEHP4Y',
|
||||
name: 'test-003',
|
||||
is_channel: true,
|
||||
is_group: false,
|
||||
is_im: false,
|
||||
is_mpim: false,
|
||||
is_private: false,
|
||||
created: 1734325731,
|
||||
is_archived: false,
|
||||
is_general: false,
|
||||
unlinked: 0,
|
||||
name_normalized: 'test-003',
|
||||
is_shared: false,
|
||||
is_org_shared: false,
|
||||
is_pending_ext_shared: false,
|
||||
pending_shared: [],
|
||||
context_team_id: 'T0364MSFHV2',
|
||||
updated: 1734325731240,
|
||||
parent_conversation: null,
|
||||
creator: 'U0362BXQYJW',
|
||||
is_ext_shared: false,
|
||||
shared_team_ids: ['T0364MSFHV2'],
|
||||
pending_connected_team_ids: [],
|
||||
is_member: true,
|
||||
last_read: '0000000000.000000',
|
||||
topic: {
|
||||
value: '',
|
||||
creator: '',
|
||||
last_set: 0,
|
||||
},
|
||||
purpose: {
|
||||
value: '',
|
||||
creator: '',
|
||||
last_set: 0,
|
||||
},
|
||||
previous_names: [],
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV2, channel => get', () => {
|
||||
nock('https://slack.com')
|
||||
.post('/api/conversations.info?channel=C085WNEHP4Y')
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['get.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "channel",
|
||||
"operation": "get",
|
||||
"channelId": {
|
||||
"__rl": true,
|
||||
"value": "C085WNEHP4Y",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-003"
|
||||
},
|
||||
"options": {
|
||||
"includeNumMembers": true
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": "C085WNEHP4Y",
|
||||
"name": "test-003",
|
||||
"is_channel": true,
|
||||
"is_group": false,
|
||||
"is_im": false,
|
||||
"is_mpim": false,
|
||||
"is_private": false,
|
||||
"created": 1734325731,
|
||||
"is_archived": false,
|
||||
"is_general": false,
|
||||
"unlinked": 0,
|
||||
"name_normalized": "test-003",
|
||||
"is_shared": false,
|
||||
"is_org_shared": false,
|
||||
"is_pending_ext_shared": false,
|
||||
"pending_shared": [],
|
||||
"context_team_id": "T0364MSFHV2",
|
||||
"updated": 1734325731240,
|
||||
"parent_conversation": null,
|
||||
"creator": "U0362BXQYJW",
|
||||
"is_ext_shared": false,
|
||||
"shared_team_ids": ["T0364MSFHV2"],
|
||||
"pending_connected_team_ids": [],
|
||||
"is_member": true,
|
||||
"last_read": "0000000000.000000",
|
||||
"topic": {
|
||||
"value": "",
|
||||
"creator": "",
|
||||
"last_set": 0
|
||||
},
|
||||
"purpose": {
|
||||
"value": "",
|
||||
"creator": "",
|
||||
"last_set": 0
|
||||
},
|
||||
"previous_names": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "753332eb-25d1-443a-acef-92993e89751a",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
channels: [
|
||||
{
|
||||
id: 'C0362BX17TM',
|
||||
name: 'general',
|
||||
is_channel: true,
|
||||
is_group: false,
|
||||
is_im: false,
|
||||
is_mpim: false,
|
||||
is_private: false,
|
||||
created: 1646724991,
|
||||
is_archived: false,
|
||||
is_general: true,
|
||||
unlinked: 0,
|
||||
name_normalized: 'general',
|
||||
is_shared: false,
|
||||
is_org_shared: false,
|
||||
is_pending_ext_shared: false,
|
||||
pending_shared: [],
|
||||
context_team_id: 'T0364MSFHV2',
|
||||
updated: 1734075560630,
|
||||
parent_conversation: null,
|
||||
creator: 'U0362BXQYJW',
|
||||
is_ext_shared: false,
|
||||
shared_team_ids: ['T0364MSFHV2'],
|
||||
pending_connected_team_ids: [],
|
||||
is_member: true,
|
||||
topic: {
|
||||
value: '',
|
||||
creator: '',
|
||||
last_set: 0,
|
||||
},
|
||||
purpose: {
|
||||
value:
|
||||
'This is the one channel that will always include everyone. It’s a great spot for announcements and team-wide conversations.',
|
||||
creator: 'U0362BXQYJW',
|
||||
last_set: 1646724991,
|
||||
},
|
||||
properties: {
|
||||
use_case: 'welcome',
|
||||
},
|
||||
previous_names: [],
|
||||
num_members: 1,
|
||||
},
|
||||
{
|
||||
id: 'C0362BXRZQA',
|
||||
name: 'random',
|
||||
is_channel: true,
|
||||
is_group: false,
|
||||
is_im: false,
|
||||
is_mpim: false,
|
||||
is_private: false,
|
||||
created: 1646724991,
|
||||
is_archived: false,
|
||||
is_general: false,
|
||||
unlinked: 0,
|
||||
name_normalized: 'random',
|
||||
is_shared: false,
|
||||
is_org_shared: false,
|
||||
is_pending_ext_shared: false,
|
||||
pending_shared: [],
|
||||
context_team_id: 'T0364MSFHV2',
|
||||
updated: 1725415586388,
|
||||
parent_conversation: null,
|
||||
creator: 'U0362BXQYJW',
|
||||
is_ext_shared: false,
|
||||
shared_team_ids: ['T0364MSFHV2'],
|
||||
pending_connected_team_ids: [],
|
||||
is_member: true,
|
||||
topic: {
|
||||
value: '',
|
||||
creator: '',
|
||||
last_set: 0,
|
||||
},
|
||||
purpose: {
|
||||
value:
|
||||
'This channel is for... well, everything else. It’s a place for team jokes, spur-of-the-moment ideas, and funny GIFs. Go wild!',
|
||||
creator: 'U0362BXQYJW',
|
||||
last_set: 1646724991,
|
||||
},
|
||||
properties: {
|
||||
tabs: [
|
||||
{
|
||||
id: 'files',
|
||||
label: '',
|
||||
type: 'files',
|
||||
},
|
||||
],
|
||||
tabz: [
|
||||
{
|
||||
type: 'files',
|
||||
},
|
||||
],
|
||||
use_case: 'random',
|
||||
},
|
||||
previous_names: [],
|
||||
num_members: 2,
|
||||
},
|
||||
],
|
||||
response_metadata: {
|
||||
next_cursor: 'dGVhbTpDMDM2MkMyM1RSOA==',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV2, channel => getAll', () => {
|
||||
nock('https://slack.com')
|
||||
.get(
|
||||
'/api/conversations.list?types=public_channel%2Cprivate_channel%2Cim%2Cmpim&exclude_archived=true&limit=2',
|
||||
)
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['getAll.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"name": "slack new tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "channel",
|
||||
"operation": "getAll",
|
||||
"limit": 2,
|
||||
"filters": {
|
||||
"excludeArchived": true,
|
||||
"types": ["public_channel", "private_channel", "im", "mpim"]
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": "C0362BX17TM",
|
||||
"name": "general",
|
||||
"is_channel": true,
|
||||
"is_group": false,
|
||||
"is_im": false,
|
||||
"is_mpim": false,
|
||||
"is_private": false,
|
||||
"created": 1646724991,
|
||||
"is_archived": false,
|
||||
"is_general": true,
|
||||
"unlinked": 0,
|
||||
"name_normalized": "general",
|
||||
"is_shared": false,
|
||||
"is_org_shared": false,
|
||||
"is_pending_ext_shared": false,
|
||||
"pending_shared": [],
|
||||
"context_team_id": "T0364MSFHV2",
|
||||
"updated": 1734075560630,
|
||||
"parent_conversation": null,
|
||||
"creator": "U0362BXQYJW",
|
||||
"is_ext_shared": false,
|
||||
"shared_team_ids": ["T0364MSFHV2"],
|
||||
"pending_connected_team_ids": [],
|
||||
"is_member": true,
|
||||
"topic": {
|
||||
"value": "",
|
||||
"creator": "",
|
||||
"last_set": 0
|
||||
},
|
||||
"purpose": {
|
||||
"value": "This is the one channel that will always include everyone. It’s a great spot for announcements and team-wide conversations.",
|
||||
"creator": "U0362BXQYJW",
|
||||
"last_set": 1646724991
|
||||
},
|
||||
"properties": {
|
||||
"use_case": "welcome"
|
||||
},
|
||||
"previous_names": [],
|
||||
"num_members": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "C0362BXRZQA",
|
||||
"name": "random",
|
||||
"is_channel": true,
|
||||
"is_group": false,
|
||||
"is_im": false,
|
||||
"is_mpim": false,
|
||||
"is_private": false,
|
||||
"created": 1646724991,
|
||||
"is_archived": false,
|
||||
"is_general": false,
|
||||
"unlinked": 0,
|
||||
"name_normalized": "random",
|
||||
"is_shared": false,
|
||||
"is_org_shared": false,
|
||||
"is_pending_ext_shared": false,
|
||||
"pending_shared": [],
|
||||
"context_team_id": "T0364MSFHV2",
|
||||
"updated": 1725415586388,
|
||||
"parent_conversation": null,
|
||||
"creator": "U0362BXQYJW",
|
||||
"is_ext_shared": false,
|
||||
"shared_team_ids": ["T0364MSFHV2"],
|
||||
"pending_connected_team_ids": [],
|
||||
"is_member": true,
|
||||
"topic": {
|
||||
"value": "",
|
||||
"creator": "",
|
||||
"last_set": 0
|
||||
},
|
||||
"purpose": {
|
||||
"value": "This channel is for... well, everything else. It’s a place for team jokes, spur-of-the-moment ideas, and funny GIFs. Go wild!",
|
||||
"creator": "U0362BXQYJW",
|
||||
"last_set": 1646724991
|
||||
},
|
||||
"properties": {
|
||||
"tabs": [
|
||||
{
|
||||
"id": "files",
|
||||
"label": "",
|
||||
"type": "files"
|
||||
}
|
||||
],
|
||||
"tabz": [
|
||||
{
|
||||
"type": "files"
|
||||
}
|
||||
],
|
||||
"use_case": "random"
|
||||
},
|
||||
"previous_names": [],
|
||||
"num_members": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "f74e9c2c-83f4-4ef8-b436-704e1a27a9ef",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
messages: [
|
||||
{
|
||||
user: 'U0362BXQYJW',
|
||||
type: 'message',
|
||||
ts: '1734322597.935429',
|
||||
bot_id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
text: 'test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_',
|
||||
team: 'T0364MSFHV2',
|
||||
bot_profile: {
|
||||
id: 'B0382SHFM46',
|
||||
deleted: false,
|
||||
name: 'blocks-test',
|
||||
updated: 1648028754,
|
||||
app_id: 'A037UTP0Z39',
|
||||
icons: {
|
||||
image_36: 'https://a.slack-edge.com/80588/img/plugins/app/bot_36.png',
|
||||
image_48: 'https://a.slack-edge.com/80588/img/plugins/app/bot_48.png',
|
||||
image_72: 'https://a.slack-edge.com/80588/img/plugins/app/service_72.png',
|
||||
},
|
||||
team_id: 'T0364MSFHV2',
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: 'rich_text',
|
||||
block_id: 'piR',
|
||||
elements: [
|
||||
{
|
||||
type: 'rich_text_section',
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'test message\n',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Automated with this ',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
url: 'http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd',
|
||||
text: 'n8n workflow',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
user: 'U0362BXQYJW',
|
||||
type: 'message',
|
||||
ts: '1734322341.161179',
|
||||
bot_id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
text: 'test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_',
|
||||
team: 'T0364MSFHV2',
|
||||
bot_profile: {
|
||||
id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
name: 'blocks-test',
|
||||
icons: {
|
||||
image_36: 'https://a.slack-edge.com/80588/img/plugins/app/bot_36.png',
|
||||
image_48: 'https://a.slack-edge.com/80588/img/plugins/app/bot_48.png',
|
||||
image_72: 'https://a.slack-edge.com/80588/img/plugins/app/service_72.png',
|
||||
},
|
||||
deleted: false,
|
||||
updated: 1648028754,
|
||||
team_id: 'T0364MSFHV2',
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: 'rich_text',
|
||||
block_id: '2BN',
|
||||
elements: [
|
||||
{
|
||||
type: 'rich_text_section',
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'test message\n',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Automated with this ',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
url: 'http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd',
|
||||
text: 'n8n workflow',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
user: 'U0362BXQYJW',
|
||||
type: 'message',
|
||||
ts: '1734321960.507649',
|
||||
edited: {
|
||||
user: 'B0382SHFM46',
|
||||
ts: '1734324905.000000',
|
||||
},
|
||||
bot_id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
text: 'updated message',
|
||||
team: 'T0364MSFHV2',
|
||||
bot_profile: {
|
||||
id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
name: 'blocks-test',
|
||||
icons: {
|
||||
image_36: 'https://a.slack-edge.com/80588/img/plugins/app/bot_36.png',
|
||||
image_48: 'https://a.slack-edge.com/80588/img/plugins/app/bot_48.png',
|
||||
image_72: 'https://a.slack-edge.com/80588/img/plugins/app/service_72.png',
|
||||
},
|
||||
deleted: false,
|
||||
updated: 1648028754,
|
||||
team_id: 'T0364MSFHV2',
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: 'rich_text',
|
||||
block_id: 'wR+',
|
||||
elements: [
|
||||
{
|
||||
type: 'rich_text_section',
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'updated message',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
subtype: 'channel_join',
|
||||
user: 'U0362BXQYJW',
|
||||
text: '<@U0362BXQYJW> has joined the channel',
|
||||
type: 'message',
|
||||
ts: '1734321942.689159',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('Test SlackV2, channel => history', () => {
|
||||
nock('https://slack.com')
|
||||
.get('/api/conversations.history?channel=C08514ZPKB8&inclusive=true&page=1&limit=100')
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['history.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,254 @@
|
||||
{
|
||||
"name": "slack new tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "channel",
|
||||
"operation": "history",
|
||||
"channelId": {
|
||||
"__rl": true,
|
||||
"value": "C08514ZPKB8",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-002"
|
||||
},
|
||||
"returnAll": true,
|
||||
"filters": {
|
||||
"inclusive": true
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"user": "U0362BXQYJW",
|
||||
"type": "message",
|
||||
"ts": "1734322597.935429",
|
||||
"bot_id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"text": "test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_",
|
||||
"team": "T0364MSFHV2",
|
||||
"bot_profile": {
|
||||
"id": "B0382SHFM46",
|
||||
"deleted": false,
|
||||
"name": "blocks-test",
|
||||
"updated": 1648028754,
|
||||
"app_id": "A037UTP0Z39",
|
||||
"icons": {
|
||||
"image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png",
|
||||
"image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png",
|
||||
"image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png"
|
||||
},
|
||||
"team_id": "T0364MSFHV2"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "piR",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "test message\n"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Automated with this ",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"url": "http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd",
|
||||
"text": "n8n workflow",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"user": "U0362BXQYJW",
|
||||
"type": "message",
|
||||
"ts": "1734322341.161179",
|
||||
"bot_id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"text": "test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_",
|
||||
"team": "T0364MSFHV2",
|
||||
"bot_profile": {
|
||||
"id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"name": "blocks-test",
|
||||
"icons": {
|
||||
"image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png",
|
||||
"image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png",
|
||||
"image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png"
|
||||
},
|
||||
"deleted": false,
|
||||
"updated": 1648028754,
|
||||
"team_id": "T0364MSFHV2"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "2BN",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "test message\n"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Automated with this ",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"url": "http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd",
|
||||
"text": "n8n workflow",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"user": "U0362BXQYJW",
|
||||
"type": "message",
|
||||
"ts": "1734321960.507649",
|
||||
"edited": {
|
||||
"user": "B0382SHFM46",
|
||||
"ts": "1734324905.000000"
|
||||
},
|
||||
"bot_id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"text": "updated message",
|
||||
"team": "T0364MSFHV2",
|
||||
"bot_profile": {
|
||||
"id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"name": "blocks-test",
|
||||
"icons": {
|
||||
"image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png",
|
||||
"image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png",
|
||||
"image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png"
|
||||
},
|
||||
"deleted": false,
|
||||
"updated": 1648028754,
|
||||
"team_id": "T0364MSFHV2"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "wR+",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "updated message"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"subtype": "channel_join",
|
||||
"user": "U0362BXQYJW",
|
||||
"text": "<@U0362BXQYJW> has joined the channel",
|
||||
"type": "message",
|
||||
"ts": "1734321942.689159"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "6b7102c7-d3c5-4b88-b67d-4ee6ae51b581",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('Test SlackV2, file => upload', () => {
|
||||
nock('https://slack.com')
|
||||
.get('/api/files.getUploadURLExternal?filename=test%20_name.txt&length=25')
|
||||
.reply(200, { ok: true, upload_url: 'https://slack.com/api/files.upload' })
|
||||
.post('/api/files.upload', () => true)
|
||||
.reply(200, { ok: true, file: { id: 'file_id' } })
|
||||
.post('/api/files.completeUploadExternal')
|
||||
.reply(200, { ok: true, files: [{ id: 'file_id' }] });
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['upload.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"name": "slack new tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [440, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "file",
|
||||
"options": {
|
||||
"fileName": "test _name.txt",
|
||||
"initialComment": "test inline",
|
||||
"threadTs": "1734322671.726339",
|
||||
"title": "Test Title"
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1100, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1320, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "toJson",
|
||||
"options": {}
|
||||
},
|
||||
"id": "76e317ed-7f2b-46b9-8c9b-f91a0b5d0fdd",
|
||||
"name": "Convert to File",
|
||||
"type": "n8n-nodes-base.convertToFile",
|
||||
"typeVersion": 1.1,
|
||||
"position": [860, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "a60db675-c351-49fc-bbad-555a70ee5d6d",
|
||||
"name": "f1",
|
||||
"value": "123",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "b96e7f11-a0ea-4a7f-b803-5ab3238af054",
|
||||
"name": "f2",
|
||||
"value": "456",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "773453a7-2bdc-4d50-a3e6-be5e1b86d820",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [660, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": "file_id"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Convert to File": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Convert to File",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "3a19732a-4416-487c-9f76-1291c610b5d1",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
channel: 'C08514ZPKB8',
|
||||
message_timestamp: '1734322671.726339',
|
||||
};
|
||||
|
||||
describe('Test SlackV2, message => delete', () => {
|
||||
nock('https://slack.com').post('/api/chat.delete').reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['delete.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "delete",
|
||||
"select": "channel",
|
||||
"channelId": {
|
||||
"__rl": true,
|
||||
"value": "C08514ZPKB8",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-002"
|
||||
},
|
||||
"timestamp": 1734322671.726339
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"ok": true,
|
||||
"channel": "C08514ZPKB8",
|
||||
"message_timestamp": "1734322671.726339"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "cf10cd01-6650-48ca-b4a2-ec07be9d026c",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
permalink: 'https://myspace-qhg7381.slack.com/archives/C08514ZPKB8/p1734322671726339',
|
||||
channel: 'C08514ZPKB8',
|
||||
};
|
||||
|
||||
describe('Test SlackV2, message => getPermalink', () => {
|
||||
nock('https://slack.com')
|
||||
.get('/api/chat.getPermalink?channel=C08514ZPKB8&message_ts=1734322671.726339')
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['getPermalink.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "getPermalink",
|
||||
"channelId": {
|
||||
"__rl": true,
|
||||
"value": "C08514ZPKB8",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-002"
|
||||
},
|
||||
"timestamp": 1734322671.726339
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"ok": true,
|
||||
"permalink": "https://myspace-qhg7381.slack.com/archives/C08514ZPKB8/p1734322671726339",
|
||||
"channel": "C08514ZPKB8"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "64c251c3-afe5-4636-a3fc-de769f8a9ea0",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
channel: 'C08514ZPKB8',
|
||||
message: {
|
||||
user: 'U0362BXQYJW',
|
||||
type: 'message',
|
||||
ts: '1734322671.726339',
|
||||
bot_id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
text: 'test message',
|
||||
team: 'T0364MSFHV2',
|
||||
bot_profile: {
|
||||
id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
name: 'blocks-test',
|
||||
icons: {
|
||||
image_36: 'https://a.slack-edge.com/80588/img/plugins/app/bot_36.png',
|
||||
image_48: 'https://a.slack-edge.com/80588/img/plugins/app/bot_48.png',
|
||||
image_72: 'https://a.slack-edge.com/80588/img/plugins/app/service_72.png',
|
||||
},
|
||||
deleted: false,
|
||||
updated: 1648028754,
|
||||
team_id: 'T0364MSFHV2',
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: 'rich_text',
|
||||
block_id: 't02Ox',
|
||||
elements: [
|
||||
{
|
||||
type: 'rich_text_section',
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'test message',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
message_timestamp: '1734322671.726339',
|
||||
};
|
||||
|
||||
describe('Test SlackV2, message => post', () => {
|
||||
nock('https://slack.com')
|
||||
.post('/api/chat.postMessage', {
|
||||
channel: 'C08514ZPKB8',
|
||||
icon_emoji: '😁',
|
||||
includeLinkToWorkflow: false,
|
||||
link_names: true,
|
||||
mrkdwn: true,
|
||||
text: 'test message',
|
||||
unfurl_links: true,
|
||||
unfurl_media: true,
|
||||
})
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['post.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"select": "channel",
|
||||
"channelId": {
|
||||
"__rl": true,
|
||||
"value": "C08514ZPKB8",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-002"
|
||||
},
|
||||
"text": "test message",
|
||||
"otherOptions": {
|
||||
"includeLinkToWorkflow": false,
|
||||
"botProfile": {
|
||||
"imageValues": {
|
||||
"profilePhotoType": "emoji",
|
||||
"icon_emoji": "😁"
|
||||
}
|
||||
},
|
||||
"link_names": true,
|
||||
"mrkdwn": true,
|
||||
"unfurl_links": true,
|
||||
"unfurl_media": true
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"ok": true,
|
||||
"channel": "C08514ZPKB8",
|
||||
"message": {
|
||||
"user": "U0362BXQYJW",
|
||||
"type": "message",
|
||||
"ts": "1734322671.726339",
|
||||
"bot_id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"text": "test message",
|
||||
"team": "T0364MSFHV2",
|
||||
"bot_profile": {
|
||||
"id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"name": "blocks-test",
|
||||
"icons": {
|
||||
"image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png",
|
||||
"image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png",
|
||||
"image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png"
|
||||
},
|
||||
"deleted": false,
|
||||
"updated": 1648028754,
|
||||
"team_id": "T0364MSFHV2"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "t02Ox",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "test message"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"message_timestamp": "1734322671.726339"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "2bc19572-58f0-4cb9-91c3-78b4b3e0cd95",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
query: 'test in:test-002',
|
||||
messages: {
|
||||
total: 3,
|
||||
pagination: {
|
||||
total_count: 3,
|
||||
page: 1,
|
||||
per_page: 2,
|
||||
page_count: 1,
|
||||
first: 1,
|
||||
last: 3,
|
||||
},
|
||||
paging: {
|
||||
count: 2,
|
||||
total: 3,
|
||||
page: 1,
|
||||
pages: 1,
|
||||
},
|
||||
matches: [
|
||||
{
|
||||
iid: 'a60ff37e-5653-4c53-8a44-783ea4315476',
|
||||
team: 'T0364MSFHV2',
|
||||
score: 0,
|
||||
channel: {
|
||||
id: 'C08514ZPKB8',
|
||||
is_channel: true,
|
||||
is_group: false,
|
||||
is_im: false,
|
||||
is_mpim: false,
|
||||
is_shared: false,
|
||||
is_org_shared: false,
|
||||
is_ext_shared: false,
|
||||
is_private: false,
|
||||
name: 'test-002',
|
||||
pending_shared: [],
|
||||
is_pending_ext_shared: false,
|
||||
},
|
||||
type: 'message',
|
||||
user: 'U0362BXQYJW',
|
||||
username: 'michael.k',
|
||||
ts: '1734322597.935429',
|
||||
blocks: [
|
||||
{
|
||||
type: 'rich_text',
|
||||
block_id: '+bc',
|
||||
elements: [
|
||||
{
|
||||
type: 'rich_text_section',
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'test message\n',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Automated with this ',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
url: 'http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd',
|
||||
text: 'n8n workflow',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
text: 'test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_',
|
||||
permalink: 'https://myspace-qhg7381.slack.com/archives/C08514ZPKB8/p1734322597935429',
|
||||
no_reactions: true,
|
||||
},
|
||||
{
|
||||
iid: '96ee0d2e-1a7d-40bf-b4b2-71ee15f004a8',
|
||||
team: 'T0364MSFHV2',
|
||||
score: 0,
|
||||
channel: {
|
||||
id: 'C08514ZPKB8',
|
||||
is_channel: true,
|
||||
is_group: false,
|
||||
is_im: false,
|
||||
is_mpim: false,
|
||||
is_shared: false,
|
||||
is_org_shared: false,
|
||||
is_ext_shared: false,
|
||||
is_private: false,
|
||||
name: 'test-002',
|
||||
pending_shared: [],
|
||||
is_pending_ext_shared: false,
|
||||
},
|
||||
type: 'message',
|
||||
user: 'U0362BXQYJW',
|
||||
username: 'michael.k',
|
||||
ts: '1734322341.161179',
|
||||
blocks: [
|
||||
{
|
||||
type: 'rich_text',
|
||||
block_id: 'FGAKN',
|
||||
elements: [
|
||||
{
|
||||
type: 'rich_text_section',
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'test message\n',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Automated with this ',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
url: 'http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd',
|
||||
text: 'n8n workflow',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
text: 'test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_',
|
||||
permalink: 'https://myspace-qhg7381.slack.com/archives/C08514ZPKB8/p1734322341161179',
|
||||
no_reactions: true,
|
||||
},
|
||||
{
|
||||
iid: '72f79902-65f7-4bf3-b64b-3fd0b0c3746c',
|
||||
team: 'T0364MSFHV2',
|
||||
score: 0,
|
||||
channel: {
|
||||
id: 'C08514ZPKB8',
|
||||
is_channel: true,
|
||||
is_group: false,
|
||||
is_im: false,
|
||||
is_mpim: false,
|
||||
is_shared: false,
|
||||
is_org_shared: false,
|
||||
is_ext_shared: false,
|
||||
is_private: false,
|
||||
name: 'test-002',
|
||||
pending_shared: [],
|
||||
is_pending_ext_shared: false,
|
||||
},
|
||||
type: 'message',
|
||||
user: 'U0362BXQYJW',
|
||||
username: 'michael.k',
|
||||
ts: '1734321960.507649',
|
||||
blocks: [
|
||||
{
|
||||
type: 'rich_text',
|
||||
block_id: 'fnjm',
|
||||
elements: [
|
||||
{
|
||||
type: 'rich_text_section',
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'test message\n',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Automated with this ',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
url: 'http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd',
|
||||
text: 'n8n workflow',
|
||||
style: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
text: 'test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_',
|
||||
permalink: 'https://myspace-qhg7381.slack.com/archives/C08514ZPKB8/p1734321960507649',
|
||||
no_reactions: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV2, message => search', () => {
|
||||
nock('https://slack.com')
|
||||
.post('/api/search.messages?query=test%20in%3Atest-002&sort=timestamp&sort_dir=desc&count=2')
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['search.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,262 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "search",
|
||||
"query": "test",
|
||||
"limit": 2,
|
||||
"options": {
|
||||
"searchChannel": ["test-002"]
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"iid": "a60ff37e-5653-4c53-8a44-783ea4315476",
|
||||
"team": "T0364MSFHV2",
|
||||
"score": 0,
|
||||
"channel": {
|
||||
"id": "C08514ZPKB8",
|
||||
"is_channel": true,
|
||||
"is_group": false,
|
||||
"is_im": false,
|
||||
"is_mpim": false,
|
||||
"is_shared": false,
|
||||
"is_org_shared": false,
|
||||
"is_ext_shared": false,
|
||||
"is_private": false,
|
||||
"name": "test-002",
|
||||
"pending_shared": [],
|
||||
"is_pending_ext_shared": false
|
||||
},
|
||||
"type": "message",
|
||||
"user": "U0362BXQYJW",
|
||||
"username": "michael.k",
|
||||
"ts": "1734322597.935429",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "+bc",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "test message\n"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Automated with this ",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"url": "http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd",
|
||||
"text": "n8n workflow",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"text": "test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_",
|
||||
"permalink": "https://myspace-qhg7381.slack.com/archives/C08514ZPKB8/p1734322597935429",
|
||||
"no_reactions": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"iid": "96ee0d2e-1a7d-40bf-b4b2-71ee15f004a8",
|
||||
"team": "T0364MSFHV2",
|
||||
"score": 0,
|
||||
"channel": {
|
||||
"id": "C08514ZPKB8",
|
||||
"is_channel": true,
|
||||
"is_group": false,
|
||||
"is_im": false,
|
||||
"is_mpim": false,
|
||||
"is_shared": false,
|
||||
"is_org_shared": false,
|
||||
"is_ext_shared": false,
|
||||
"is_private": false,
|
||||
"name": "test-002",
|
||||
"pending_shared": [],
|
||||
"is_pending_ext_shared": false
|
||||
},
|
||||
"type": "message",
|
||||
"user": "U0362BXQYJW",
|
||||
"username": "michael.k",
|
||||
"ts": "1734322341.161179",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "FGAKN",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "test message\n"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Automated with this ",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"url": "http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd",
|
||||
"text": "n8n workflow",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"text": "test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_",
|
||||
"permalink": "https://myspace-qhg7381.slack.com/archives/C08514ZPKB8/p1734322341161179",
|
||||
"no_reactions": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"iid": "72f79902-65f7-4bf3-b64b-3fd0b0c3746c",
|
||||
"team": "T0364MSFHV2",
|
||||
"score": 0,
|
||||
"channel": {
|
||||
"id": "C08514ZPKB8",
|
||||
"is_channel": true,
|
||||
"is_group": false,
|
||||
"is_im": false,
|
||||
"is_mpim": false,
|
||||
"is_shared": false,
|
||||
"is_org_shared": false,
|
||||
"is_ext_shared": false,
|
||||
"is_private": false,
|
||||
"name": "test-002",
|
||||
"pending_shared": [],
|
||||
"is_pending_ext_shared": false
|
||||
},
|
||||
"type": "message",
|
||||
"user": "U0362BXQYJW",
|
||||
"username": "michael.k",
|
||||
"ts": "1734321960.507649",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "fnjm",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "test message\n"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Automated with this ",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"url": "http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd",
|
||||
"text": "n8n workflow",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"text": "test message\n_Automated with this <http://localhost:5678/workflow/qJdEfiBgYLdfYOTs?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.slack_be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd|n8n workflow>_",
|
||||
"permalink": "https://myspace-qhg7381.slack.com/archives/C08514ZPKB8/p1734321960507649",
|
||||
"no_reactions": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "0ea58133-0522-40ae-ab51-ec3c1eced057",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
channel: 'C08514ZPKB8',
|
||||
text: 'updated message',
|
||||
message: {
|
||||
user: 'U0362BXQYJW',
|
||||
type: 'message',
|
||||
edited: {
|
||||
user: 'B0382SHFM46',
|
||||
ts: '1734324905.000000',
|
||||
},
|
||||
bot_id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
text: 'updated message',
|
||||
team: 'T0364MSFHV2',
|
||||
bot_profile: {
|
||||
id: 'B0382SHFM46',
|
||||
app_id: 'A037UTP0Z39',
|
||||
name: 'blocks-test',
|
||||
icons: {
|
||||
image_36: 'https://a.slack-edge.com/80588/img/plugins/app/bot_36.png',
|
||||
image_48: 'https://a.slack-edge.com/80588/img/plugins/app/bot_48.png',
|
||||
image_72: 'https://a.slack-edge.com/80588/img/plugins/app/service_72.png',
|
||||
},
|
||||
deleted: false,
|
||||
updated: 1648028754,
|
||||
team_id: 'T0364MSFHV2',
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: 'rich_text',
|
||||
block_id: 'Akc',
|
||||
elements: [
|
||||
{
|
||||
type: 'rich_text_section',
|
||||
elements: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'updated message',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
message_timestamp: '1734321960.507649',
|
||||
};
|
||||
|
||||
describe('Test SlackV2, message => update', () => {
|
||||
nock('https://slack.com')
|
||||
.post('/api/chat.update', {
|
||||
channel: 'C08514ZPKB8',
|
||||
link_names: true,
|
||||
parse: 'none',
|
||||
text: 'updated message',
|
||||
ts: '1734321960.507649',
|
||||
})
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['update.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "update",
|
||||
"channelId": {
|
||||
"__rl": true,
|
||||
"value": "C08514ZPKB8",
|
||||
"mode": "list",
|
||||
"cachedResultName": "test-002"
|
||||
},
|
||||
"ts": 1734321960.507649,
|
||||
"text": "updated message",
|
||||
"updateFields": {
|
||||
"link_names": true,
|
||||
"parse": "none"
|
||||
},
|
||||
"otherOptions": {
|
||||
"includeLinkToWorkflow": false
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"ok": true,
|
||||
"channel": "C08514ZPKB8",
|
||||
"text": "updated message",
|
||||
"message": {
|
||||
"user": "U0362BXQYJW",
|
||||
"type": "message",
|
||||
"edited": {
|
||||
"user": "B0382SHFM46",
|
||||
"ts": "1734324905.000000"
|
||||
},
|
||||
"bot_id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"text": "updated message",
|
||||
"team": "T0364MSFHV2",
|
||||
"bot_profile": {
|
||||
"id": "B0382SHFM46",
|
||||
"app_id": "A037UTP0Z39",
|
||||
"name": "blocks-test",
|
||||
"icons": {
|
||||
"image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png",
|
||||
"image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png",
|
||||
"image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png"
|
||||
},
|
||||
"deleted": false,
|
||||
"updated": 1648028754,
|
||||
"team_id": "T0364MSFHV2"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "Akc",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "updated message"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"message_timestamp": "1734321960.507649"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "27133240-8ff6-4115-b330-9cbc9aa95b25",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
describe('Test SlackV2, user => updateProfile', () => {
|
||||
nock('https://slack.com')
|
||||
.post('/api/users.profile.set', {
|
||||
profile: {
|
||||
customFieldUi: {
|
||||
customFieldValues: [{ alt: '', id: 'Xf05SGHVUDKM', value: 'TEST title' }],
|
||||
},
|
||||
email: 'some@email.com',
|
||||
fields: { Xf05SGHVUDKM: { alt: '', value: 'TEST title' } },
|
||||
first_name: 'first',
|
||||
last_name: 'last',
|
||||
status_emoji: '👶',
|
||||
status_expiration: 1734670800,
|
||||
status_text: 'test status',
|
||||
},
|
||||
user: 'id-new',
|
||||
})
|
||||
.reply(200, { profile: { test: 'OK' } });
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['updateProfile.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"name": "slack new tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "user",
|
||||
"operation": "updateProfile",
|
||||
"options": {
|
||||
"customFieldUi": {
|
||||
"customFieldValues": [
|
||||
{
|
||||
"id": "Xf05SGHVUDKM",
|
||||
"value": "TEST title"
|
||||
}
|
||||
]
|
||||
},
|
||||
"email": "some@email.com",
|
||||
"first_name": "first",
|
||||
"last_name": "last",
|
||||
"status": {
|
||||
"set_status": [
|
||||
{
|
||||
"status_emoji": "👶",
|
||||
"status_expiration": "2024-12-20T00:00:00",
|
||||
"status_text": "test status"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user": "id-new"
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"test": "OK"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "297af4ef-eca5-493d-8a1f-c735fcb805f2",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
usergroup: {
|
||||
id: 'S0615G0KT',
|
||||
team_id: 'T060RNRCH',
|
||||
is_usergroup: true,
|
||||
name: 'Marketing Team',
|
||||
description: 'Marketing gurus, PR experts and product advocates.',
|
||||
handle: 'marketing-team',
|
||||
is_external: false,
|
||||
date_create: 1446746793,
|
||||
date_update: 1446746793,
|
||||
date_delete: 0,
|
||||
auto_type: null,
|
||||
created_by: 'U060RNRCZ',
|
||||
updated_by: 'U060RNRCZ',
|
||||
deleted_by: null,
|
||||
prefs: {
|
||||
channels: [],
|
||||
groups: [],
|
||||
},
|
||||
user_count: '0',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV2, userGroup => create', () => {
|
||||
nock('https://slack.com').post('/api/usergroups.create').reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['create.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking 'Execute workflow'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "userGroup",
|
||||
"operation": "create",
|
||||
"name": "Marketing Team",
|
||||
"Options": {
|
||||
"channelIds": ["C08514ZPKB8"],
|
||||
"description": "Marketing department user group",
|
||||
"handle": "marketing",
|
||||
"include_count": true
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"auto_type": null,
|
||||
"created_by": "U060RNRCZ",
|
||||
"date_create": 1446746793,
|
||||
"date_delete": 0,
|
||||
"date_update": 1446746793,
|
||||
"deleted_by": null,
|
||||
"description": "Marketing gurus, PR experts and product advocates.",
|
||||
"handle": "marketing-team",
|
||||
"id": "S0615G0KT",
|
||||
"is_external": false,
|
||||
"is_usergroup": true,
|
||||
"name": "Marketing Team",
|
||||
"prefs": {
|
||||
"channels": [],
|
||||
"groups": []
|
||||
},
|
||||
"team_id": "T060RNRCH",
|
||||
"updated_by": "U060RNRCZ",
|
||||
"user_count": "0"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "cf10cd01-6650-48ca-b4a2-ec07be9d026c",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
usergroup: {
|
||||
id: 'S07XXXXXX',
|
||||
team_id: 'T1234567890',
|
||||
is_usergroup: true,
|
||||
is_subteam: true,
|
||||
name: 'Marketing Team',
|
||||
description: 'Marketing department user group',
|
||||
handle: 'marketing',
|
||||
is_external: false,
|
||||
date_create: 1734322671,
|
||||
date_update: 1734322671,
|
||||
date_delete: 1734322800,
|
||||
auto_type: null,
|
||||
auto_provision: false,
|
||||
enterprise_subteam_id: '',
|
||||
created_by: 'U1234567890',
|
||||
updated_by: 'U1234567890',
|
||||
deleted_by: 'U1234567890',
|
||||
prefs: {
|
||||
channels: [],
|
||||
groups: [],
|
||||
},
|
||||
users: [],
|
||||
user_count: 0,
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV2, userGroup => disable', () => {
|
||||
nock('https://slack.com').post('/api/usergroups.disable').reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['disable.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking 'Execute workflow'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "userGroup",
|
||||
"operation": "disable",
|
||||
"userGroupId": "S07XXXXXX",
|
||||
"options": {
|
||||
"include_count": true
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"auto_provision": false,
|
||||
"auto_type": null,
|
||||
"created_by": "U1234567890",
|
||||
"date_create": 1734322671,
|
||||
"date_delete": 1734322800,
|
||||
"date_update": 1734322671,
|
||||
"deleted_by": "U1234567890",
|
||||
"description": "Marketing department user group",
|
||||
"enterprise_subteam_id": "",
|
||||
"handle": "marketing",
|
||||
"id": "S07XXXXXX",
|
||||
"is_external": false,
|
||||
"is_subteam": true,
|
||||
"is_usergroup": true,
|
||||
"name": "Marketing Team",
|
||||
"prefs": {
|
||||
"channels": [],
|
||||
"groups": []
|
||||
},
|
||||
"team_id": "T1234567890",
|
||||
"updated_by": "U1234567890",
|
||||
"user_count": 0,
|
||||
"users": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "cf10cd01-6650-48ca-b4a2-ec07be9d026c",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
usergroup: {
|
||||
id: 'S07XXXXXX',
|
||||
team_id: 'T1234567890',
|
||||
is_usergroup: true,
|
||||
is_subteam: true,
|
||||
name: 'Marketing Team',
|
||||
description: 'Marketing department user group',
|
||||
handle: 'marketing',
|
||||
is_external: false,
|
||||
date_create: 1734322671,
|
||||
date_update: 1734322671,
|
||||
date_delete: 0,
|
||||
auto_type: null,
|
||||
auto_provision: false,
|
||||
enterprise_subteam_id: '',
|
||||
created_by: 'U1234567890',
|
||||
updated_by: 'U1234567890',
|
||||
deleted_by: null,
|
||||
prefs: {
|
||||
channels: [],
|
||||
groups: [],
|
||||
},
|
||||
users: [],
|
||||
user_count: 0,
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV2, userGroup => enable', () => {
|
||||
nock('https://slack.com').post('/api/usergroups.enable').reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['enable.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking 'Execute workflow'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "userGroup",
|
||||
"operation": "enable",
|
||||
"userGroupId": "S07XXXXXX",
|
||||
"option": {
|
||||
"include_count": true
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"auto_provision": false,
|
||||
"auto_type": null,
|
||||
"created_by": "U1234567890",
|
||||
"date_create": 1734322671,
|
||||
"date_delete": 0,
|
||||
"date_update": 1734322671,
|
||||
"deleted_by": null,
|
||||
"description": "Marketing department user group",
|
||||
"enterprise_subteam_id": "",
|
||||
"handle": "marketing",
|
||||
"id": "S07XXXXXX",
|
||||
"is_external": false,
|
||||
"is_subteam": true,
|
||||
"is_usergroup": true,
|
||||
"name": "Marketing Team",
|
||||
"prefs": {
|
||||
"channels": [],
|
||||
"groups": []
|
||||
},
|
||||
"team_id": "T1234567890",
|
||||
"updated_by": "U1234567890",
|
||||
"user_count": 0,
|
||||
"users": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "cf10cd01-6650-48ca-b4a2-ec07be9d026c",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
usergroups: [
|
||||
{
|
||||
id: 'S07XXXXXX',
|
||||
team_id: 'T1234567890',
|
||||
is_usergroup: true,
|
||||
is_subteam: true,
|
||||
name: 'Marketing Team',
|
||||
description: 'Marketing department user group',
|
||||
handle: 'marketing',
|
||||
is_external: false,
|
||||
date_create: 1734322671,
|
||||
date_update: 1734322671,
|
||||
date_delete: 0,
|
||||
auto_type: null,
|
||||
auto_provision: false,
|
||||
enterprise_subteam_id: '',
|
||||
created_by: 'U1234567890',
|
||||
updated_by: 'U1234567890',
|
||||
deleted_by: null,
|
||||
prefs: {
|
||||
channels: [],
|
||||
groups: [],
|
||||
},
|
||||
users: ['U1234567890', 'U0987654321'],
|
||||
user_count: 2,
|
||||
},
|
||||
{
|
||||
id: 'S08YYYYYY',
|
||||
team_id: 'T1234567890',
|
||||
is_usergroup: true,
|
||||
is_subteam: true,
|
||||
name: 'Engineering Team',
|
||||
description: 'Engineering department user group',
|
||||
handle: 'engineering',
|
||||
is_external: false,
|
||||
date_create: 1734322600,
|
||||
date_update: 1734322600,
|
||||
date_delete: 0,
|
||||
auto_type: null,
|
||||
auto_provision: false,
|
||||
enterprise_subteam_id: '',
|
||||
created_by: 'U1234567890',
|
||||
updated_by: 'U1234567890',
|
||||
deleted_by: null,
|
||||
prefs: {
|
||||
channels: [],
|
||||
groups: [],
|
||||
},
|
||||
users: ['U1111111111', 'U2222222222', 'U3333333333'],
|
||||
user_count: 3,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('Test SlackV2, userGroup => getAll', () => {
|
||||
nock('https://slack.com')
|
||||
.get('/api/usergroups.list')
|
||||
.query({ include_count: 'true', include_disabled: 'true', include_users: 'true' })
|
||||
.reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['getAll.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking 'Execute workflow'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "userGroup",
|
||||
"operation": "getAll",
|
||||
"returnAll": false,
|
||||
"limit": 100,
|
||||
"options": {
|
||||
"include_count": true,
|
||||
"include_disabled": true,
|
||||
"include_users": true
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"id": "S07XXXXXX",
|
||||
"team_id": "T1234567890",
|
||||
"is_usergroup": true,
|
||||
"is_subteam": true,
|
||||
"name": "Marketing Team",
|
||||
"description": "Marketing department user group",
|
||||
"handle": "marketing",
|
||||
"is_external": false,
|
||||
"date_create": 1734322671,
|
||||
"date_update": 1734322671,
|
||||
"date_delete": 0,
|
||||
"auto_type": null,
|
||||
"auto_provision": false,
|
||||
"enterprise_subteam_id": "",
|
||||
"created_by": "U1234567890",
|
||||
"updated_by": "U1234567890",
|
||||
"deleted_by": null,
|
||||
"prefs": {
|
||||
"channels": [],
|
||||
"groups": []
|
||||
},
|
||||
"users": ["U1234567890", "U0987654321"],
|
||||
"user_count": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "S08YYYYYY",
|
||||
"team_id": "T1234567890",
|
||||
"is_usergroup": true,
|
||||
"is_subteam": true,
|
||||
"name": "Engineering Team",
|
||||
"description": "Engineering department user group",
|
||||
"handle": "engineering",
|
||||
"is_external": false,
|
||||
"date_create": 1734322600,
|
||||
"date_update": 1734322600,
|
||||
"date_delete": 0,
|
||||
"auto_type": null,
|
||||
"auto_provision": false,
|
||||
"enterprise_subteam_id": "",
|
||||
"created_by": "U1234567890",
|
||||
"updated_by": "U1234567890",
|
||||
"deleted_by": null,
|
||||
"prefs": {
|
||||
"channels": [],
|
||||
"groups": []
|
||||
},
|
||||
"users": ["U1111111111", "U2222222222", "U3333333333"],
|
||||
"user_count": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "cf10cd01-6650-48ca-b4a2-ec07be9d026c",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import nock from 'nock';
|
||||
|
||||
const API_RESPONSE = {
|
||||
ok: true,
|
||||
usergroup: {
|
||||
id: 'S07XXXXXX',
|
||||
team_id: 'T1234567890',
|
||||
is_usergroup: true,
|
||||
is_subteam: true,
|
||||
name: 'Marketing Team Updated',
|
||||
description: 'Updated marketing department user group',
|
||||
handle: 'marketing-team',
|
||||
is_external: false,
|
||||
date_create: 1734322671,
|
||||
date_update: 1734322900,
|
||||
date_delete: 0,
|
||||
auto_type: null,
|
||||
auto_provision: false,
|
||||
enterprise_subteam_id: '',
|
||||
created_by: 'U1234567890',
|
||||
updated_by: 'U1234567890',
|
||||
deleted_by: null,
|
||||
prefs: {
|
||||
channels: ['C08514ZPKB8', 'C08514ZPKB9'],
|
||||
groups: [],
|
||||
},
|
||||
users: [],
|
||||
user_count: 0,
|
||||
},
|
||||
};
|
||||
|
||||
describe('Test SlackV2, userGroup => update', () => {
|
||||
nock('https://slack.com').post('/api/usergroups.update').reply(200, API_RESPONSE);
|
||||
|
||||
new NodeTestHarness().setupTests({
|
||||
workflowFiles: ['update.workflow.json'],
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"name": "slack tests",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e679c883-1839-47dc-9511-8f7dc370e6b0",
|
||||
"name": "When clicking 'Execute workflow'",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [820, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "userGroup",
|
||||
"operation": "update",
|
||||
"userGroupId": "S07XXXXXX",
|
||||
"updateFields": {
|
||||
"name": "Marketing Team Updated",
|
||||
"description": "Updated marketing department user group",
|
||||
"handle": "marketing-team",
|
||||
"channels": ["C08514ZPKB8", "C08514ZPKB9"],
|
||||
"include_count": true
|
||||
}
|
||||
},
|
||||
"id": "2e1937a6-4c8f-4cd1-ae42-11b2bd12cc4c",
|
||||
"name": "Slack",
|
||||
"type": "n8n-nodes-base.slack",
|
||||
"typeVersion": 2.3,
|
||||
"position": [1040, 360],
|
||||
"webhookId": "04c5e584-45f4-48d0-bcd2-0ecacecb0f53",
|
||||
"credentials": {
|
||||
"slackApi": {
|
||||
"id": "Bg0bWXf8apAimCqJ",
|
||||
"name": "Slack account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "06652908-6b8e-443a-9508-ab229b011b73",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1260, 360]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"auto_provision": false,
|
||||
"auto_type": null,
|
||||
"created_by": "U1234567890",
|
||||
"date_create": 1734322671,
|
||||
"date_delete": 0,
|
||||
"date_update": 1734322900,
|
||||
"deleted_by": null,
|
||||
"description": "Updated marketing department user group",
|
||||
"enterprise_subteam_id": "",
|
||||
"handle": "marketing-team",
|
||||
"id": "S07XXXXXX",
|
||||
"is_external": false,
|
||||
"is_subteam": true,
|
||||
"is_usergroup": true,
|
||||
"name": "Marketing Team Updated",
|
||||
"prefs": {
|
||||
"channels": ["C08514ZPKB8", "C08514ZPKB9"],
|
||||
"groups": []
|
||||
},
|
||||
"team_id": "T1234567890",
|
||||
"updated_by": "U1234567890",
|
||||
"user_count": 0,
|
||||
"users": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking 'Execute workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Slack",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Slack": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "cf10cd01-6650-48ca-b4a2-ec07be9d026c",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
|
||||
},
|
||||
"id": "qJdEfiBgYLdfYOTs",
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
import { type MockProxy, mock } from 'jest-mock-extended';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
|
||||
import { getTarget, createSendAndWaitMessageBody } from '../../V2/GenericFunctions';
|
||||
|
||||
describe('Slack Utility Functions', () => {
|
||||
let mockExecuteFunctions: MockProxy<IExecuteFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||
mockExecuteFunctions.getNode.mockReturnValue({ name: 'Slack', typeVersion: 1 } as any);
|
||||
mockExecuteFunctions.getSignedResumeUrl.mockReturnValueOnce(
|
||||
'http://localhost/waiting-webhook/nodeID?approved=true&signature=abc',
|
||||
);
|
||||
mockExecuteFunctions.getSignedResumeUrl.mockReturnValueOnce(
|
||||
'http://localhost/waiting-webhook/nodeID?approved=false&signature=abc',
|
||||
);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getTarget', () => {
|
||||
it('should return correct target id', () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'user') {
|
||||
return 'testUser';
|
||||
}
|
||||
return 'testChannel';
|
||||
});
|
||||
expect(getTarget(mockExecuteFunctions, 0, 'channel')).toEqual('testChannel');
|
||||
|
||||
expect(getTarget(mockExecuteFunctions, 0, 'user')).toEqual('testUser');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSendAndWaitMessageBody', () => {
|
||||
it('should create message with single button - pre 2.3 plain_text', () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channel');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channelID');
|
||||
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('message');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('subject');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({});
|
||||
|
||||
expect(createSendAndWaitMessageBody(mockExecuteFunctions)).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
text: {
|
||||
emoji: true,
|
||||
text: 'message',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'section',
|
||||
},
|
||||
{
|
||||
text: {
|
||||
text: ' ',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'section',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
elements: [
|
||||
{
|
||||
style: 'primary',
|
||||
text: {
|
||||
emoji: true,
|
||||
text: 'Approve',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'button',
|
||||
url: 'http://localhost/waiting-webhook/nodeID?approved=true&signature=abc',
|
||||
},
|
||||
],
|
||||
type: 'actions',
|
||||
},
|
||||
],
|
||||
channel: 'channelID',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create message with double buttons - pre 2.3 plain_text', () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channel');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channelID');
|
||||
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('message');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('subject');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({ approvalType: 'double' });
|
||||
|
||||
expect(createSendAndWaitMessageBody(mockExecuteFunctions)).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
text: {
|
||||
emoji: true,
|
||||
text: 'message',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'section',
|
||||
},
|
||||
{
|
||||
text: {
|
||||
text: ' ',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'section',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
elements: [
|
||||
{
|
||||
style: undefined,
|
||||
text: {
|
||||
emoji: true,
|
||||
text: 'Disapprove',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'button',
|
||||
url: 'http://localhost/waiting-webhook/nodeID?approved=false&signature=abc',
|
||||
},
|
||||
|
||||
{
|
||||
style: 'primary',
|
||||
text: {
|
||||
emoji: true,
|
||||
text: 'Approve',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'button',
|
||||
url: 'http://localhost/waiting-webhook/nodeID?approved=true&signature=abc',
|
||||
},
|
||||
],
|
||||
type: 'actions',
|
||||
},
|
||||
],
|
||||
channel: 'channelID',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create message with single button - 2.3+ mrkdwn', () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channel');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channelID');
|
||||
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('message');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('subject');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({});
|
||||
mockExecuteFunctions.getNode.mockReturnValue({ name: 'Slack', typeVersion: 2.3 } as any);
|
||||
|
||||
expect(createSendAndWaitMessageBody(mockExecuteFunctions)).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
text: {
|
||||
text: 'message',
|
||||
type: 'mrkdwn',
|
||||
},
|
||||
type: 'section',
|
||||
},
|
||||
{
|
||||
text: {
|
||||
text: ' ',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'section',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
elements: [
|
||||
{
|
||||
style: 'primary',
|
||||
text: {
|
||||
emoji: true,
|
||||
text: 'Approve',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'button',
|
||||
url: 'http://localhost/waiting-webhook/nodeID?approved=true&signature=abc',
|
||||
},
|
||||
],
|
||||
type: 'actions',
|
||||
},
|
||||
],
|
||||
channel: 'channelID',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create message with double buttons - 2.3+ mrkdwn', () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channel');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('channelID');
|
||||
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('message');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('subject');
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({ approvalType: 'double' });
|
||||
|
||||
mockExecuteFunctions.getNode.mockReturnValue({ name: 'Slack', typeVersion: 2.3 } as any);
|
||||
|
||||
expect(createSendAndWaitMessageBody(mockExecuteFunctions)).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
text: {
|
||||
text: 'message',
|
||||
type: 'mrkdwn',
|
||||
},
|
||||
type: 'section',
|
||||
},
|
||||
{
|
||||
text: {
|
||||
text: ' ',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'section',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
elements: [
|
||||
{
|
||||
style: undefined,
|
||||
text: {
|
||||
emoji: true,
|
||||
text: 'Disapprove',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'button',
|
||||
url: 'http://localhost/waiting-webhook/nodeID?approved=false&signature=abc',
|
||||
},
|
||||
|
||||
{
|
||||
style: 'primary',
|
||||
text: {
|
||||
emoji: true,
|
||||
text: 'Approve',
|
||||
type: 'plain_text',
|
||||
},
|
||||
type: 'button',
|
||||
url: 'http://localhost/waiting-webhook/nodeID?approved=true&signature=abc',
|
||||
},
|
||||
],
|
||||
type: 'actions',
|
||||
},
|
||||
],
|
||||
channel: 'channelID',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user