zumito-framework 1.2.11 → 1.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,7 @@ import { TranslationManager } from './services/TranslationManager.js';
8
8
  import { EventManager } from './services/EventManager.js';
9
9
  import { CommandManager } from './services/CommandManager.js';
10
10
  import { ModuleManager } from './services/ModuleManager.js';
11
+ import { Route } from './definitions/Route.js';
11
12
  /**
12
13
  * @class ZumitoFramework
13
14
  * @description The main class of the framework.
@@ -64,7 +65,7 @@ export declare class ZumitoFramework {
64
65
  * @see {@link TranslationManager}
65
66
  */
66
67
  translations: TranslationManager;
67
- routes: any;
68
+ routes: Route[];
68
69
  /**
69
70
  * The database models loaded in the framework.
70
71
  * @type {Array<DatabaseModel>}
@@ -137,6 +138,7 @@ export declare class ZumitoFramework {
137
138
  */
138
139
  private registerModules;
139
140
  private registerModule;
141
+ private registerBundle;
140
142
  /**
141
143
  * Initializes the Discord client using the Discord.js library.
142
144
  * Logs in to the Discord API using the provided token and logs a message when the client is ready.
@@ -154,7 +156,7 @@ export declare class ZumitoFramework {
154
156
  *
155
157
  * @deprecated
156
158
  */
157
- memberHasPermission(member: GuildMember, channel: TextChannel, permission: bigint): Promise<any>;
159
+ memberHasPermission(member: GuildMember, channel: TextChannel, permission: bigint): Promise<boolean>;
158
160
  /**
159
161
  * Use GuildDataGetter service
160
162
  *
@@ -165,4 +167,5 @@ export declare class ZumitoFramework {
165
167
  * @deprecated
166
168
  */
167
169
  refreshSlashCommands(): Promise<void>;
170
+ registerRoute(route: Route): Promise<void>;
168
171
  }
