zumito-framework 1.1.33 → 1.1.35

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.
@@ -4,7 +4,12 @@ export class TranslationManager {
4
4
  defaultLanguage = 'en';
5
5
  constructor() { }
6
6
  get(key, language, params) {
7
- return this.translations.get(key).get(language, params);
7
+ if (this.translations.has(key)) {
8
+ return this.translations.get(key).get(language, params);
9
+ }
10
+ else {
11
+ return key;
12
+ }
8
13
  }
9
14
  set(key, language, text) {
10
15
  if (!this.translations.has(key)) {
@@ -46,4 +46,5 @@ export declare class ZumitoFramework {
46
46
  private initializeDiscordClient;
47
47
  static splitCommandLine(commandLine: any): any;
48
48
  memberHasPermission(member: GuildMember, channel: TextChannel, permission: bigint): Promise<boolean>;
49
+ getGuildSettings(guildId: string): Promise<any>;
49
50
  }
@@ -13,6 +13,7 @@ import mongoose from "mongoose";
13
13
  import cookieParser from 'cookie-parser';
14
14
  import cors from 'cors';
15
15
  import http from 'http';
16
+ import * as url from 'url';
16
17
  /**
17
18
  * @class ZumitoFramework
18
19
  * @classdesc The main class of the framework.
@@ -28,7 +29,7 @@ export class ZumitoFramework {
28
29
  modules;
29
30
  commands;
30
31
  events;
31
- translations = new TranslationManager();
32
+ translations;
32
33
  routes;
33
34
  models;
34
35
  database;
@@ -54,9 +55,12 @@ export class ZumitoFramework {
54
55
  this.events = new Map();
55
56
  this.translations = new TranslationManager();
56
57
  this.models = new Map();
58
+ if (settings.logLevel) {
59
+ console.logLevel = settings.logLevel;
60
+ }
57
61
  this.initialize().then(() => {
58
62
  if (callback)
59
- callback();
63
+ callback(this);
60
64
  }).catch(err => {
61
65
  console.error(err, err.message, err.stack, err.name);
62
66
  });
@@ -75,7 +79,7 @@ export class ZumitoFramework {
75
79
  }
76
80
  this.initializeDiscordClient();
77
81
  this.startApiServer();
78
- this.registerModules();
82
+ await this.registerModules();
79
83
  }
80
84
  startApiServer() {
81
85
  this.app = express();
@@ -108,7 +112,7 @@ export class ZumitoFramework {
108
112
  }
109
113
  });
110
114
  }
111
- registerModules() {
115
+ async registerModules() {
112
116
  let modulesFolder;
113
117
  if (fs.existsSync(`${process.cwd()}/modules`)) {
114
118
  modulesFolder = `${process.cwd()}/modules`;
@@ -118,10 +122,12 @@ export class ZumitoFramework {
118
122
  }
119
123
  else
120
124
  return;
121
- this.registerModule(path.resolve(), 'baseModule', baseModule);
122
- fs.readdirSync(modulesFolder).forEach(file => {
123
- this.registerModule(modulesFolder, file);
124
- });
125
+ const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
126
+ await this.registerModule(__dirname, 'baseModule', baseModule);
127
+ let files = fs.readdirSync(modulesFolder);
128
+ for (let file of files) {
129
+ await this.registerModule(modulesFolder, file);
130
+ }
125
131
  this.models.forEach((modelDefiniton, modelName) => {
126
132
  const schema = new mongoose.Schema(modelDefiniton);
127
133
  this.models.set(modelName, mongoose.model(modelName, schema));
@@ -147,12 +153,18 @@ export class ZumitoFramework {
147
153
  try {
148
154
  moduleInstance = new module(path.join(modulesFolder, moduleName), this);
149
155
  await moduleInstance.initialize();
150
- this.modules.set(moduleInstance.constructor.name, moduleInstance);
156
+ this.modules.set(moduleName || moduleInstance.constructor.name, moduleInstance);
151
157
  }
152
158
  catch (err) {
153
159
  console.error(`[📦🔴] Error loading module ${moduleName}: ${err.message}`);
160
+ console.error(err.stack);
154
161
  }
155
162
  // Register module commands
163
+ if (moduleInstance.getCommands()) {
164
+ moduleInstance.getCommands().forEach((command) => {
165
+ this.commands.set(command.name, command);
166
+ });
167
+ }
156
168
  this.commands = new Map([...this.commands, ...moduleInstance.getCommands()]);
157
169
  // Register module events
158
170
  this.events = new Map([...this.events, ...moduleInstance.getEvents()]);
@@ -210,6 +222,13 @@ export class ZumitoFramework {
210
222
  let memberPermission = await channel.permissionsFor(member);
211
223
  return memberPermission.has(permission);
212
224
  }
225
+ async getGuildSettings(guildId) {
226
+ let guild = await this.models.get('Guild').findOne({ guild_id: guildId });
227
+ if (guild == null) {
228
+ guild = await this.models.get('Guild').create({ guild_id: guildId });
229
+ }
230
+ return guild;
231
+ }
213
232
  }
214
233
  function MergeRecursive(obj1, obj2) {
215
234
  for (var p in obj2) {
@@ -3,6 +3,10 @@ import { FrameworkEvent } from "../../../types/FrameworkEvent.js";
3
3
  export class InteractionCreate extends FrameworkEvent {
4
4
  once = false;
5
5
  async execute({ interaction, client, framework }) {
6
+ let guildSettings;
7
+ if (interaction.guildId) {
8
+ guildSettings = await framework.getGuildSettings(interaction.guildId);
9
+ }
6
10
  if (interaction.isCommand()) {
7
11
  if (!framework.commands.has(interaction.commandName))
8
12
  return;
@@ -17,10 +21,10 @@ export class InteractionCreate extends FrameworkEvent {
17
21
  if (![CommandType.any, CommandType.separated, CommandType.slash].includes(commandInstance.type))
18
22
  return;
19
23
  if (commandInstance.type === CommandType.separated || commandInstance.type === CommandType.slash) {
20
- await commandInstance.executeSlashCommand({ client, interaction, args, framework });
24
+ await commandInstance.executeSlashCommand({ client, interaction, args, framework, guildSettings });
21
25
  }
22
26
  else {
23
- await commandInstance.execute({ client, interaction, args, framework });
27
+ await commandInstance.execute({ client, interaction, args, framework, guildSettings });
24
28
  }
25
29
  }
26
30
  else if (interaction.isButton()) {
@@ -29,7 +33,7 @@ export class InteractionCreate extends FrameworkEvent {
29
33
  let path = interaction.customId.split('.');
30
34
  const command = framework.commands.get(path[0]);
31
35
  if (command.selectMenu) {
32
- command.selectMenu({ path, interaction, client, framework });
36
+ command.selectMenu({ path, interaction, client, framework, guildSettings });
33
37
  }
34
38
  }
35
39
  }
@@ -1,6 +1,16 @@
1
+ import { ActionRowBuilder, EmbedBuilder } from "discord.js";
1
2
  import { EventParameters } from "../../../types/EventParameters.js";
2
3
  import { FrameworkEvent } from "../../../types/FrameworkEvent.js";
3
4
  export declare class MessageCreate extends FrameworkEvent {
4
5
  once: boolean;
5
6
  execute({ message, client, framework }: EventParameters): Promise<import("discord.js").Message<boolean>>;
7
+ autocorrect(str: string, words: string[]): any;
8
+ getErrorEmbed(error: any, parse: any): {
9
+ embeds: EmbedBuilder[];
10
+ components: ActionRowBuilder<import("discord.js").AnyComponentBuilder>[];
11
+ allowedMentions: {
12
+ repliedUser: boolean;
13
+ };
14
+ };
15
+ parseError(error: any): any;
6
16
  }
@@ -1,6 +1,10 @@
1
- import { PermissionsBitField } from "discord.js";
1
+ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, PermissionsBitField } from "discord.js";
2
2
  import { FrameworkEvent } from "../../../types/FrameworkEvent.js";
3
3
  import { ZumitoFramework } from "../../../ZumitoFramework.js";
4
+ import leven from 'leven';
5
+ import ErrorStackParser from 'error-stack-parser';
6
+ import * as url from 'url';
7
+ import path from "path";
4
8
  export class MessageCreate extends FrameworkEvent {
5
9
  once = false;
6
10
  async execute({ message, client, framework }) {
@@ -12,9 +16,7 @@ export class MessageCreate extends FrameworkEvent {
12
16
  if (message.content.startsWith(prefix)) {
13
17
  if (!framework.commands.has(command)) {
14
18
  let commandNames = Array.from(framework.commands.keys());
15
- var autocorrect = await import('autocorrect');
16
- autocorrect = autocorrect({ words: commandNames });
17
- var correctedCommand = autocorrect(command);
19
+ var correctedCommand = this.autocorrect(command, commandNames);
18
20
  if (framework.commands.has(correctedCommand)) {
19
21
  commandInstance = framework.commands.get(correctedCommand);
20
22
  }
@@ -27,11 +29,11 @@ export class MessageCreate extends FrameworkEvent {
27
29
  }
28
30
  if (message.guild == null && commandInstance.dm == false)
29
31
  return;
30
- if (commandInstance.adminOnly || commandInstance.permissions.length > 0) {
32
+ if (commandInstance.adminOnly || commandInstance.userPermissions.length > 0) {
31
33
  let denied = false;
32
34
  if (framework.memberHasPermission(message.member, message.channel, PermissionsBitField.Flags.Administrator) || message.member.id != message.guild.ownerId) {
33
- if (commandInstance.permissions.length > 0) {
34
- commandInstance.permissions.forEach(permission => {
35
+ if (commandInstance.userPermissions.length > 0) {
36
+ commandInstance.userPermissions.forEach(permission => {
35
37
  if (!framework.memberHasPermission(message.member, message.channel, permission)) {
36
38
  denied = true;
37
39
  }
@@ -60,18 +62,17 @@ export class MessageCreate extends FrameworkEvent {
60
62
  }
61
63
  }
62
64
  try {
65
+ let guildSettings = await framework.getGuildSettings(message.guildId);
63
66
  let parsedArgs = new Map();
64
67
  args.forEach(function (arg, index) {
65
- parsedArgs.set(commandInstance.args?.[index]?.name || index, {
66
- name: commandInstance.args?.[index]?.name || index,
67
- value: arg
68
- });
68
+ parsedArgs.set(commandInstance.args?.[index]?.name || index, arg);
69
69
  });
70
70
  await commandInstance.execute({
71
71
  message,
72
72
  args: parsedArgs,
73
73
  client: framework.client,
74
74
  framework: framework,
75
+ guildSettings: guildSettings,
75
76
  });
76
77
  if (!message.channel.isDMBased && !message.deletable && (false)) { // false = settings.deleteCommands
77
78
  try {
@@ -86,19 +87,121 @@ export class MessageCreate extends FrameworkEvent {
86
87
  }
87
88
  }
88
89
  catch (error) {
89
- // let content = await getErrorEmbed({
90
- // name: error.name,
91
- // message: error.message,
92
- // comid: com,
93
- // args: args,
94
- // stack: error.stack,
95
- // }, true);
96
- // try {
97
- // message.reply(content);
98
- // } catch (e) {
99
- // channel.send(content);
100
- // }
90
+ let content = await this.getErrorEmbed({
91
+ name: error.name,
92
+ message: error.message,
93
+ command: commandInstance,
94
+ args: args,
95
+ stack: error.stack,
96
+ }, true);
97
+ try {
98
+ message.reply(content);
99
+ }
100
+ catch (e) {
101
+ channel.send(content);
102
+ }
101
103
  }
102
104
  }
103
105
  }
106
+ autocorrect(str, words) {
107
+ var distance, bestWord, i, word, min;
108
+ var dictionary = words || [];
109
+ var len = dictionary.length;
110
+ for (i = 0; i < len; i++) {
111
+ word = dictionary[i];
112
+ distance = leven(str, word);
113
+ if (distance === 0) {
114
+ return word;
115
+ }
116
+ else if (min === undefined || distance < min) {
117
+ min = distance;
118
+ bestWord = word;
119
+ }
120
+ }
121
+ return bestWord;
122
+ }
123
+ getErrorEmbed(error, parse) {
124
+ let parsedError;
125
+ if (parse) {
126
+ parsedError = this.parseError(error);
127
+ }
128
+ else {
129
+ parsedError = error;
130
+ }
131
+ let embed = new EmbedBuilder()
132
+ .setTitle('Error')
133
+ .setDescription('An error has occured while executing this command.')
134
+ .setTimestamp()
135
+ .addFields([{
136
+ name: 'Command:',
137
+ value: (error.command.name || 'Not defined')
138
+ }])
139
+ .addFields([{
140
+ name: 'Arguments:',
141
+ value: (error.args.toString() || 'None')
142
+ }])
143
+ .addFields([{
144
+ name: 'Error name:',
145
+ value: (error.name || 'Not defined')
146
+ }])
147
+ .addFields([{
148
+ name: 'Error message:',
149
+ value: (error.message || 'Not defined')
150
+ }]);
151
+ if (error.possibleSolutions !== undefined) {
152
+ error.possibleSolutions.forEach((solution) => {
153
+ embed.addFields([{
154
+ name: 'Posible solution:',
155
+ value: solution
156
+ }]);
157
+ });
158
+ }
159
+ let stackFrames = ErrorStackParser.parse(error).filter(e => !e.fileName.includes('node_modules') && !e.fileName.includes('node:internal'));
160
+ let stack = '';
161
+ const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
162
+ let path1 = path.resolve('./');
163
+ let path2 = path1.replaceAll('\\', '/');
164
+ stackFrames.forEach((frame) => {
165
+ stack += `[${frame.fileName.replace(path1, '').replace(path2, '').replace('file://', '')}:${frame.lineNumber}](https://zumito.ga/redirect?url=vscode://file/${frame.fileName.replace('file://', '')}:${frame.lineNumber}) ${frame.functionName}()\n`;
166
+ });
167
+ if (error.stack !== undefined) {
168
+ embed.addFields([{
169
+ name: 'Call stack:',
170
+ value: stack || error.stack || error.stack.toString()
171
+ }]);
172
+ }
173
+ if (error.details !== undefined) {
174
+ error.details.forEach((detail) => {
175
+ embed.addFields([{
176
+ name: 'Detail:',
177
+ value: detail
178
+ }]);
179
+ });
180
+ }
181
+ const body = `\n\n\n---\nComand:\`\`\`${error.command.name || 'not defined'}\`\`\`\nArguments:\`\`\`${error.args.toString() || 'none'}\`\`\`\nError:\`\`\`${error.name || 'not defined'}\`\`\`\nError message:\`\`\`${error.message || 'not defined'}\`\`\`\n`;
182
+ const requestUrl = `https://github.com/fernandomema/Zumito/issues/new?body=${encodeURIComponent(body)}`;
183
+ const row = new ActionRowBuilder()
184
+ .addComponents(new ButtonBuilder()
185
+ .setStyle(ButtonStyle.Link)
186
+ .setLabel('Report error')
187
+ .setEmoji('975645505302437978')
188
+ .setURL(requestUrl));
189
+ return {
190
+ embeds: [embed],
191
+ components: [row],
192
+ allowedMentions: {
193
+ repliedUser: false
194
+ }
195
+ };
196
+ }
197
+ parseError(error) {
198
+ error.possibleSolutions = [];
199
+ if (/(?:^|(?<= ))(EmbedBuilder|Discord|ActionRowBuilder|ButtonBuilder|MessageSelectMenu)(?:(?= )|$) is not defined/gm.test(error.message)) {
200
+ error.possibleSolutions.push('const { ' + error.message.split(" ")[0] + ' } = require(\'discord.js\');');
201
+ }
202
+ else if (error.message.includes('A custom id and url cannot both be specified')) {
203
+ error.possibleSolutions.push("Remove .setCustomId(...) or .setURL(...)");
204
+ }
205
+ return error;
206
+ }
104
207
  }
@@ -2,5 +2,5 @@ import { Module } from "../types/Module.js";
2
2
  import { ZumitoFramework } from "../ZumitoFramework.js";
3
3
  export declare class baseModule extends Module {
4
4
  constructor(modulePath: string, framework: ZumitoFramework);
5
- asyncregisterEvents(): any;
5
+ registerEvents(): Promise<any>;
6
6
  }
@@ -5,7 +5,7 @@ export class baseModule extends Module {
5
5
  constructor(modulePath, framework) {
6
6
  super(modulePath, framework);
7
7
  }
8
- asyncregisterEvents() {
8
+ async registerEvents() {
9
9
  this.events.set('interactionCreate', new InteractionCreate());
10
10
  this.events.set('messageCreate', new MessageCreate());
11
11
  this.events.forEach(event => {
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "string",
3
+ "command": {
4
+ "command": "string",
5
+ "args": {
6
+ "type": "array"
7
+ }
8
+ },
9
+ "error": {
10
+ "title": "string",
11
+ "message": "string",
12
+ "stacktrace": "string"
13
+ }
14
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "guild_id": {
3
+ "type": "string",
4
+ "index": true,
5
+ "unique": true
6
+ },
7
+ "lang": {
8
+ "type": "string",
9
+ "default": "en"
10
+ },
11
+ "prefix": {
12
+ "type": "string",
13
+ "default": "z-"
14
+ },
15
+ "public": {
16
+ "type": "boolean",
17
+ "default": false
18
+ },
19
+ "deleteCommands": {
20
+ "type": "boolean",
21
+ "default": false
22
+ }
23
+ }
@@ -3,15 +3,15 @@ import { SelectMenuParameters } from "./SelectMenuParameters.js";
3
3
  export declare abstract class Command {
4
4
  name: string;
5
5
  categories: string[];
6
- aliases?: string[];
7
- examples?: string[];
8
- permissions?: bigint[];
9
- botPermissions?: string[];
10
- hidden?: boolean;
11
- adminOnly?: boolean;
12
- nsfw?: boolean;
13
- cooldown?: number;
14
- slashCommand?: boolean;
6
+ aliases: string[];
7
+ examples: string[];
8
+ userPermissions: bigint[];
9
+ botPermissions: string[];
10
+ hidden: boolean;
11
+ adminOnly: boolean;
12
+ nsfw: boolean;
13
+ cooldown: number;
14
+ slashCommand: boolean;
15
15
  dm: boolean;
16
16
  args: CommandArgDefinition[];
17
17
  type: string;
@@ -19,5 +19,5 @@ export declare abstract class Command {
19
19
  abstract execute({ message, interaction, args, client, framework }: CommandParameters): void;
20
20
  executePrefixCommand({ message, interaction, args, client, framework }: CommandParameters): void;
21
21
  executeSlashCommand({ message, interaction, args, client, framework }: CommandParameters): void;
22
- selectMenu({ path, interaction, client, framework }: SelectMenuParameters): void;
22
+ abstract selectMenu({ path, interaction, client, framework }: SelectMenuParameters): void;
23
23
  }
@@ -4,7 +4,7 @@ export class Command {
4
4
  categories = [];
5
5
  aliases = [];
6
6
  examples = [];
7
- permissions = [];
7
+ userPermissions = [];
8
8
  botPermissions = [];
9
9
  hidden = false;
10
10
  adminOnly = false;
@@ -22,5 +22,4 @@ export class Command {
22
22
  executeSlashCommand({ message, interaction, args, client, framework }) {
23
23
  this.execute({ message, interaction, args, client, framework });
24
24
  }
25
- selectMenu({ path, interaction, client, framework }) { }
26
25
  }
@@ -14,4 +14,5 @@ export interface CommandParameters {
14
14
  args: Map<String, any>;
15
15
  client: Client;
16
16
  framework: ZumitoFramework;
17
+ guildSettings?: any;
17
18
  }
@@ -1,4 +1,4 @@
1
- import { CommandType } from "./CommandType";
1
+ import { CommandType } from "./CommandType.js";
2
2
  export class Command {
3
3
  name = this.constructor.name.toLowerCase();
4
4
  categories = [];
@@ -10,8 +10,8 @@ export declare abstract class Module {
10
10
  constructor(path: any, framework: any);
11
11
  initialize(): Promise<void>;
12
12
  registerCommands(): Promise<void>;
13
- onCommandCreated(filePath: string): void;
14
- onCommandChanged(filePath: string): void;
13
+ onCommandCreated(filePath: string): Promise<void>;
14
+ onCommandChanged(filePath: string): Promise<void>;
15
15
  onErrorLoadingCommand(error: Error): void;
16
16
  getCommands(): Map<string, Command>;
17
17
  registerEvents(): Promise<void>;
@@ -1,7 +1,9 @@
1
1
  import * as chokidar from 'chokidar';
2
+ import chalk from 'chalk';
2
3
  import boxen from "boxen";
3
4
  import * as fs from 'fs';
4
5
  import path from 'path';
6
+ import { SelectMenuInteraction } from "discord.js";
5
7
  export class Module {
6
8
  path;
7
9
  framework;
@@ -16,41 +18,63 @@ export class Module {
16
18
  await this.registerCommands();
17
19
  await this.registerEvents();
18
20
  await this.registerTranslations();
21
+ await this.registerModels();
22
+ // console.error('[🔄🔴 ] Error initializing module ' + this.constructor.name);
23
+ // console.log(boxen(e + '\n' + e.stack, { padding: 1 }));
19
24
  }
20
25
  async registerCommands() {
21
- if (!fs.existsSync(path.join(this.path, 'commands')))
22
- return;
23
- let files = fs.readdirSync(path.join(this.path, 'commands'));
24
- for (let file of files) {
25
- if (file.endsWith('.js') || file.endsWith('.ts')) {
26
- let command = await import('file://' + path.join(this.path, 'commands', file));
27
- command = Object.values(command)[0];
28
- command = new command();
29
- this.commands.set(command.constructor.name.toLowerCase(), command);
26
+ if (fs.existsSync(path.join(this.path, 'commands'))) {
27
+ let files = fs.readdirSync(path.join(this.path, 'commands'));
28
+ for (let file of files) {
29
+ if (file.endsWith('.js') || file.endsWith('.ts')) {
30
+ let command = await import('file://' + path.join(this.path, 'commands', file)).catch(e => {
31
+ console.error(`[🔄🔴 ] Error loading ${file.slice(0, -3)} command on module ${this.constructor.name}`);
32
+ console.error(e + '\n' + e.name + '\n' + e.stack);
33
+ });
34
+ command = Object.values(command)[0];
35
+ command = new command();
36
+ this.commands.set(command.constructor.name.toLowerCase(), command);
37
+ }
38
+ }
39
+ ;
40
+ // register watcher
41
+ if (process.env.DEBUG) {
42
+ /*
43
+ Debug only cause in prod environment commands should't be changed.
44
+ Appart from that, esm module cache invalidation is not working properly
45
+ and can cause memory leaks and crashes.
46
+ */
47
+ chokidar.watch(path.resolve(path.join(this.path, 'commands')), { ignored: /^\./, persistent: true, ignoreInitial: true })
48
+ .on('add', this.onCommandCreated.bind(this))
49
+ .on('change', this.onCommandChanged.bind(this))
50
+ //.on('unlink', function(path) {console.log('File', path, 'has been removed');})
51
+ .on('error', this.onErrorLoadingCommand.bind(this));
30
52
  }
31
- }
32
- ;
33
- // register watcher
34
- if (process.env.DEBUG) {
35
- chokidar.watch(path.resolve(path.join(this.path, 'commands')), { ignored: /^\./, persistent: true, ignoreInitial: true })
36
- .on('add', this.onCommandCreated.bind(this))
37
- .on('change', this.onCommandChanged.bind(this))
38
- //.on('unlink', function(path) {console.log('File', path, 'has been removed');})
39
- .on('error', this.onErrorLoadingCommand.bind(this));
40
53
  }
41
54
  }
