1 |
--- |
2 |
title: Creating Extensions for SudoBot v9 |
3 |
short_name: Creating Extensions for SudoBot v9 |
4 |
--- |
5 |
|
6 |
import Callout from "@/components/Alerts/Callout"; |
7 |
|
8 |
# Creating Extensions for SudoBot v9 |
9 |
|
10 |
SudoBot has support for extensions, and it was introduced in version 6.17. Extensions can extend the bot's feature, by adding commands, event listeners, services and a lot more stuff. You can install/uninstall/disable extensions, or create your own to make the bot behave exactly how you want. Extensions can be written using JavaScript and TypeScript. |
11 |
|
12 |
In this article, you'll learn how SudoBot's extension system works. |
13 |
|
14 |
## The extensions directory |
15 |
|
16 |
To start using extensions, you need to create a root directory where extensions will be installed. |
17 |
In this guide, we'll name the directory `installed_extensions/`. To create a directory named `installed_extensions/` in the main project root, run the following: |
18 |
|
19 |
```bash |
20 |
mkdir installed_extensions |
21 |
``` |
22 |
|
23 |
Every installed extension goes inside this directory. |
24 |
|
25 |
Now to tell SudoBot where to look for extensions, add the `EXTENSIONS_DIRECTORY` environment variable to your `.env` file: |
26 |
|
27 |
```bash |
28 |
# Path to the extensions root directory |
29 |
EXTENSIONS_DIRECTORY=/example/path/to/extensions/directory |
30 |
``` |
31 |
|
32 |
Each installed extension has a directory associated with it inside the `installed_extensions/` directory. Inside of that inner directory, there is a special file `extension.json` which contains meta information about the extension, and how to build it. |
33 |
|
34 |
The `extension.json` file looks something like this: |
35 |
|
36 |
```json5 |
37 |
{ |
38 |
main: "./build/index.js" /* The entry point. */, |
39 |
commands: "./build/commands" /* Commands directory. The bot will load commands from this directory. */, |
40 |
events: "./build/events" /* Event handlers directory. The bot will load event handlers from this directory. */, |
41 |
language: "typescript" /* The language being used for this extension. Can be either "javascript" or "typescript". */, |
42 |
main_directory: "./build" /* The main directory where the entry point is located. */, |
43 |
build_command: "npm run build" /* Command to build the extension. In this case `npm run build` invokes `tsc`. */, |
44 |
} |
45 |
``` |
46 |
|
47 |
## Creating your first SudoBot extension |
48 |
|
49 |
To get started, first create a directory named `installed_extensions` inside the project root. In that directory, create another directory for your extension. The name of this directory usually should be your extension's name. In this example, we'll name the extension "hello". |
50 |
|
51 |
Then inside your extension's directory, create the `extension.json` file, and the `src/` directory. Inside `src`, create `events` and `commands` directories. The final directory tree should look something like this: |
52 |
|
53 |
``` |
54 |
+ sudobot/ [project root] |
55 |
+ installed_extensions/ |
56 |
+ hello/ |
57 |
+ src/ |
58 |
+ commands/ |
59 |
+ events/ |
60 |
- extension.json |
61 |
``` |
62 |
|
63 |
Now add the following to your `extension.json` file: |
64 |
|
65 |
```json |
66 |
{ |
67 |
"main": "./build/index.js", |
68 |
"commands": "./build/commands", |
69 |
"events": "./build/events", |
70 |
"language": "typescript", |
71 |
"main_directory": "./build", |
72 |
"build_command": "npm run build" |
73 |
} |
74 |
``` |
75 |
|
76 |
We'll be using TypeScript to write the extension in this example. If you'd like to use JavaScript instead, you can set `language` to `javascript` and you don't need to specify a build command, and your main directory will be the directory where you put your JavaScript files (usually `src/`). You should also adjust the paths to point to that directory (rather than `build/` which is used in this example). |
77 |
|
78 |
#### Setting up TypeScript and Dependencies |
79 |
|
80 |
First, run `npm init` to initialize your extension project. This will ask you a few questions and create a `package.json` file. Then run: |
81 |
|
82 |
```bash |
83 |
npm install --save ../.. # Path to the sudobot project root |
84 |
``` |
85 |
|
86 |
<Callout type="info"> |
87 |
Remember **this is a really important step** to make sure your extension can |
88 |
access SudoBot's core utilities to initialize itself. If you don't link |
89 |
SudoBot with your extension, it will fail to import the necessary files. |
90 |
</Callout> |
91 |
|
92 |
Then we can go ahead and install the dependencies and also set up TypeScript. |
93 |
|
94 |
```shell |
95 |
npm install module-alias |
96 |
npm install -D typescript @types/node |
97 |
npx tsc --init |
98 |
``` |
99 |
|
100 |
This will add `typescript` as a dev dependency and also create the `tsconfig.json` file which contains the configuration for the TypeScript compiler. |
101 |
|
102 |
Now open up `tsconfig.json` file, and add the following (you can tweak these options if you want): |
103 |
|
104 |
```json |
105 |
{ |
106 |
"compilerOptions": { |
107 |
"target": "ES2021", |
108 |
"module": "commonjs", |
109 |
"rootDir": "./src", |
110 |
"baseUrl": "./", |
111 |
"paths": { |
112 |
"@sudobot/*": ["node_modules/sudobot/build/out/main/typescript/*"], |
113 |
"@framework/*": [ |
114 |
"node_modules/sudobot/build/out/framework/typescript/*" |
115 |
] |
116 |
}, |
117 |
"resolveJsonModule": true, |
118 |
"outDir": "./build", |
119 |
"newLine": "lf", |
120 |
"noEmitHelpers": true, |
121 |
"noEmitOnError": true, |
122 |
"allowSyntheticDefaultImports": true, |
123 |
"esModuleInterop": true, |
124 |
"forceConsistentCasingInFileNames": true, |
125 |
"strict": true, |
126 |
"skipLibCheck": true |
127 |
}, |
128 |
"exclude": ["./tests", "./build"] |
129 |
} |
130 |
``` |
131 |
|
132 |
This sets up the `@sudobot` and `@framework` import aliases for TypeScript, specifies the source root and build directory, and a few other things that are needed. |
133 |
|
134 |
<Callout type="info"> |
135 |
Remember to build the bot beforehand! As you can see, this alias points to |
136 |
the `build` directory which is created when you build the bot. |
137 |
</Callout> |
138 |
|
139 |
Then open up `package.json` file and add the following inside the root object: |
140 |
|
141 |
```json |
142 |
"_moduleAliases": { |
143 |
"@framework": "node_modules/sudobot/build/out/framework/typescript", |
144 |
"@sudobot": "node_modules/sudobot/build/out/main/typescript" |
145 |
}, |
146 |
"scripts": { |
147 |
"build": "tsc" |
148 |
} |
149 |
``` |
150 |
|
151 |
You might be thinking, why do we need to add the module aliases twice? It's because TypeScript doesn't actually deal with these module aliases, it just checks the types and imports. In runtime, we need another way to resolve these imports. We use `module-alias` for that. |
152 |
|
153 |
#### The entry point |
154 |
|
155 |
We need to create the entry point now! Make a file `src/index.ts` and put the following code inside of that file: |
156 |
|
157 |
```typescript |
158 |
import "module-alias/register"; |
159 |
import { Extension } from "@sudobot/core/Extension"; |
160 |
|
161 |
class HelloExtension extends Extension { |
162 |
// ... |
163 |
} |
164 |
|
165 |
export default HelloExtension; |
166 |
``` |
167 |
|
168 |
That's actually all we need inside this file. |
169 |
|
170 |
#### Adding commands to the extension |
171 |
|
172 |
Alright, let's add a command to the extension! Create a file `src/commands/HelloCommand.ts` and inside of that file, put the following code: |
173 |
|
174 |
```typescript |
175 |
import { Command, type CommandMessage } from "@framework/commands/Command"; |
176 |
import type Context from "@framework/commands/Context"; |
177 |
|
178 |
class HelloCommand extends Command { |
179 |
public override readonly name = "hello"; |
180 |
public override readonly description = "A simple hello-world command."; |
181 |
|
182 |
public override async execute(context: Context<CommandMessage>) { |
183 |
await context.reply("Hello world, from the hello extension!"); |
184 |
} |
185 |
} |
186 |
|
187 |
export default HelloCommand; |
188 |
``` |
189 |
|
190 |
This command just responds to the user with "Hello world, from the hello extension!". |
191 |
|
192 |
#### Adding event listeners to the extension |
193 |
|
194 |
Now, let's add an event listener to the extension! Create a file `src/events/MessageCreateEventListener.ts` and inside of that file, put the following code: |
195 |
|
196 |
```typescript |
197 |
import EventListener from "@framework/events/EventListener"; |
198 |
import { Events } from "@framework/types/ClientEvents"; |
199 |
import type { Message } from "discord.js"; |
200 |
|
201 |
class MessageCreateEventListener extends EventListener<Events.MessageCreate> { |
202 |
public override readonly name = Events.MessageCreate; |
203 |
|
204 |
public override async execute(message: Message<boolean>): Promise<void> { |
205 |
if (message.author.bot) { |
206 |
return; |
207 |
} |
208 |
|
209 |
if (message.content === "ping") { |
210 |
await message.reply("Pong, from the hello extension!"); |
211 |
} |
212 |
} |
213 |
} |
214 |
|
215 |
export default MessageCreateEventListener; |
216 |
``` |
217 |
|
218 |
This event listener listens to `MessageCreate` event, and whenever someone sends a message with content "ping", it will reply to them. |
219 |
|
220 |
#### Building the extension |
221 |
|
222 |
Building your newly created extension involves the same procedures as any other TypeScript project. |
223 |
Install the dependencies and run the TypeScript compiler from the extension's directory (installed_extensions/hello): |
224 |
|
225 |
```bash |
226 |
npm install -D |
227 |
npm run build |
228 |
``` |
229 |
|
230 |
If using [Bun](https://bun.sh): |
231 |
|
232 |
```bash |
233 |
bun install -D |
234 |
bun run build |
235 |
``` |
236 |
|
237 |
This will take a little bit time. After that, you're ready to go. You can now start the bot from the main project root (assuming you've built it already): |
238 |
|
239 |
```bash |
240 |
npm start |
241 |
``` |
242 |
|
243 |
Or using Bun (no build step required): |
244 |
|
245 |
```bash |
246 |
bun dev |
247 |
``` |
248 |
|
249 |
And then if everything was configured correctly, the `hello` command will be loaded and can be executed on any server. |
250 |
|
251 |
Congratulations, you've just built an extension for SudoBot! |
252 |
|
253 |
### Help and Support |
254 |
|
255 |
If you need help with anything, feel free to create a discussion topic at the [GitHub repo](https://github.com/onesoft-sudo/sudobot). You can also contact via email at [[email protected]](mailto:[email protected]), or join our [Discord Server](https://discord.gg/892GWhTzgs). |