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,160 @@
|
||||
import {
|
||||
getSendAndWaitConfig,
|
||||
getSendAndWaitProperties,
|
||||
} from 'n8n-nodes-base/dist/utils/sendAndWait/utils';
|
||||
import {
|
||||
ChatNodeMessageType,
|
||||
FREE_TEXT_CHAT_RESPONSE_TYPE,
|
||||
NodeOperationError,
|
||||
UserError,
|
||||
WAIT_INDEFINITELY,
|
||||
} from 'n8n-workflow';
|
||||
import type {
|
||||
ChatNodeMessage,
|
||||
ChatNodeMessageButtonType,
|
||||
IExecuteFunctions,
|
||||
INodeProperties,
|
||||
INodePropertyOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export function configureWaitTillDate(context: IExecuteFunctions) {
|
||||
let waitTill = WAIT_INDEFINITELY;
|
||||
|
||||
const limitOptions = context.getNodeParameter('options.limitWaitTime.values', 0, {}) as {
|
||||
limitType?: string;
|
||||
resumeAmount?: number;
|
||||
resumeUnit?: string;
|
||||
maxDateAndTime?: string;
|
||||
};
|
||||
|
||||
if (Object.keys(limitOptions).length) {
|
||||
try {
|
||||
if (limitOptions.limitType === 'afterTimeInterval') {
|
||||
let waitAmount = limitOptions.resumeAmount as number;
|
||||
|
||||
if (limitOptions.resumeUnit === 'minutes') {
|
||||
waitAmount *= 60;
|
||||
}
|
||||
if (limitOptions.resumeUnit === 'hours') {
|
||||
waitAmount *= 60 * 60;
|
||||
}
|
||||
if (limitOptions.resumeUnit === 'days') {
|
||||
waitAmount *= 60 * 60 * 24;
|
||||
}
|
||||
|
||||
waitAmount *= 1000;
|
||||
waitTill = new Date(new Date().getTime() + waitAmount);
|
||||
} else {
|
||||
waitTill = new Date(limitOptions.maxDateAndTime as string);
|
||||
}
|
||||
|
||||
if (isNaN(waitTill.getTime())) {
|
||||
throw new UserError('Invalid date format');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new NodeOperationError(context.getNode(), 'Could not configure Limit Wait Time', {
|
||||
description: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return waitTill;
|
||||
}
|
||||
|
||||
export const configureInputs = (parameters: { options?: { memoryConnection?: boolean } }) => {
|
||||
const inputs = [
|
||||
{
|
||||
type: 'main',
|
||||
},
|
||||
];
|
||||
if (parameters.options?.memoryConnection) {
|
||||
return [
|
||||
...inputs,
|
||||
{
|
||||
type: 'ai_memory',
|
||||
displayName: 'Memory',
|
||||
maxConnections: 1,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return inputs;
|
||||
};
|
||||
|
||||
const freeTextResponseTypeOption: INodePropertyOptions = {
|
||||
name: 'Free Text',
|
||||
// use a different name to not show options for `freeText` response type
|
||||
value: FREE_TEXT_CHAT_RESPONSE_TYPE,
|
||||
description: 'User can submit a response in the chat',
|
||||
};
|
||||
|
||||
const blockUserInput: INodeProperties = {
|
||||
displayName: 'Block User Input',
|
||||
name: 'blockUserInput',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to block input from the user while waiting for approval',
|
||||
displayOptions: {
|
||||
show: {
|
||||
responseType: ['approval'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const getSendAndWaitPropertiesForChatNode = () => {
|
||||
const originalProperties = getSendAndWaitProperties([], null);
|
||||
const filteredProperties = originalProperties.filter(
|
||||
// `subject` is not needed and we provide our own `message` and `options` properties
|
||||
(p) => p.name !== 'subject' && p.name !== 'message' && p.name !== 'options',
|
||||
);
|
||||
const responseTypeProperty = filteredProperties.find((p) => p.name === 'responseType');
|
||||
if (responseTypeProperty) {
|
||||
const approvalOption = responseTypeProperty.options?.find(
|
||||
(o) => 'value' in o && o.value === 'approval',
|
||||
);
|
||||
responseTypeProperty.options = approvalOption
|
||||
? [
|
||||
// for now we only support `approval` and `freeText` response types
|
||||
approvalOption,
|
||||
freeTextResponseTypeOption,
|
||||
]
|
||||
: [freeTextResponseTypeOption];
|
||||
responseTypeProperty.default = FREE_TEXT_CHAT_RESPONSE_TYPE;
|
||||
}
|
||||
|
||||
filteredProperties.splice(1, 0, blockUserInput);
|
||||
return filteredProperties;
|
||||
};
|
||||
|
||||
export function getChatMessage(ctx: IExecuteFunctions): ChatNodeMessage {
|
||||
const nodeVersion = ctx.getNode().typeVersion;
|
||||
const message = ctx.getNodeParameter('message', 0, '') as string;
|
||||
if (nodeVersion < 1.1) {
|
||||
return message;
|
||||
}
|
||||
|
||||
const responseType = ctx.getNodeParameter(
|
||||
'responseType',
|
||||
0,
|
||||
FREE_TEXT_CHAT_RESPONSE_TYPE,
|
||||
) as string;
|
||||
if (responseType === FREE_TEXT_CHAT_RESPONSE_TYPE) {
|
||||
// for free text, we just return the message
|
||||
// since the user will respond with the text in the chat
|
||||
return message;
|
||||
}
|
||||
|
||||
const blockUserInput = ctx.getNodeParameter('blockUserInput', 0, false) as boolean;
|
||||
const config = getSendAndWaitConfig(ctx);
|
||||
return {
|
||||
type: ChatNodeMessageType.WITH_BUTTONS,
|
||||
text: message,
|
||||
blockUserInput,
|
||||
// the buttons are reversed to show the primary button first
|
||||
buttons: [...config.options].reverse().map((option) => ({
|
||||
text: option.label,
|
||||
link: option.url,
|
||||
type: option.style as ChatNodeMessageButtonType,
|
||||
})),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user