42
- onCommandCreated(filePath) {
43
- console.log(filePath);
44
- // GRreen dot emoji
45
- // try {
46
- // delete require.cache[path.resolve(path.join(this.path, 'commands', path))];
47
- // console.debug('[🔄🟢 ] Command ' + chalk.blue(path.replace(/^.*[\\\/]/, '').split('.').slice(0, -1).join('.')) + ' reloaded');
48
- // } catch(e) {
49
- // console.error('[🔄🔴 ] Error reloading command ' + chalk.blue(path.replace(/^.*[\\\/]/, '').split('.').slice(0, -1).join('.')));
50
- // console.log(boxen(e + '\n' + e.stack, { padding: 1 }));
51
- // }
55
+ async onCommandCreated(filePath) {
56
+ if (filePath.endsWith('.js') || filePath.endsWith('.ts')) {
57
+ let command = await import('file://' + filePath).catch(e => {
58
+ console.error('[🆕🔴 ] Error loading command ' + chalk.blue(filePath.replace(/^.*[\\\/]/, '').split('.').slice(0, -1).join('.')));
59
+ console.log(e + '\n' + e.name + '\n' + e.stack);
60
+ });
61
+ command = Object.values(command)[0];
62
+ command = new command();
63
+ this.commands.set(command.constructor.name.toLowerCase(), command);
64
+ console.debug('[🆕🟢 ] Command ' + chalk.blue(filePath.replace(/^.*[\\\/]/, '').split('.').slice(0, -1).join('.')) + ' loaded');
65
+ }
52
66
  }
53
- onCommandChanged(filePath) {
67
+ async onCommandChanged(filePath) {
68
+ if (filePath.endsWith('.js') || filePath.endsWith('.ts')) {
69
+ let command = await import('file://' + filePath + '?update=' + Date.now().toString()).catch(e => {
70
+ console.error('[🔄🔴 ] Error reloading command ' + chalk.blue(filePath.replace(/^.*[\\\/]/, '').split('.').slice(0, -1).join('.')));
71
+ console.log(boxen(e + '\n' + e.name + '\n' + e.stack, { padding: 1 }));
72
+ });
73
+ command = Object.values(command)[0];
74
+ command = new command();
75
+ this.commands.set(command.constructor.name.toLowerCase(), command);
76
+ console.debug('[🔄🟢 ] Command ' + chalk.blue(filePath.replace(/^.*[\\\/]/, '').split('.').slice(0, -1).join('.')) + ' reloaded');
77
+ }
54
78
  }
55
79
  onErrorLoadingCommand(error) {
56
80
  console.error('[🔄🔴 ] Error reloading command');
@@ -68,7 +92,10 @@ export class Module {
68
92
  let moduleFileNames = fs.readdirSync(path.join(this.path, 'events', 'discord'));
69
93
  for (let moduleFileName of moduleFileNames) {
70
94
  if (moduleFileName.endsWith('.js') || moduleFileName.endsWith('.ts')) {
71
- let event = await import('file://' + path.join(this.path, 'events', 'discord', moduleFileName));
95
+ let event = await import('file://' + path.join(this.path, 'events', 'discord', moduleFileName)).catch(e => {
96
+ console.error(`[🔄🔴 ] Error loading ${moduleFileName.slice(0, -3)} event on module ${this.constructor.name}`);
97
+ console.log(boxen(e + '\n' + e.name + '\n' + e.stack, { padding: 1 }));
98
+ });
72
99
  event = Object.values(event)[0];
73
100
  event = new event();
74
101
  this.events.set(event.constructor.name.toLowerCase(), event);
@@ -101,6 +128,10 @@ export class Module {
101
128
  args.forEach(arg => {
102
129
  finalArgs[arg.constructor.name.toLowerCase()] = arg;
103
130
  });
131
+ let selectMenuInteraction = args.find((arg) => arg instanceof SelectMenuInteraction);
132
+ if (selectMenuInteraction) {
133
+ finalArgs['interaction'] = selectMenuInteraction;
134
+ }
104
135
  return finalArgs;
105
136
  }
106
137
  getEvents() {
@@ -112,7 +143,6 @@ export class Module {
112
143
  let files = fs.readdirSync(path.join(this.path, 'translations'));
113
144
  for (let file of files) {
114
145
  if (file.endsWith('.json')) {
115
- console.log('load ' + file);
116
146
  let json = await import('file://' + `${this.path}/translations/${file}`, {
117
147
  assert: {
118
148
  type: "json",
@@ -122,7 +152,7 @@ export class Module {
122
152
  console.log(boxen(e + '\n' + e.name + '\n' + e.stack, { padding: 1 }));
123
153
  });
124
154
  let lang = file.slice(0, -5);
125
- this.parseTranslation('', lang, json);
155
+ this.parseTranslation('', lang, json.default);
126
156
  }
127
157
  }
128
158
  }
@@ -143,7 +173,6 @@ export class Module {
143
173
  for (let file of files) {
144
174
  if (file.endsWith('.json')) {
145
175
  let modelName = file.slice(0, -5).charAt(0).toUpperCase() + file.slice(0, -5).slice(1);
146
- console.log('load ' + file);
147
176
  let modelDefiniton = await import('file://' + `${this.path}/models/${file}`, {
148
177
  assert: {
149
178
  type: "json",
@@ -1,8 +1,9 @@
1
- import { Client, Interaction } from "discord.js";
1
+ import { Client, SelectMenuInteraction } from "discord.js";
2
2
  import { ZumitoFramework } from "../ZumitoFramework.js";
3
3
  export interface SelectMenuParameters {
4
4
  path: string[];
5
- interaction: Interaction;
5
+ interaction: SelectMenuInteraction;
6
6
  client: Client;
7
7
  framework: ZumitoFramework;
8
+ guildSettings?: any;
8
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zumito-framework",
3
- "version": "1.1.33",
3
+ "version": "1.1.35",
4
4
  "description": "Discord.js bot framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "scripts": {
17
17
  "test": "echo \"Error: no test specified\" && exit 1",
18
- "build": "tsc",
18
+ "build": "tsc-esm",
19
19
  "publish": "npm publish",
20
20
  "docs": "typedoc --out docs src"
21
21
  },
@@ -30,16 +30,17 @@
30
30
  "chokidar": "^3.5.3",
31
31
  "cookie-parser": "^1.4.6",
32
32
  "cors": "^2.8.5",
33
- "discord.js": "^14.2.0",
33
+ "discord.js": "^14.3.0",
34
+ "error-stack-parser": "^2.1.4",
34
35
  "express": "^4.18.1",
36
+ "leven": "^4.0.0",
35
37
  "mongoose": "^6.5.2",
36
- "plop": "^3.1.1",
37
- "zumito-framework": "^1.1.16"
38
+ "plop": "^3.1.1"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/node": "^18.7.16",
41
- "typedoc": "^0.23.10",
42
- "typescript": "^4.7.4"
42
+ "typedoc": "^0.23.14",
43
+ "typescript": "^4.8.2"
43
44
  },
44
45
  "type": "module"
45
46
  }
@@ -1,10 +1,17 @@
1
1
  import { Command, CommandParameters } from "zumito-framework";
2
2
 
3
- export class {{command.charAt(0).toUpperCase() + command.slice(1)}} extends Command {
3
+ export class {{capitalize command}} extends Command {
4
4
 
5
- execute({ message, interaction, args, client, framework }: CommandParameters): void {
6
- (message || interaction).reply({
5
+ execute({ message, interaction, args, client, framework, guildSettings }: CommandParameters): void {
6
+ (message || interaction!).reply({
7
7
  content: "Message content",
8
8
  });
9
9
  }
10
+
11
+ async selectMenu({ path, interaction, client, framework }: SelectMenuParameters): Promise<void> {
12
+ await interaction.deferUpdate();
13
+ await interaction.editReply({
14
+ content: "Select menu content",
15
+ });
16
+ }
10
17
  }
package/plopfile.js CHANGED
@@ -1,4 +1,7 @@
1
1
  module.exports = function (plop) {
2
+ plop.setHelper('capitalize', function (text) {
3
+ return text.charAt(0).toUpperCase() + text.slice(1);
4
+ });
2
5
  // create your generators here
3
6
  plop.setGenerator('command', {
4
7
  description: 'this is a skeleton command file',