Skip to main content
Version: Next

Dynamic Tasks

While static tasks are great for recurring operations, you often need to create tasks dynamically based on user interactions or events. The tasks plugin provides utilities for creating tasks on-demand.

Creating Dynamic Tasks

Use the createTask function to create tasks programmatically:

import { createTask } from '@commandkit/tasks';

// Create a task that runs in 5 minutes
const taskId = await createTask({
name: 'reminder',
data: { userId: '123', message: "Don't forget your meeting!" },
schedule: Date.now() + 5 * 60 * 1000, // 5 minutes from now
});

Reminder System Example

Here's a complete example of a reminder command that creates dynamic tasks:

import type { CommandData, ChatInputCommand } from 'commandkit';
import { ApplicationCommandOptionType } from 'discord.js';
import ms from 'ms';
import { createTask } from '@commandkit/tasks';

export const command: CommandData = {
name: 'remind',
description: 'remind command',
options: [
{
name: 'time',
description: 'The time to remind after. Eg: 6h, 10m, 1d',
type: ApplicationCommandOptionType.String,
required: true,
},
{
name: 'message',
description: 'The message to remind about.',
type: ApplicationCommandOptionType.String,
required: true,
},
],
};

export const chatInput: ChatInputCommand = async (ctx) => {
const time = ctx.options.getString('time', true);
const message = ctx.options.getString('message', true);
const timeMs = Date.now() + ms(time as `${number}`);

await createTask({
name: 'remind',
data: {
userId: ctx.interaction.user.id,
message,
channelId: ctx.interaction.channelId,
setAt: Date.now(),
},
schedule: timeMs,
});

await ctx.interaction.reply(
`I will remind you <t:${Math.floor(timeMs / 1000)}:R> for \`${message}\``,
);
};

The Reminder Task

Create a static task definition that handles the actual reminder:

// src/app/tasks/remind.ts
import { task } from '@commandkit/tasks';

export interface RemindTaskData {
userId: string;
message: string;
channelId: string;
setAt: number;
}

export default task<RemindTaskData>({
name: 'remind',
async execute(ctx) {
const { userId, message, channelId, setAt } = ctx.data;

const channel = await ctx.client.channels.fetch(channelId);

if (!channel?.isTextBased() || !channel.isSendable()) return;

await channel.send({
content: `<@${userId}>`,
embeds: [
{
title: `You asked me <t:${Math.floor(setAt / 1000)}:R> to remind you about:`,
description: message,
color: 0x0099ff,
timestamp: new Date(setAt).toISOString(),
},
],
});
},
});

Advanced Dynamic Task Patterns

Conditional Task Creation

import type { CommandData, ChatInputCommand } from 'commandkit';
import { ApplicationCommandOptionType } from 'discord.js';
import { createTask } from '@commandkit/tasks';

export const command: CommandData = {
name: 'schedule-maintenance',
description: 'Schedule maintenance for a specific time',
options: [
{
name: 'duration',
description: 'Duration in minutes',
type: ApplicationCommandOptionType.Integer,
required: true,
},
],
};

export const chatInput: ChatInputCommand = async (ctx) => {
const maintenanceMode = ctx.commandkit.store.get('maintenance-mode');

if (maintenanceMode) {
await ctx.interaction.reply('Maintenance mode is already enabled.');
return;
}

const duration = ctx.options.getInteger('duration', true);

// Create maintenance task
await createTask({
name: 'maintenance',
data: {
requestedBy: ctx.interaction.user.id,
duration: duration * 60 * 1000, // Convert to milliseconds
},
schedule: Date.now() + 5 * 60 * 1000, // 5 minutes from now
});

await ctx.interaction.reply('Maintenance scheduled for 5 minutes from now.');
};

Batch Task Creation

import type { CommandData, ChatInputCommand } from 'commandkit';
import { ApplicationCommandOptionType } from 'discord.js';
import { createTask } from '@commandkit/tasks';

export const command: CommandData = {
name: 'schedule-events',
description: 'Schedule multiple events',
options: [
{
name: 'events',
description: 'Number of events to schedule',
type: ApplicationCommandOptionType.Integer,
required: true,
},
],
};

export const chatInput: ChatInputCommand = async (ctx) => {
const eventCount = ctx.options.getInteger('events', true);

const events = Array.from({ length: eventCount }, (_, i) => ({
name: `Event ${i + 1}`,
time: Date.now() + (i + 1) * 60 * 60 * 1000, // 1 hour apart
}));

const taskIds = await Promise.all(
events.map((event) =>
createTask({
name: 'event-notification',
data: {
eventName: event.name,
channelId: ctx.interaction.channelId,
},
schedule: event.time,
}),
),
);

await ctx.interaction.reply(`Scheduled ${events.length} events.`);
};

Managing Dynamic Tasks

Deleting Tasks

import type { CommandData, ChatInputCommand } from 'commandkit';
import { ApplicationCommandOptionType } from 'discord.js';
import { deleteTask } from '@commandkit/tasks';

export const command: CommandData = {
name: 'cancel-reminder',
description: 'Cancel a scheduled reminder',
options: [
{
name: 'task-id',
description: 'The task ID to cancel',
type: ApplicationCommandOptionType.String,
required: true,
},
],
};