@@ -79,7 +79,7 @@ export class ZumitoFramework {
79
79
  * @see {@link TranslationManager}
80
80
  */
81
81
  translations;
82
- routes;
82
+ routes = [];
83
83
  /**
84
84
  * The database models loaded in the framework.
85
85
  * @type {Array<DatabaseModel>}
@@ -161,11 +161,11 @@ export class ZumitoFramework {
161
161
  async initialize() {
162
162
  await this.initializeDatabase();
163
163
  await this.initializeDiscordClient();
164
- this.startApiServer();
165
164
  this.eventManager.addEventEmitter('discord', this.client);
166
165
  this.eventManager.addEventEmitter('framework', this.eventEmitter);
167
166
  await this.registerModules();
168
167
  await this.refreshSlashCommands();
168
+ this.startApiServer();
169
169
  if (this.settings.statusOptions) {
170
170
  this.statusManager = new StatusManager(this, this.settings.statusOptions);
171
171
  }
@@ -215,6 +215,11 @@ export class ZumitoFramework {
215
215
  //Route Prefixes
216
216
  //this.app.use("/", indexRouter);
217
217
  //this.app.use("/api/", apiRouter);
218
+ this.routes.forEach(route => {
219
+ this.app[route.method](route.path, function (req, res) {
220
+ return route.execute(req, res);
221
+ });
222
+ });
218
223
  // throw 404 if URL not found
219
224
  this.app.all('*', function (req, res) {
220
225
  return ApiResponse.notFoundResponse(res, 'Page not found');
@@ -241,7 +246,18 @@ export class ZumitoFramework {
241
246
  modulesFolder = `${process.cwd()}/src/modules`;
242
247
  }
243
248
  const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
249
+ if (this.settings.bundles && this.settings.bundles.length > 0) {
250
+ for (let bundle of this.settings.bundles) {
251
+ await this.registerBundle(bundle.path, bundle.options);
252
+ }
253
+ }
244
254
  await this.registerModule(path.join(__dirname, 'modules', 'core'), 'baseModule');
255
+ if (fs.existsSync(`${process.cwd()}/node_modules/.zumitoBundles`)) {
256
+ const files = fs.readdirSync(`${process.cwd()}/node_modules/.zumitoBundles`);
257
+ for (const file of files) {
258
+ await this.registerModule(`${process.cwd()}/node_modules/.zumitoBundles`, file);
259
+ }
260
+ }
245
261
  if (modulesFolder) {
246
262
  const files = fs.readdirSync(modulesFolder);
247
263
  for (const file of files) {
@@ -277,6 +293,13 @@ export class ZumitoFramework {
277
293
  // Register module in the framework
278
294
  this.modules.registerModule(moduleInstance);
279
295
  }
296
+ async registerBundle(bundlePath, bundleOptions) {
297
+ console.log(bundlePath);
298
+ const bundle = await this.modules.loadModuleFile(bundlePath);
299
+ const bundleName = path.basename(bundlePath);
300
+ const moduleInstance = await this.modules.instanceModule(bundle, bundlePath, bundleName, bundleOptions);
301
+ this.modules.registerModule(moduleInstance);
302
+ }
280
303
  /**
281
304
  * Initializes the Discord client using the Discord.js library.
282
305
  * Logs in to the Discord API using the provided token and logs a message when the client is ready.
@@ -311,7 +334,7 @@ export class ZumitoFramework {
311
334
  */
312
335
  async memberHasPermission(member, channel, permission) {
313
336
  const memberPermissionChecker = ServiceContainer.getService(MemberPermissionChecker);
314
- return await memberPermissionChecker(member, channel, permission);
337
+ return await memberPermissionChecker.hasPermissionOnChannel(member, channel, permission);
315
338
  }
316
339
  /**
317
340
  * Use GuildDataGetter service
@@ -329,4 +352,7 @@ export class ZumitoFramework {
329
352
  const slashCommandRefresher = ServiceContainer.getService(SlashCommandRefresher);
330
353
  slashCommandRefresher.refreshSlashCommands();
331
354
  }
355
+ async registerRoute(route) {
356
+ this.routes.push(route);
357
+ }
332
358
  }
@@ -1,5 +1,6 @@
1
1
  export declare abstract class FrameworkEvent {
2
- once: boolean;
3
2
  disabled: boolean;
3
+ once: boolean;
4
+ source: string;
4
5
  abstract execute(...args: any[]): any;
5
6
  }
@@ -1,4 +1,5 @@
1
1
  export class FrameworkEvent {
2
- once = false;
3
2
  disabled = false;
3
+ once = false;
4
+ source;
4
5
  }
@@ -1,3 +1,4 @@
1
+ import { ModuleParameters } from "./parameters/ModuleParameters";
1
2
  import { StatusManagerOptions } from "./StatusManagerOptions";
2
3
  export interface FrameworkSettings {
3
4
  database: any;
@@ -11,4 +12,8 @@ export interface FrameworkSettings {
11
12
  defaultPrefix?: string;
12
13
  statusOptions?: StatusManagerOptions;
13
14
  srcMode?: 'multiBundle' | 'monoBundle' | undefined;
15
+ bundles?: {
16
+ path: string;
17
+ options?: ModuleParameters;
18
+ }[];
14
19
  }
@@ -24,4 +24,6 @@ export declare abstract class Module {
24
24
  registerTranslations(subpath?: string): Promise<void>;
25
25
  registerModels(): Promise<void>;
26
26
  getModels(): Array<DatabaseModel>;
27
+ registerRoutes(): Promise<void>;
28
+ registerRoutesFolder(folder: string): Promise<void>;
27
29
  }
@@ -23,6 +23,7 @@ export class Module {
23
23
  await this.registerEvents();
24
24
  await this.registerTranslations();
25
25
  await this.registerModels();
26
+ await this.registerRoutes();
26
27
  }
27
28
  async registerCommands() {
28
29
  const commandsFolder = path.join(this.path, 'commands');
@@ -69,7 +70,7 @@ export class Module {
69
70
  event = Object.values(event)[0];
70
71
  event = new event();
71
72
  this.events.set(event.constructor.name.toLowerCase(), event);
72
- this.registerEvent(event, folder);
73
+ this.registerEvent(event, event.source);
73
74
  }
74
75
  }
75
76
  }
@@ -130,4 +131,28 @@ export class Module {
130
131
  getModels() {
131
132
  return this.models;
132
133
  }
134
+ async registerRoutes() {
135
+ const folderPath = path.join(this.path, 'routes');
136
+ if (fs.existsSync(folderPath)) {
137
+ await this.registerRoutesFolder('');
138
+ }
139
+ }
140
+ async registerRoutesFolder(folder) {
141
+ const folderPath = path.join(this.path, 'routes', folder);
142
+ if (!fs.existsSync(folderPath))
143
+ throw new Error(`Folder ${folder} doesn't exist`);
144
+ const files = fs.readdirSync(folderPath);
145
+ for (const file of files) {
146
+ if (file.endsWith('d.ts'))
147
+ continue;
148
+ if (file.endsWith('.js') || file.endsWith('.ts')) {
149
+ let route = await import('file://' + path.join(folderPath, file)).catch((e) => {
150
+ console.error(`[🔄🔴 ] Error loading ${file.slice(0, -3)} route on module ${this.constructor.name}`);
151
+ });
152
+ route = Object.values(route)[0];
153
+ route = new route();
154
+ this.framework.registerRoute(route);
155
+ }
156
+ }
157
+ }
133
158
  }
@@ -0,0 +1,13 @@
1
+ import { Request, Response } from "express";
2
+ export declare enum RouteMethod {
3
+ get = "get",
4
+ post = "post",
5
+ put = "put",
6
+ delete = "delete",
7
+ all = "all"
8
+ }
9
+ export declare abstract class Route {
10
+ method: RouteMethod;
11
+ path: string;
12
+ abstract execute(req: Request, res: Response): any;
13
+ }
@@ -0,0 +1,12 @@
1
+ export var RouteMethod;
2
+ (function (RouteMethod) {
3
+ RouteMethod["get"] = "get";
4
+ RouteMethod["post"] = "post";
5
+ RouteMethod["put"] = "put";
6
+ RouteMethod["delete"] = "delete";
7
+ RouteMethod["all"] = "all";
8
+ })(RouteMethod || (RouteMethod = {}));
9
+ export class Route {
10
+ method;
11
+ path;
12
+ }
package/dist/index.d.ts CHANGED
@@ -26,4 +26,5 @@ import { GuildDataGetter } from './services/GuildDataGetter.js';
26
26
  import { CommandParser } from './services/CommandParser.js';
27
27
  import { SlashCommandRefresher } from './services/SlashCommandRefresher.js';
28
28
  import { ErrorHandler } from './services/ErrorHandler.js';
29
- export { ZumitoFramework, FrameworkSettings, Command, Module, CommandParameters, CommandArguments, FrameworkEvent, Translation, TranslationManager, ApiResponse, SelectMenuParameters, CommandType, CommandArgDefinition, CommandChoiceDefinition, ButtonPressed, ButtonPressedParams, TextFormatter, EmojiFallback, DatabaseConfigLoader, DatabaseModel, PresenceDataRule, RuledPresenceData, StatusManagerOptions, discord, EventParameters, ServiceContainer, GuildDataGetter, SlashCommandRefresher, CommandParser, ErrorHandler, };
29
+ import { Route, RouteMethod } from './definitions/Route.js';
30
+ export { ZumitoFramework, FrameworkSettings, Command, Module, CommandParameters, CommandArguments, FrameworkEvent, Translation, TranslationManager, ApiResponse, SelectMenuParameters, CommandType, CommandArgDefinition, CommandChoiceDefinition, ButtonPressed, ButtonPressedParams, TextFormatter, EmojiFallback, DatabaseConfigLoader, DatabaseModel, PresenceDataRule, RuledPresenceData, StatusManagerOptions, discord, EventParameters, ServiceContainer, GuildDataGetter, SlashCommandRefresher, CommandParser, ErrorHandler, Route, RouteMethod };
package/dist/index.js CHANGED
@@ -19,11 +19,12 @@ import { MemberPermissionChecker } from './services/MemberPermissionChecker.js';
19
19
  import { CommandParser } from './services/CommandParser.js';
20
20
  import { SlashCommandRefresher } from './services/SlashCommandRefresher.js';
21
21
  import { ErrorHandler } from './services/ErrorHandler.js';
22
+ import { Route, RouteMethod } from './definitions/Route.js';
22
23
  ServiceContainer.addService(TextFormatter, []);
23
- ServiceContainer.addService(EmojiFallback, []);
24
+ ServiceContainer.addService(EmojiFallback, [discord.Client.name, TranslationManager.name]);
24
25
  ServiceContainer.addService(GuildDataGetter, [ZumitoFramework.name]);
25
26
  ServiceContainer.addService(MemberPermissionChecker, []);
26
27
  ServiceContainer.addService(CommandParser, []);
27
28
  ServiceContainer.addService(SlashCommandRefresher, [ZumitoFramework.name]);
28
29
  ServiceContainer.addService(ErrorHandler, ['ZumitoFramework']);
29
- export { ZumitoFramework, Command, Module, CommandArguments, FrameworkEvent, Translation, TranslationManager, ApiResponse, CommandType, ButtonPressed, TextFormatter, EmojiFallback, DatabaseConfigLoader, DatabaseModel, discord, ServiceContainer, GuildDataGetter, SlashCommandRefresher, CommandParser, ErrorHandler, };
30
+ export { ZumitoFramework, Command, Module, CommandArguments, FrameworkEvent, Translation, TranslationManager, ApiResponse, CommandType, ButtonPressed, TextFormatter, EmojiFallback, DatabaseConfigLoader, DatabaseModel, discord, ServiceContainer, GuildDataGetter, SlashCommandRefresher, CommandParser, ErrorHandler, Route, RouteMethod };
@@ -3,6 +3,7 @@ import { EventParameters } from '../../../../../definitions/parameters/EventPara
3
3
  import { FrameworkEvent } from '../../../../../definitions/FrameworkEvent.js';
4
4
  export declare class InteractionCreate extends FrameworkEvent {
5
5
  once: boolean;
6
+ source: string;
6
7
  execute({ interaction, client, framework, }: EventParameters): Promise<void>;
7
8
  getTransMethod(commandInstance: Command, framework: any, guildSettings: any): (key: string, params?: any) => any;
8
9
  }
@@ -2,6 +2,7 @@ import { CommandType } from '../../../../../definitions/commands/CommandType.js'
2
2
  import { FrameworkEvent } from '../../../../../definitions/FrameworkEvent.js';
3
3
  export class InteractionCreate extends FrameworkEvent {
4
4
  once = false;
5
+ source = 'discord';
5
6
  async execute({ interaction, client, framework, }) {
6
7
  let guildSettings;
7
8
  if (interaction.guildId) {
@@ -3,6 +3,7 @@ import { EventParameters } from '../../../../../definitions/parameters/EventPara
3
3
  import { FrameworkEvent } from '../../../../../definitions/FrameworkEvent.js';
4
4
  export declare class MessageCreate extends FrameworkEvent {
5
5
  once: boolean;
6
+ source: string;
6
7
  execute({ message, framework }: EventParameters): Promise<import("discord.js").Message<boolean>>;
7
8
  autocorrect(str: string, words: string[]): any;
8
9
  getErrorEmbed(error: any, parse: any): {
@@ -7,6 +7,7 @@ import path from 'path';
7
7
  import { InteractionIdGenerator } from '../../../../../services/InteractionIdGenerator.js';
8
8
  export class MessageCreate extends FrameworkEvent {
9
9
  once = false;
10
+ source = 'discord';
10
11
  async execute({ message, framework }) {
11
12
  const channel = message.channel;
12
13
  const prefix = framework.settings.defaultPrefix;
@@ -1,6 +1,10 @@
1
1
  import { Client } from 'discord.js';
2
+ import { TranslationManager } from './TranslationManager';
2
3
  export declare class EmojiFallback {
3
- static getEmoji(client: Client, emojiId: string, fallbackEmoji: any): any;
4
- static getEmojiByName(client: Client, emojiName: string, fallbackEmoji: any): any;
5
- static getEmojiByIdentifier(client: Client, emojiId: string, fallbackEmoji: any): any;
4
+ client: Client;
5
+ translator: TranslationManager;
6
+ constructor(client: Client, translator: TranslationManager);
7
+ getEmoji(emojiId: string, fallbackEmoji: any): any;
8
+ getEmojiByName(emojiName: string, fallbackEmoji: any): any;
9
+ getEmojiByIdentifier(emojiId: string, fallbackEmoji: any): any;
6
10
  }
@@ -1,14 +1,26 @@
1
1
  export class EmojiFallback {
2
- static getEmoji(client, emojiId, fallbackEmoji) {
3
- const emoji = client.emojis.cache.get(emojiId);
2
+ client;
3
+ translator;
4
+ constructor(client, translator) {
5
+ this.client = client;
6
+ this.translator = translator;
7
+ }
8
+ getEmoji(emojiId, fallbackEmoji) {
9
+ if (this.translator.has(emojiId)) {
10
+ emojiId = this.translator.get(emojiId);
11
+ }
12
+ const emoji = this.client.emojis.cache.get(emojiId);
4
13
  return emoji?.toString() || fallbackEmoji;
5
14
  }
6
- static getEmojiByName(client, emojiName, fallbackEmoji) {
7
- const emoji = client.emojis.cache.find((emoji) => emoji.name === emojiName);
15
+ getEmojiByName(emojiName, fallbackEmoji) {
16
+ const emoji = this.client.emojis.cache.find((emoji) => emoji.name === emojiName);
8
17
  return emoji?.toString() || fallbackEmoji;
9
18
  }
10
- static getEmojiByIdentifier(client, emojiId, fallbackEmoji) {
11
- const emoji = client.emojis.cache.find((emoji) => emoji.id === emojiId);
19
+ getEmojiByIdentifier(emojiId, fallbackEmoji) {
20
+ if (this.translator.has(emojiId)) {
21
+ emojiId = this.translator.get(emojiId);
22
+ }
23
+ const emoji = this.client.emojis.cache.find((emoji) => emoji.id === emojiId);
12
24
  return emoji?.toString() || fallbackEmoji;
13
25
  }
14
26
  }
@@ -1,5 +1,6 @@
1
1
  import { ZumitoFramework } from "../ZumitoFramework.js";
2
2
  import { Module } from "../definitions/Module.js";
3
+ import { ModuleParameters } from "../definitions/parameters/ModuleParameters.js";
3
4
  export declare class ModuleManager {
4
5
  protected modules: Map<string, Module>;
5
6
  protected framework: ZumitoFramework;
@@ -13,5 +14,5 @@ export declare class ModuleManager {
13
14
  get size(): number;
14
15
  loadModuleFile(folderPath: string): Promise<unknown>;
15
16
  registerModule(module: InstanceType<typeof Module>): void;
16
- instanceModule(module: any, rootPath: string, name?: string): Promise<Module>;
17
+ instanceModule(module: any, rootPath: string, name?: string, options?: ModuleParameters): Promise<Module>;
17
18
  }
@@ -57,11 +57,11 @@ export class ModuleManager {
57
57
 
58
58
  */
59
59
  }
