2023-03-09 09:13:15 -08:00
import type {
ITriggerFunctions ,
INodeType ,
INodeTypeDescription ,
ITriggerResponse ,
} from 'n8n-workflow' ;
2024-08-29 06:55:53 -07:00
import { NodeConnectionType , NodeOperationError } from 'n8n-workflow' ;
2024-01-02 03:50:31 -08:00
import moment from 'moment-timezone' ;
2024-07-16 11:42:48 -07:00
import { sendAt } from 'cron' ;
import type { IRecurrenceRule , Rule } from './SchedulerInterface' ;
import { intervalToRecurrence , recurrenceCheck , toCronExpression } from './GenericFunctions' ;
2022-10-18 04:59:17 -07:00
export class ScheduleTrigger implements INodeType {
description : INodeTypeDescription = {
displayName : 'Schedule Trigger' ,
name : 'scheduleTrigger' ,
2022-10-18 06:45:55 -07:00
icon : 'fa:clock' ,
2022-10-18 04:59:17 -07:00
group : [ 'trigger' , 'schedule' ] ,
2024-04-18 02:57:36 -07:00
version : [ 1 , 1.1 , 1.2 ] ,
2022-10-18 04:59:17 -07:00
description : 'Triggers the workflow on a given schedule' ,
eventTriggerDescription : '' ,
activationMessage :
'Your schedule trigger will now trigger executions on the schedule you have defined.' ,
defaults : {
name : 'Schedule Trigger' ,
2022-10-18 06:45:55 -07:00
color : '#31C49F' ,
2022-10-18 04:59:17 -07:00
} ,
2023-08-01 06:32:33 -07:00
2022-10-18 04:59:17 -07:00
inputs : [ ] ,
2024-08-29 06:55:53 -07:00
outputs : [ NodeConnectionType . Main ] ,
2022-10-18 04:59:17 -07:00
properties : [
{
displayName :
2024-01-15 06:12:43 -08:00
'This workflow will run on the schedule you define here once you <a data-key="activate">activate</a> it.<br><br>For testing, you can also trigger it manually: by going back to the canvas and clicking \'test workflow\'' ,
2022-10-18 04:59:17 -07:00
name : 'notice' ,
type : 'notice' ,
default : '' ,
} ,
{
displayName : 'Trigger Rules' ,
name : 'rule' ,
placeholder : 'Add Rule' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
} ,
default : {
interval : [
{
field : 'days' ,
} ,
] ,
} ,
options : [
{
name : 'interval' ,
displayName : 'Trigger Interval' ,
values : [
{
displayName : 'Trigger Interval' ,
name : 'field' ,
type : 'options' ,
default : 'days' ,
2022-10-19 00:36:25 -07:00
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
2022-10-18 04:59:17 -07:00
options : [
{
2022-10-18 08:47:26 -07:00
name : 'Seconds' ,
value : 'seconds' ,
2022-10-18 04:59:17 -07:00
} ,
{
2022-10-18 08:47:26 -07:00
name : 'Minutes' ,
value : 'minutes' ,
2022-10-18 04:59:17 -07:00
} ,
{
name : 'Hours' ,
value : 'hours' ,
} ,
{
2022-10-18 08:47:26 -07:00
name : 'Days' ,
value : 'days' ,
2022-10-18 04:59:17 -07:00
} ,
{
2022-10-18 08:47:26 -07:00
name : 'Weeks' ,
value : 'weeks' ,
2022-10-18 04:59:17 -07:00
} ,
{
2022-10-18 08:47:26 -07:00
name : 'Months' ,
value : 'months' ,
2022-10-18 04:59:17 -07:00
} ,
{
2022-10-18 08:47:26 -07:00
name : 'Custom (Cron)' ,
value : 'cronExpression' ,
2022-10-18 04:59:17 -07:00
} ,
] ,
} ,
{
displayName : 'Seconds Between Triggers' ,
name : 'secondsInterval' ,
type : 'number' ,
default : 30 ,
displayOptions : {
show : {
field : [ 'seconds' ] ,
} ,
} ,
description : 'Number of seconds between each workflow trigger' ,
} ,
{
displayName : 'Minutes Between Triggers' ,
name : 'minutesInterval' ,
type : 'number' ,
default : 5 ,
displayOptions : {
show : {
field : [ 'minutes' ] ,
} ,
} ,
description : 'Number of minutes between each workflow trigger' ,
} ,
{
displayName : 'Hours Between Triggers' ,
name : 'hoursInterval' ,
type : 'number' ,
displayOptions : {
show : {
field : [ 'hours' ] ,
} ,
} ,
default : 1 ,
description : 'Number of hours between each workflow trigger' ,
} ,
{
displayName : 'Days Between Triggers' ,
name : 'daysInterval' ,
type : 'number' ,
displayOptions : {
show : {
field : [ 'days' ] ,
} ,
} ,
default : 1 ,
description : 'Number of days between each workflow trigger' ,
} ,
{
displayName : 'Weeks Between Triggers' ,
name : 'weeksInterval' ,
type : 'number' ,
displayOptions : {
show : {
field : [ 'weeks' ] ,
} ,
} ,
default : 1 ,
description : 'Would run every week unless specified otherwise' ,
} ,
{
displayName : 'Months Between Triggers' ,
name : 'monthsInterval' ,
type : 'number' ,
displayOptions : {
show : {
field : [ 'months' ] ,
} ,
} ,
default : 1 ,
description : 'Would run every month unless specified otherwise' ,
} ,
{
displayName : 'Trigger at Day of Month' ,
name : 'triggerAtDayOfMonth' ,
type : 'number' ,
displayOptions : {
show : {
field : [ 'months' ] ,
} ,
} ,
typeOptions : {
minValue : 1 ,
maxValue : 31 ,
} ,
default : 1 ,
description : 'The day of the month to trigger (1-31)' ,
hint : 'If a month doesn’ t have this day, the node won’ t trigger' ,
} ,
{
displayName : 'Trigger on Weekdays' ,
name : 'triggerAtDay' ,
type : 'multiOptions' ,
displayOptions : {
show : {
field : [ 'weeks' ] ,
} ,
} ,
typeOptions : {
maxValue : 7 ,
} ,
options : [
{
name : 'Monday' ,
value : 1 ,
} ,
{
name : 'Tuesday' ,
value : 2 ,
} ,
{
name : 'Wednesday' ,
value : 3 ,
} ,
{
name : 'Thursday' ,
value : 4 ,
} ,
{
name : 'Friday' ,
value : 5 ,
} ,
{
name : 'Saturday' ,
value : 6 ,
} ,
{
name : 'Sunday' ,
2022-10-19 08:28:41 -07:00
value : 0 ,
2022-10-18 04:59:17 -07:00
} ,
] ,
2022-10-19 08:28:41 -07:00
default : [ 0 ] ,
2022-10-18 04:59:17 -07:00
} ,
{
displayName : 'Trigger at Hour' ,
name : 'triggerAtHour' ,
type : 'options' ,
default : 0 ,
displayOptions : {
show : {
field : [ 'days' , 'weeks' , 'months' ] ,
} ,
} ,
options : [
{
name : 'Midnight' ,
displayName : 'Midnight' ,
value : 0 ,
} ,
{
name : '1am' ,
displayName : '1am' ,
value : 1 ,
} ,
{
name : '2am' ,
displayName : '2am' ,
value : 2 ,
} ,
{
name : '3am' ,
displayName : '3am' ,
value : 3 ,
} ,
{
name : '4am' ,
displayName : '4am' ,
value : 4 ,
} ,
{
name : '5am' ,
displayName : '5am' ,
value : 5 ,
} ,
{
name : '6am' ,
displayName : '6am' ,
value : 6 ,
} ,
{
name : '7am' ,
displayName : '7am' ,
value : 7 ,
} ,
{
name : '8am' ,
displayName : '8am' ,
value : 8 ,
} ,
{
name : '9am' ,
displayName : '9am' ,
value : 9 ,
} ,
{
name : '10am' ,
displayName : '10am' ,
value : 10 ,
} ,
{
name : '11am' ,
displayName : '11am' ,
value : 11 ,
} ,
{
name : 'Noon' ,
displayName : 'Noon' ,
value : 12 ,
} ,
{
name : '1pm' ,
displayName : '1pm' ,
value : 13 ,
} ,
{
name : '2pm' ,
displayName : '2pm' ,
value : 14 ,
} ,
{
name : '3pm' ,
displayName : '3pm' ,
value : 15 ,
} ,
{
name : '4pm' ,
displayName : '4pm' ,
value : 16 ,
} ,
{
name : '5pm' ,
displayName : '5pm' ,
value : 17 ,
} ,
{
name : '6pm' ,
displayName : '6pm' ,
value : 18 ,
} ,
{
name : '7pm' ,
displayName : '7pm' ,
value : 19 ,
} ,
{
name : '8pm' ,
displayName : '8pm' ,
value : 20 ,
} ,
{
name : '9pm' ,
displayName : '9pm' ,
value : 21 ,
} ,
{
name : '10pm' ,
displayName : '10pm' ,
value : 22 ,
} ,
{
name : '11pm' ,
displayName : '11pm' ,
value : 23 ,
} ,
] ,
description : 'The hour of the day to trigger' ,
} ,
{
displayName : 'Trigger at Minute' ,
name : 'triggerAtMinute' ,
type : 'number' ,
default : 0 ,
displayOptions : {
show : {
field : [ 'hours' , 'days' , 'weeks' , 'months' ] ,
} ,
} ,
typeOptions : {
minValue : 0 ,
maxValue : 59 ,
} ,
description : 'The minute past the hour to trigger (0-59)' ,
} ,
{
displayName :
2023-03-30 04:07:16 -07:00
'You can find help generating your cron expression <a href="https://crontab.guru/examples.html" target="_blank">here</a>' ,
2022-10-18 04:59:17 -07:00
name : 'notice' ,
type : 'notice' ,
displayOptions : {
show : {
field : [ 'cronExpression' ] ,
} ,
} ,
default : '' ,
} ,
{
displayName : 'Expression' ,
name : 'expression' ,
type : 'string' ,
default : '' ,
placeholder : 'eg. 0 15 * 1 sun' ,
displayOptions : {
show : {
field : [ 'cronExpression' ] ,
} ,
} ,
2024-07-16 11:42:48 -07:00
hint : 'Format: [Second] [Minute] [Hour] [Day of Month] [Month] [Day of Week]' ,
2022-10-18 04:59:17 -07:00
} ,
] ,
} ,
] ,
} ,
] ,
} ;
async trigger ( this : ITriggerFunctions ) : Promise < ITriggerResponse > {
2024-07-16 11:42:48 -07:00
const { interval : intervals } = this . getNodeParameter ( 'rule' , [ ] ) as Rule ;
2022-10-18 04:59:17 -07:00
const timezone = this . getTimezone ( ) ;
2022-11-21 01:32:23 -08:00
const staticData = this . getWorkflowStaticData ( 'node' ) as {
2024-07-16 11:42:48 -07:00
recurrenceRules : number [ ] ;
2022-11-21 01:32:23 -08:00
} ;
2024-07-16 11:42:48 -07:00
if ( ! staticData . recurrenceRules ) {
staticData . recurrenceRules = [ ] ;
2022-11-21 01:32:23 -08:00
}
2024-07-16 11:42:48 -07:00
const executeTrigger = ( recurrence : IRecurrenceRule ) = > {
const shouldTrigger = recurrenceCheck ( recurrence , staticData . recurrenceRules , timezone ) ;
if ( ! shouldTrigger ) return ;
const momentTz = moment . tz ( timezone ) ;
2022-10-18 04:59:17 -07:00
const resultData = {
2024-07-16 11:42:48 -07:00
timestamp : momentTz.toISOString ( true ) ,
'Readable date' : momentTz . format ( 'MMMM Do YYYY, h:mm:ss a' ) ,
'Readable time' : momentTz . format ( 'h:mm:ss a' ) ,
'Day of week' : momentTz . format ( 'dddd' ) ,
Year : momentTz.format ( 'YYYY' ) ,
Month : momentTz.format ( 'MMMM' ) ,
'Day of month' : momentTz . format ( 'DD' ) ,
Hour : momentTz.format ( 'HH' ) ,
Minute : momentTz.format ( 'mm' ) ,
Second : momentTz.format ( 'ss' ) ,
Timezone : ` ${ timezone } (UTC ${ momentTz . format ( 'Z' ) } ) ` ,
2022-10-18 04:59:17 -07:00
} ;
2022-11-21 01:32:23 -08:00
2024-07-16 11:42:48 -07:00
this . emit ( [ this . helpers . returnJsonArray ( [ resultData ] ) ] ) ;
2022-10-18 04:59:17 -07:00
} ;
2022-11-21 01:32:23 -08:00
2024-07-16 11:42:48 -07:00
const rules = intervals . map ( ( interval , i ) = > ( {
interval ,
cronExpression : toCronExpression ( interval ) ,
recurrence : intervalToRecurrence ( interval , i ) ,
} ) ) ;
if ( this . getMode ( ) !== 'manual' ) {
for ( const { interval , cronExpression , recurrence } of rules ) {
2022-10-18 04:59:17 -07:00
try {
2024-07-16 11:42:48 -07:00
this . helpers . registerCron ( cronExpression , ( ) = > executeTrigger ( recurrence ) ) ;
2022-10-18 04:59:17 -07:00
} catch ( error ) {
2024-07-16 11:42:48 -07:00
if ( interval . field === 'cronExpression' ) {
throw new NodeOperationError ( this . getNode ( ) , 'Invalid cron expression' , {
description : 'More information on how to build them at https://crontab.guru/' ,
} ) ;
} else {
throw error ;
}
2023-02-01 13:53:05 -08:00
}
2022-10-18 04:59:17 -07:00
}
2024-07-16 11:42:48 -07:00
return { } ;
} else {
const manualTriggerFunction = async ( ) = > {
const { interval , cronExpression , recurrence } = rules [ 0 ] ;
if ( interval . field === 'cronExpression' ) {
try {
sendAt ( cronExpression ) ;
} catch ( error ) {
throw new NodeOperationError ( this . getNode ( ) , 'Invalid cron expression' , {
description : 'More information on how to build them at https://crontab.guru/' ,
} ) ;
}
2022-11-21 01:32:23 -08:00
}
2024-07-16 11:42:48 -07:00
executeTrigger ( recurrence ) ;
} ;
2022-10-18 04:59:17 -07:00
2024-07-16 11:42:48 -07:00
return { manualTriggerFunction } ;
2022-10-18 04:59:17 -07:00
}
}
}