The Scheduler API allows apps to create tasks that run in a defined schedule. These tasks can be one-time events or recurring tasks. It uses agenda.js as the backend, so the schedule syntax and internal processes are all according to its documentation.
The API details are in the Module scheduler definition and this example app can also be used as a guide. Check your app logs to see the example in action.
Add permissions
As per the permission system, the scheduler API needs the following permission in your app's manifest (app.json file):
{
"permissions": [
{ "name": "scheduler" }
]
}Register jobs (processors)
To use the scheduler API, you'll need two things:
The functions to be run as jobs (we call them
processors).The schedule in which they run.
This is configured using the app's extendConfiguration method. The processors are registered during the app's startup to make them available for scheduling. Refer to the following code snippet:
import { IConfigurationExtend } from '@rocket.chat/apps-engine/definition/accessors';
import { IJobContext } from '@rocket.chat/apps-engine/definition/scheduler';
import { App } from '@rocket.chat/apps-engine/definition/App';
export class MyApp extends App {
public async extendConfiguration(configuration: IConfigurationExtend): Promise<void> {
configuration.scheduler.registerProcessors([
{
id: 'first',
// NOTE: Use a method reference — do NOT use an arrow function or .bind(this)
processor: this.myProcessor,
},
]);
}
private async myProcessor(jobData: IJobContext): Promise<void> {
this.getLogger().info(`[${ new Date() }] this is a task`, jobData);
}
}The processor is an async function and can receive arguments (jobData). The arguments are passed during scheduling.
Processor functions are commonly registered as arrow functions. For example,
processor: async (jobData) => this.getLogger().info(`[${ Date() }] this is a task`, jobData)But this poses a challenge for us to inject the correct app reference to its execution. Arrow functions lexically bind
thisat definition time, which prevents the runtime from injecting the correct request-scoped app proxy during execution. It is recommended that you follow the code examples used in this document. For further details on logging, refer to the App Logs document.
Start a job
To trigger the registered processor as a job, you must provide:
The
idof the processor.The type of job it will be. The available types are
scheduleRecurring(the job runs in aninterval) andscheduleOnce(the job runs once using thewhenkeyword).
Starting a job can be done when running a slash command, for example:
// slashcommand class
public async executor(context: SlashCommandContext, read: IRead, modify: IModify): Promise<void> {
// SCHEDULING A RECURRING TASK
const task = {
id: 'first',
interval: '10 seconds',
data: { test: true },
};
await modify.getScheduler().scheduleRecurring(task);
// SCHEDULING ONETIME TASK
const task = {
id: 'first',
when: '8 seconds',
};
await modify.getScheduler().scheduleOnce(task);
}You can also trigger a job as soon as it gets registered without manual or automated triggering. When you register your processor in the extendConfiguration method, you can pass a prop called startupSetting in the processor's object, as follows:
import { IConfigurationExtend } from '@rocket.chat/apps-engine/definition/accessors';
import { IJobContext, StartupType } from '@rocket.chat/apps-engine/definition/scheduler';
import { App } from '@rocket.chat/apps-engine/definition/App';
export class MyApp extends App {
public async extendConfiguration(configuration: IConfigurationExtend): Promise<void> {
configuration.scheduler.registerProcessors([
{
id: 'first',
// Using a method reference
processor: this.firstProcessor,
startupSetting: {
type: StartupType.ONETIME,
when: '20 seconds',
data: { test: true },
},
},
{
id: 'second',
// Using a method reference
processor: this.secondProcessor,
startupSetting: {
type: StartupType.RECURRING,
interval: '20 seconds',
},
},
]);
}
private async firstProcessor(jobData: IJobContext): Promise<void> {
this.getLogger().info(`[${ new Date() }] first processor ran`, jobData);
}
private async secondProcessor(jobData: IJobContext): Promise<void> {
this.getLogger().info(`[${ new Date() }] second processor ran`, jobData);
}
}This indicates that you want that particular processor to be scheduled as soon as it is registered. You can define the "immediate scheduling" as a recurring job ( StartupType.RECURRING) or a one-time job ( StartupType.ONETIME). You can also pass data using the data object. It will work just like when you schedule a task using the modify accessor.
Here, data is not something that's passed to the processor or a function as a living object or executable code. This data is a static piece of data that is passed to the processor's first argument.
The signature of the processor function is as follows:
(jobContext: IJobContext, read: IRead, modify: IModify, http: IHttp, persis: IPersistence) => Promise<void>The first argument is the data object you're passing when actually scheduling the job; ( [k: string]:any). The rest of the arguments are passed when the function is run.
Cancel a job
To stop a job, all you have to do is pass the ID of the job you want to stop:
const jobId = 'first';
await modify.getScheduler().cancelJob(jobId);It will stop the running job (if any).
Cancel all jobs from the app
To stop all the current running jobs from the app:
await modify.getScheduler().cancelAllJobs();