export const chatInput: ChatInputCommand = async (ctx) => {
const taskId = ctx.options.getString('task-id', true);

try {
await deleteTask(taskId);
await ctx.interaction.reply('Reminder cancelled successfully.');
} catch (error) {
await ctx.interaction.reply(
'Failed to cancel reminder. It may have already been executed.',
);
}
};

Task Storage and Retrieval

For more complex scenarios, you might want to store task IDs in your database:

import type { CommandData, ChatInputCommand } from 'commandkit';
import { ApplicationCommandOptionType } from 'discord.js';
import ms from 'ms';
import { createTask } from '@commandkit/tasks';

export const command: CommandData = {
name: 'remind',
description: 'Set a reminder',
options: [
{
name: 'time',
description: 'The time to remind after. Eg: 6h, 10m, 1d',
type: ApplicationCommandOptionType.String,
required: true,
},
{
name: 'message',
description: 'The message to remind about.',
type: ApplicationCommandOptionType.String,
required: true,
},
],
};

export const chatInput: ChatInputCommand = async (ctx) => {
const timeStr = ctx.options.getString('time', true);
const message = ctx.options.getString('message', true);

const delay = ms(timeStr as `${number}`);
const taskId = await createTask({
name: 'reminder',
data: {
userId: ctx.interaction.user.id,
message,
},
schedule: Date.now() + delay,
});

// Store the task ID in your database
await db.reminders.create({
userId: ctx.interaction.user.id,
taskId,
message,
scheduledFor: new Date(Date.now() + delay),
});

await ctx.interaction.reply(`Reminder set! Task ID: ${taskId}`);
};

Error Handling

Always handle potential errors when creating dynamic tasks:

import type { CommandData, ChatInputCommand } from 'commandkit';
import { ApplicationCommandOptionType } from 'discord.js';
import { createTask } from '@commandkit/tasks';

export const command: CommandData = {
name: 'schedule-task',
description: 'Schedule a custom task',
options: [
{
name: 'delay',
description: 'Delay in minutes',
type: ApplicationCommandOptionType.Integer,
required: true,
},
],
};

export const chatInput: ChatInputCommand = async (ctx) => {
try {
const delay = ctx.options.getInteger('delay', true);
const taskId = await createTask({
name: 'custom-task',
data: { userId: ctx.interaction.user.id },
schedule: Date.now() + delay * 60 * 1000,
});

await ctx.interaction.reply(`Task scheduled successfully. ID: ${taskId}`);
} catch (error) {
console.error('Failed to schedule task:', error);
await ctx.interaction.reply('Failed to schedule task. Please try again.');
}
};

Best Practices

1. Use Descriptive Task Names

// Good
await createTask({
name: 'user-reminder',
data: { userId, message },
schedule: reminderTime,
});

// Avoid
await createTask({
name: 'task',
data: { userId, message },
schedule: reminderTime,
});

2. Validate Input Data

import type { CommandData, ChatInputCommand } from 'commandkit';
import { ApplicationCommandOptionType } from 'discord.js';
import ms from 'ms';
import { createTask } from '@commandkit/tasks';

export const command: CommandData = {
name: 'schedule-reminder',
description: 'Schedule a reminder',
options: [
{
name: 'time',
description: 'The time to remind after. Eg: 6h, 10m, 1d',
type: ApplicationCommandOptionType.String,
required: true,
},
{
name: 'message',
description: 'The message to remind about.',
type: ApplicationCommandOptionType.String,
required: true,
},
],
};

export const chatInput: ChatInputCommand = async (ctx) => {
const timeStr = ctx.options.getString('time', true);
const message = ctx.options.getString('message', true);

// Validate time format
const delay = ms(timeStr as `${number}`);
if (!delay || delay < 60000 || delay > 30 * 24 * 60 * 60 * 1000) {
await ctx.interaction.reply(
'Please specify a time between 1 minute and 30 days.',
);
return;
}

// Validate message length
if (message.length > 1000) {
await ctx.interaction.reply(
'Message too long. Please keep it under 1000 characters.',
);
return;
}

// Create task
await createTask({
name: 'reminder',
data: { userId: ctx.interaction.user.id, message },
schedule: Date.now() + delay,
});

await ctx.interaction.reply('Reminder scheduled successfully!');
};

3. Handle Task Limits

import type { CommandData, ChatInputCommand } from 'commandkit';
import { ApplicationCommandOptionType } from 'discord.js';
import { createTask } from '@commandkit/tasks';

export const command: CommandData = {
name: 'schedule-reminder',
description: 'Schedule a reminder',
options: [
{
name: 'time',
description: 'The time to remind after. Eg: 6h, 10m, 1d',
type: ApplicationCommandOptionType.String,
required: true,
},
{
name: 'message',
description: 'The message to remind about.',
type: ApplicationCommandOptionType.String,
required: true,
},
],
};

export const chatInput: ChatInputCommand = async (ctx) => {
const userId = ctx.interaction.user.id;

// Check existing reminders
const existingReminders = await db.reminders.count({
where: { userId, active: true },
});

if (existingReminders >= 10) {
await ctx.interaction.reply(
'You already have 10 active reminders. Please cancel some first.',
);
return;
}

// Create new reminder
const timeStr = ctx.options.getString('time', true);
const message = ctx.options.getString('message', true);
const delay = ms(timeStr as `${number}`);

await createTask({
name: 'reminder',
data: { userId, message },
schedule: Date.now() + delay,
});

await ctx.interaction.reply('Reminder scheduled successfully!');
};

Next Steps