60
- async instanceModule(module, rootPath, name) {
60
+ async instanceModule(module, rootPath, name, options) {
61
61
  let moduleInstance;
62
62
  if (module.constructor) {
63
63
  try {
64
- moduleInstance = new module(rootPath);
64
+ moduleInstance = new module(rootPath, options);
65
65
  await moduleInstance.initialize();
66
66
  this.modules.set(name || moduleInstance.constructor.name, moduleInstance);
67
67
  }
@@ -1,8 +1 @@
1
- declare class ServiceContainerManager {
2
- private services;
3
- addService(serviceClass: any, dependencies: string[], singleton?: boolean, instance?: any): void;
4
- getService(serviceClass: any): any;
5
- addInstance(serviceClass: any, instance: any): void;
6
- }
7
- export declare const ServiceContainer: ServiceContainerManager;
8
- export {};
1
+ export declare const ServiceContainer: any;
@@ -28,4 +28,6 @@ class ServiceContainerManager {
28
28
  this.services.get(serviceName).instance = instance;
29
29
  }
30
30
  }
31
- export const ServiceContainer = new ServiceContainerManager();
31
+ if (!global.ServiceContainer)
32
+ global.ServiceContainer = new ServiceContainerManager();
33
+ export const ServiceContainer = global.ServiceContainer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zumito-framework",
3
- "version": "1.2.11",
3
+ "version": "1.2.13",
4
4
  "description": "Discord.js bot framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -22,10 +22,10 @@
22
22
  <p align="center">
23
23
  Fast and scalable discord bot framework to jumpstart your projects!
24
24
  <br />
25
- <a href="https://docs.zumito.ga/"><strong>Explore the docs »</strong></a>
25
+ <a href="https://docs.zumito.dev/"><strong>Explore the docs »</strong></a>
26
26
  <br />
27
27
  <br />
28
- <a href="https://zumito.ga">View Demo</a>
28
+ <a href="#">View Demo (Coming soon)</a>
29
29
  ·
30
30
  <a href="https://github.com/ZumitoTeam/zumito-framework/issues">Report Bug</a>
31
31
  ·
@@ -72,7 +72,7 @@ With this framework, you can easily create a feature-rich bot that solves a prob
72
72
 
73
73
  The framework is constantly evolving, with new features and updates being added on a regular basis. You can also suggest changes and contribute to the development of the framework by forking the repo and creating a pull request or opening an issue. Thanks to all the contributors who have helped expand this framework!
74
74
 
75
- To get started, simply visit the quickstart guide at [docs.zumito.ga/docs/custom/quickstart](https://docs.zumito.ga/docs/custom/quickstart) and start building your bot today!
75
+ To get started, simply visit the quickstart guide at [docs.zumito.ga/docs/custom/quickstart](https://docs.zumito.dev/guides/start/) and start building your bot today!
76
76
 
77
77
  <p align="right">(<a href="#readme-top">back to top</a>)</p>
78
78
 
@@ -85,9 +85,7 @@ This section lists the major frameworks and libraries that the framework is buil
85
85
  * [Node.js](https://nodejs.org/en/)
86
86
  * [TypeScript](https://www.typescriptlang.org/)
87
87
  * [Discord.js](https://discord.js.org/#/)
88
- * [MongoDB](https://www.mongodb.com/)
89
88
  * [Express](https://expressjs.com/)
90
- * [Mongoose](https://mongoosejs.com/)
91
89
 
92
90
  <p align="right">(<a href="#readme-top">back to top</a>)</p>
93
91
 
@@ -98,7 +96,7 @@ This section lists the major frameworks and libraries that the framework is buil
98
96
 
99
97
  This is a guide of how you can set up the project locally to start contributing to the framework.
100
98
 
101
- :warning: If you're here to create your own bot, refer to the quickstart guide at [docs.zumito.ga/docs/custom/quickstart](https://docs.zumito.ga/docs/custom/quickstart).
99
+ :warning: If you're here to create your own bot, refer to the quickstart guide at [docs.zumito.dev/guides/start](https://docs.zumito.dev/guides/start/).
102
100
 
103
101
  ### Prerequisites
104
102
 
@@ -135,7 +133,7 @@ For build the source code, run the following command:
135
133
  npm run build
136
134
  ```
137
135
 
138
- Then for test it, you will need to create a bot, you can find a guide [here](https://docs.zumito.ga/docs/custom/quickstart).
136
+ Then for test it, you will need to create a bot, you can find a guide [here](https://docs.zumito.dev/guides/start).
139
137
 
140
138
  for link the framework to your bot, you will need to run these commands
141
139
  1. In the root of framework:
@@ -148,8 +146,6 @@ for link the framework to your bot, you will need to run these commands
148
146
  npm link zumito-framework
149
147
  ```
150
148
 
151
- _For more info of the framework, please refer to the [Documentation](https://docs.zumito.ga/docs/category/zumito-framework)_
152
-
153
149
  <p align="right">(<a href="#readme-top">back to top</a>)</p>
154
150
 
155
151
 
@@ -157,9 +153,9 @@ _For more info of the framework, please refer to the [Documentation](https://doc
157
153
  <!-- ROADMAP -->
158
154
  ## Roadmap
159
155
 
160
- - [X] Add Quick start template
161
- - [ ] Add more documentation, examples, and guides
162
- - [ ] Add cli for easy command, events, etc boilerplate generation.
156
+ <!-- issueTable -->
157
+
158
+ <!-- issueTable -->
163
159
 
164
160
  See the [open issues](https://github.com/ZumitoTeam/zumito-framework/issues) for a full list of proposed features (and known issues).
165
161
 
@@ -209,6 +205,7 @@ Any questions or suggestions? Come to our [Discord server](https://discord.gg/rF
209
205
  We would like to thank the following projects for their inspiration and/or help:
210
206
 
211
207
  * [Discord.js](https://discord.js.org)
208
+ * [Caminte](https://github.com/biggora/caminte)
212
209
 
213
210
  <p align="right">(<a href="#readme-top">back to top</a>)</p>
214
211