Create Your First Addon
Build one minimal addon that ItsMyBot can actually load: addon class, config, one command, and the correct build loop.
This page builds the smallest useful addon on purpose.
You are not trying to learn every addon folder here. You are trying to prove that you understand the addon flow from source files to a running feature.
What you are building
At the end of this page, you should have:
- one addon class in
src/addons/MyAddon/index.ts - one config validator in
resources/config.ts - one default config file in
resources/config.yml - one slash command in
interactions/pingCommand.ts
Understand the goal first
For a first addon, the important thing is not the command itself.
The real goal is to understand this chain:
- define an addon class
- load config in
load() - add one interaction file
- build the project
- confirm that ItsMyBot loads the compiled addon from
build/addons
If that chain makes sense, the rest of addon development becomes much easier.
Minimal structure
You can scaffold an addon with:
npm run make:addonFor learning, it helps to understand the structure it creates:
You do not need events/, leaderboards/, or any scripting extension folder yet.
Create the addon class
Every addon starts with one default export that extends Addon.
import { Addon, ConfigFile } from '@itsmybot';
import AddonConfig from './resources/config.js';
interface Configs {
config: ConfigFile;
}
export default class MyAddon extends Addon {
version = '0.0.1';
authors = ['YourName'];
description = 'My first ItsMyBot addon';
configs = {} as Configs;
async load() {
this.configs.config = await this.createConfig('config.yml', AddonConfig);
}
async initialize() {}
}What matters here:
index.tsis the addon entry pointload()is where you usually load config firstinitialize()can stay empty in a first addonconfigsis your typed place to keep loaded config files
Add one config file
Now give the addon one small config file so you understand how resources/ works.
Create resources/config.ts:
import { IsDefined, IsString } from 'class-validator';
export default class AddonConfig {
@IsDefined()
@IsString()
name: string;
}Create resources/config.yml:
name: "My addon"And load it in load():
this.configs.config = await this.createConfig('config.yml', AddonConfig);That line does three important jobs:
- it looks for
configs/MyAddon/config.yml - it creates the file from your default
resources/config.ymlif needed - it validates the loaded data with your
AddonConfigclass
That is why config loading belongs near the start of the addon lifecycle.
Add one slash command
Most first addons should begin with a slash command because it is the easiest thing to trigger on demand.
Create interactions/pingCommand.ts:
import { Command, CommandBuilder, User } from '@itsmybot';
import { ChatInputCommandInteraction } from 'discord.js';
import MyAddon from '..';
export default class PingCommand extends Command<MyAddon> {
build() {
return new CommandBuilder()
.setName('ping')
.setDescription('Check if the addon is alive');
}
async execute(interaction: ChatInputCommandInteraction<'cached'>, user: User) {
await interaction.reply({ content: 'Pong!' });
}
}Why this file works:
- it lives in
interactions/, so it is part of the addon interaction set - it exports one default class
- it extends
Command<MyAddon> build()defines the slash commandexecute()runs when the command is used
For a first addon, that is enough. Do not add buttons, modals, and extra commands yet.
Add translations only if you already need them
Addon translations live in resources/lang/.
You can read addon translation keys with:
this.lang.getString('path.to.key')For the first addon, hard-coded text is acceptable. Add translations once the addon flow itself already works.
Add addon-specific dependencies
If your addon needs a third-party package, create a package.json yourself directly in the addon folder.
{
"dependencies": {
"dayjs": "^1.11.13"
}
}ItsMyBot can then install those dependencies automatically for that addon.
Build before testing
Most first addon mistakes come from editing src/addons/... and then testing an old build/addons/... output.
ItsMyBot loads compiled addons from build/addons/<AddonName>, not from src/addons/<AddonName>.
The normal loop is:
npm run build
npm run startThe build step matters because it:
- compiles TypeScript into
build/ - copies YAML resources into
build/ - gives the runtime the files it actually reads
If the addon does not seem to update, check the build output first.
Know what to do next
Once this minimal addon works, you are ready for the next layer:
- open Add Configuration to understand config folders, nested config, and getters properly
- open Additional Features when you need interactions, events, leaderboards, or scripting-related extensions
- use
_Examplein the source repo only after the basic addon flow already makes sense
_Example is a reference, not the best first explanation.