zumito-framework 1.1.76 → 1.1.77
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.
- package/dist/ZumitoFramework.d.ts +8 -0
- package/dist/ZumitoFramework.js +19 -5
- package/dist/baseModule/events/discord/InteractionCreate.d.ts +8 -0
- package/dist/baseModule/events/discord/InteractionCreate.js +128 -0
- package/dist/baseModule/events/discord/MessageCreate.d.ts +16 -0
- package/dist/baseModule/events/discord/MessageCreate.js +269 -0
- package/dist/baseModule/index.js +4 -3
- package/dist/managers/EventManager.d.ts +8 -0
- package/dist/managers/EventManager.js +27 -0
- package/dist/types/Module.d.ts +2 -1
- package/dist/types/Module.js +30 -31
- package/dist/utils/InteractionIdGenerator.d.ts +13 -0
- package/dist/utils/InteractionIdGenerator.js +40 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { FrameworkSettings } from './types/FrameworkSettings.js';
|
|
|
7
7
|
import { Module } from './types/Module.js';
|
|
8
8
|
import { StatusManager } from './managers/StatusManager.js';
|
|
9
9
|
import { TranslationManager } from './TranslationManager.js';
|
|
10
|
+
import { EventManager } from './managers/EventManager.js';
|
|
10
11
|
/**
|
|
11
12
|
* @class ZumitoFramework
|
|
12
13
|
* @description The main class of the framework.
|
|
@@ -99,6 +100,13 @@ export declare class ZumitoFramework {
|
|
|
99
100
|
* @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
|
|
100
101
|
*/
|
|
101
102
|
eventEmitter: EventEmitter;
|
|
103
|
+
/**
|
|
104
|
+
* Event manager for the framework.
|
|
105
|
+
* All events related to the framework and discord.js are handled here.
|
|
106
|
+
* @type {EventManager}
|
|
107
|
+
* @private
|
|
108
|
+
*/
|
|
109
|
+
eventManager: EventManager;
|
|
102
110
|
/**
|
|
103
111
|
* @constructor
|
|
104
112
|
* @param {FrameworkSettings} settings - The settings to use for the framework.
|
package/dist/ZumitoFramework.js
CHANGED
|
@@ -17,6 +17,7 @@ import cors from 'cors';
|
|
|
17
17
|
import express from 'express';
|
|
18
18
|
import http from 'http';
|
|
19
19
|
import path from 'path';
|
|
20
|
+
import { EventManager } from './managers/EventManager.js';
|
|
20
21
|
// import better-logging
|
|
21
22
|
betterLogging(console);
|
|
22
23
|
/**
|
|
@@ -111,6 +112,13 @@ export class ZumitoFramework {
|
|
|
111
112
|
* @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
|
|
112
113
|
*/
|
|
113
114
|
eventEmitter = new EventEmitter();
|
|
115
|
+
/**
|
|
116
|
+
* Event manager for the framework.
|
|
117
|
+
* All events related to the framework and discord.js are handled here.
|
|
118
|
+
* @type {EventManager}
|
|
119
|
+
* @private
|
|
120
|
+
*/
|
|
121
|
+
eventManager;
|
|
114
122
|
/**
|
|
115
123
|
* @constructor
|
|
116
124
|
* @param {FrameworkSettings} settings - The settings to use for the framework.
|
|
@@ -123,6 +131,7 @@ export class ZumitoFramework {
|
|
|
123
131
|
this.events = new Map();
|
|
124
132
|
this.translations = new TranslationManager();
|
|
125
133
|
this.models = [];
|
|
134
|
+
this.eventManager = new EventManager();
|
|
126
135
|
if (settings.logLevel) {
|
|
127
136
|
console.logLevel = settings.logLevel;
|
|
128
137
|
}
|
|
@@ -145,8 +154,10 @@ export class ZumitoFramework {
|
|
|
145
154
|
*/
|
|
146
155
|
async initialize() {
|
|
147
156
|
await this.initializeDatabase();
|
|
148
|
-
this.initializeDiscordClient();
|
|
157
|
+
await this.initializeDiscordClient();
|
|
149
158
|
this.startApiServer();
|
|
159
|
+
this.eventManager.addEventEmitter('discord', this.client);
|
|
160
|
+
this.eventManager.addEventEmitter('framework', this.eventEmitter);
|
|
150
161
|
await this.registerModules();
|
|
151
162
|
await this.refreshSlashCommands();
|
|
152
163
|
if (this.settings.statusOptions) {
|
|
@@ -301,14 +312,17 @@ export class ZumitoFramework {
|
|
|
301
312
|
* Logs in to the Discord API using the provided token and logs a message when the client is ready.
|
|
302
313
|
* @private
|
|
303
314
|
*/
|
|
304
|
-
initializeDiscordClient() {
|
|
315
|
+
async initializeDiscordClient() {
|
|
305
316
|
this.client = new Client({
|
|
306
317
|
intents: this.settings.discordClientOptions.intents,
|
|
307
318
|
});
|
|
308
319
|
this.client.login(this.settings.discordClientOptions.token);
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
320
|
+
await new Promise((resolve) => {
|
|
321
|
+
this.client.on('ready', () => {
|
|
322
|
+
// Bot emoji
|
|
323
|
+
console.log('[🤖🟢] Discord client ready');
|
|
324
|
+
resolve();
|
|
325
|
+
});
|
|
312
326
|
});
|
|
313
327
|
}
|
|
314
328
|
/**
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from '../../../types/Command.js';
|
|
2
|
+
import { EventParameters } from '../../../types/EventParameters.js';
|
|
3
|
+
import { FrameworkEvent } from '../../../types/FrameworkEvent.js';
|
|
4
|
+
export declare class InteractionCreate extends FrameworkEvent {
|
|
5
|
+
once: boolean;
|
|
6
|
+
execute({ interaction, client, framework, }: EventParameters): Promise<void>;
|
|
7
|
+
getTransMethod(commandInstance: Command, framework: any, guildSettings: any): (key: string, params?: any) => any;
|
|
8
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { CommandType } from '../../../types/CommandType.js';
|
|
2
|
+
import { FrameworkEvent } from '../../../types/FrameworkEvent.js';
|
|
3
|
+
export class InteractionCreate extends FrameworkEvent {
|
|
4
|
+
once = false;
|
|
5
|
+
async execute({ interaction, client, framework, }) {
|
|
6
|
+
let guildSettings;
|
|
7
|
+
if (interaction.guildId) {
|
|
8
|
+
guildSettings = await framework.getGuildSettings(interaction.guildId);
|
|
9
|
+
}
|
|
10
|
+
if (interaction.isCommand()) {
|
|
11
|
+
if (!framework.commands.has(interaction.commandName))
|
|
12
|
+
return;
|
|
13
|
+
const commandInstance = framework.commands.get(interaction.commandName);
|
|
14
|
+
const args = new Map();
|
|
15
|
+
commandInstance.args.forEach((arg) => {
|
|
16
|
+
const option = interaction.options.get(arg.name);
|
|
17
|
+
if (option) {
|
|
18
|
+
switch (arg.type) {
|
|
19
|
+
case 'user':
|
|
20
|
+
args.set(arg.name, option.user);
|
|
21
|
+
break;
|
|
22
|
+
case 'member':
|
|
23
|
+
args.set(arg.name, option.member);
|
|
24
|
+
break;
|
|
25
|
+
default:
|
|
26
|
+
args.set(arg.name, option.value ||
|
|
27
|
+
option.user ||
|
|
28
|
+
option.role ||
|
|
29
|
+
option.channel ||
|
|
30
|
+
option.options ||
|
|
31
|
+
option.message ||
|
|
32
|
+
option.member ||
|
|
33
|
+
option.focused ||
|
|
34
|
+
option.autocomplete ||
|
|
35
|
+
option.attachment);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
if (![CommandType.any, CommandType.separated, CommandType.slash,].includes(commandInstance.type))
|
|
41
|
+
return;
|
|
42
|
+
const trans = this.getTransMethod(commandInstance, framework, guildSettings);
|
|
43
|
+
if (commandInstance.type === CommandType.separated ||
|
|
44
|
+
commandInstance.type === CommandType.slash) {
|
|
45
|
+
await commandInstance.executeSlashCommand({
|
|
46
|
+
client, interaction, args, framework, guildSettings, trans,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
await commandInstance.execute({
|
|
51
|
+
client, interaction, args, framework, guildSettings, trans,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (interaction.isButton()) {
|
|
56
|
+
interaction = interaction;
|
|
57
|
+
const path = interaction.customId.split('.');
|
|
58
|
+
const commandInstance = framework.commands.get(path[0]);
|
|
59
|
+
if (!commandInstance)
|
|
60
|
+
throw new Error(`Command ${path[0]} not found or button id bad formatted`);
|
|
61
|
+
// If the command has impements ButtonPress class then execute the method
|
|
62
|
+
if (commandInstance.constructor.prototype.hasOwnProperty('buttonPressed')) {
|
|
63
|
+
commandInstance.buttonPressed({
|
|
64
|
+
path,
|
|
65
|
+
interaction,
|
|
66
|
+
client,
|
|
67
|
+
framework,
|
|
68
|
+
guildSettings,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (interaction.isStringSelectMenu()) {
|
|
73
|
+
const path = interaction.customId.split('.');
|
|
74
|
+
const commandInstance = framework.commands.get(path[0]);
|
|
75
|
+
if (!commandInstance)
|
|
76
|
+
throw new Error(`Command ${path[0]} not found or select menu id bad formatted`);
|
|
77
|
+
const trans = (key, params) => {
|
|
78
|
+
if (key.startsWith('$')) {
|
|
79
|
+
return framework.translations.get(key.replace('$', ''), guildSettings.lang, params);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
return framework.translations.get('command.' + commandInstance.name + '.' + key, guildSettings.lang, params);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
if (commandInstance.selectMenu) {
|
|
86
|
+
commandInstance.selectMenu({
|
|
87
|
+
path,
|
|
88
|
+
interaction,
|
|
89
|
+
client,
|
|
90
|
+
framework,
|
|
91
|
+
guildSettings,
|
|
92
|
+
trans,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else if (interaction.isModalSubmit()) {
|
|
97
|
+
const path = interaction.customId.split('.');
|
|
98
|
+
const commandInstance = framework.commands.get(path[0]);
|
|
99
|
+
if (!commandInstance) {
|
|
100
|
+
throw new Error(`Command ${path[0]} not found or modal id bad formatted`);
|
|
101
|
+
}
|
|
102
|
+
if (commandInstance.modalSubmit) {
|
|
103
|
+
commandInstance.modalSubmit({
|
|
104
|
+
path,
|
|
105
|
+
interaction,
|
|
106
|
+
client,
|
|
107
|
+
framework,
|
|
108
|
+
guildSettings,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
framework.eventManager.emitEvent('modalSubmit', 'framework', {
|
|
113
|
+
path, interaction, client, framework, guildSettings,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
getTransMethod(commandInstance, framework, guildSettings) {
|
|
119
|
+
return (key, params) => {
|
|
120
|
+
if (key.startsWith('$')) {
|
|
121
|
+
return framework.translations.get(key.replace('$', ''), guildSettings.lang, params);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
return framework.translations.get('command.' + commandInstance.name + '.' + key, guildSettings.lang, params);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActionRowBuilder, EmbedBuilder } from 'discord.js';
|
|
2
|
+
import { EventParameters } from '../../../types/EventParameters.js';
|
|
3
|
+
import { FrameworkEvent } from '../../../types/FrameworkEvent.js';
|
|
4
|
+
export declare class MessageCreate extends FrameworkEvent {
|
|
5
|
+
once: boolean;
|
|
6
|
+
execute({ message, 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("@discordjs/builders").AnyComponentBuilder>[];
|
|
11
|
+
allowedMentions: {
|
|
12
|
+
repliedUser: boolean;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
parseError(error: any): any;
|
|
16
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, EmbedBuilder, PermissionsBitField, } from 'discord.js';
|
|
2
|
+
import ErrorStackParser from 'error-stack-parser';
|
|
3
|
+
import { FrameworkEvent } from '../../../types/FrameworkEvent.js';
|
|
4
|
+
import { ZumitoFramework } from '../../../ZumitoFramework.js';
|
|
5
|
+
import leven from 'leven';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { InteractionIdGenerator } from '../../../utils/InteractionIdGenerator.js';
|
|
8
|
+
export class MessageCreate extends FrameworkEvent {
|
|
9
|
+
once = false;
|
|
10
|
+
async execute({ message, framework }) {
|
|
11
|
+
const channel = message.channel;
|
|
12
|
+
const prefix = framework.settings.defaultPrefix;
|
|
13
|
+
const args = ZumitoFramework.splitCommandLine(message.content.slice(prefix.length));
|
|
14
|
+
const command = args.shift().toLowerCase();
|
|
15
|
+
let commandInstance;
|
|
16
|
+
if (message.content.startsWith(prefix)) {
|
|
17
|
+
if (!framework.commands.has(command)) {
|
|
18
|
+
const commandNames = Array.from(framework.commands.keys());
|
|
19
|
+
const correctedCommand = this.autocorrect(command, commandNames);
|
|
20
|
+
if (framework.commands.has(correctedCommand)) {
|
|
21
|
+
commandInstance = framework.commands.get(correctedCommand);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return; // Command not found
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
commandInstance = framework.commands.get(command);
|
|
29
|
+
}
|
|
30
|
+
if (message.guild == null && commandInstance.dm == false)
|
|
31
|
+
return;
|
|
32
|
+
if (commandInstance.adminOnly ||
|
|
33
|
+
commandInstance.userPermissions.length > 0) {
|
|
34
|
+
let denied = false;
|
|
35
|
+
if (framework.memberHasPermission(message.member, message.channel, PermissionsBitField.Flags.Administrator) ||
|
|
36
|
+
message.member.id != message.guild.ownerId) {
|
|
37
|
+
if (commandInstance.userPermissions.length > 0) {
|
|
38
|
+
commandInstance.userPermissions.forEach((permission) => {
|
|
39
|
+
if (!framework.memberHasPermission(message.member, message.channel, permission)) {
|
|
40
|
+
denied = true;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (denied) {
|
|
46
|
+
return message.reply({
|
|
47
|
+
content: 'You do not have permission to use this command.',
|
|
48
|
+
allowedMentions: {
|
|
49
|
+
repliedUser: false,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (message.channel.isTextBased) {
|
|
55
|
+
const channel = message.channel;
|
|
56
|
+
// Check command is nsfw and if channel is allowed
|
|
57
|
+
if (commandInstance.nsfw &&
|
|
58
|
+
!channel.nsfw &&
|
|
59
|
+
!channel
|
|
60
|
+
.permissionsFor(message.member)
|
|
61
|
+
.has(PermissionsBitField.Flags.Administrator) &&
|
|
62
|
+
message.member.id != message.guild.ownerId) {
|
|
63
|
+
return message.reply({
|
|
64
|
+
content: 'This command is nsfw and this channel is not nsfw.',
|
|
65
|
+
allowedMentions: {
|
|
66
|
+
repliedUser: false,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const guildSettings = await framework.getGuildSettings(message.guildId);
|
|
73
|
+
const parsedArgs = new Map();
|
|
74
|
+
for (let i = 0; i < args.length; i++) {
|
|
75
|
+
const arg = args[i];
|
|
76
|
+
const type = commandInstance.args[i]?.type;
|
|
77
|
+
if (type) {
|
|
78
|
+
if (type == 'member' || type == 'user') {
|
|
79
|
+
const member = await message.guild.members.cache.get(arg.replace(/[<@!>]/g, ''));
|
|
80
|
+
if (member) {
|
|
81
|
+
if (type == 'user') {
|
|
82
|
+
parsedArgs.set(commandInstance.args[i].name, member.user);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
parsedArgs.set(commandInstance.args[i].name, member);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return message.reply({
|
|
90
|
+
content: 'Invalid user.',
|
|
91
|
+
allowedMentions: {
|
|
92
|
+
repliedUser: false,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (type == 'string') {
|
|
98
|
+
parsedArgs.set(commandInstance.args?.[i]?.name || i.toString(), arg);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const interactionIdGenerator = new InteractionIdGenerator(undefined, commandInstance.name);
|
|
103
|
+
await commandInstance.execute({
|
|
104
|
+
message,
|
|
105
|
+
args: parsedArgs,
|
|
106
|
+
client: framework.client,
|
|
107
|
+
framework: framework,
|
|
108
|
+
guildSettings: guildSettings,
|
|
109
|
+
trans: (key, params) => {
|
|
110
|
+
if (key.startsWith('$')) {
|
|
111
|
+
return framework.translations.get(key.replace('$', ''), guildSettings.lang, params);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
return framework.translations.get('command.' + commandInstance.name + '.' + key, guildSettings.lang, params);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
if (!message.channel.isDMBased && !message.deletable) {
|
|
119
|
+
return; // TODO: test if this works
|
|
120
|
+
// false = settings.deleteCommands
|
|
121
|
+
try {
|
|
122
|
+
message.delete().catch(function () {
|
|
123
|
+
console.error("can't delete user command");
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
console.error(err.name, err.message);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const content = await this.getErrorEmbed({
|
|
133
|
+
name: error.name,
|
|
134
|
+
message: error.message,
|
|
135
|
+
command: commandInstance,
|
|
136
|
+
args: args,
|
|
137
|
+
stack: error.stack,
|
|
138
|
+
}, true);
|
|
139
|
+
try {
|
|
140
|
+
message.reply(content);
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
if (channel.type !== ChannelType.GuildStageVoice) {
|
|
144
|
+
channel.send(content);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
autocorrect(str, words) {
|
|
151
|
+
let distance, bestWord, i, word, min;
|
|
152
|
+
const dictionary = words || [];
|
|
153
|
+
const len = dictionary.length;
|
|
154
|
+
for (i = 0; i < len; i++) {
|
|
155
|
+
word = dictionary[i];
|
|
156
|
+
distance = leven(str, word);
|
|
157
|
+
if (distance === 0) {
|
|
158
|
+
return word;
|
|
159
|
+
}
|
|
160
|
+
else if (min === undefined || distance < min) {
|
|
161
|
+
min = distance;
|
|
162
|
+
bestWord = word;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return bestWord;
|
|
166
|
+
}
|
|
167
|
+
getErrorEmbed(error, parse) {
|
|
168
|
+
let parsedError;
|
|
169
|
+
if (parse) {
|
|
170
|
+
parsedError = this.parseError(error);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
parsedError = error;
|
|
174
|
+
}
|
|
175
|
+
const embed = new EmbedBuilder()
|
|
176
|
+
.setTitle('Error')
|
|
177
|
+
.setDescription('An error has occured while executing this command.')
|
|
178
|
+
.setTimestamp()
|
|
179
|
+
.addFields([
|
|
180
|
+
{
|
|
181
|
+
name: 'Command:',
|
|
182
|
+
value: error.command.name || 'Not defined',
|
|
183
|
+
},
|
|
184
|
+
])
|
|
185
|
+
.addFields([
|
|
186
|
+
{
|
|
187
|
+
name: 'Arguments:',
|
|
188
|
+
value: error.args.toString() || 'None',
|
|
189
|
+
},
|
|
190
|
+
])
|
|
191
|
+
.addFields([
|
|
192
|
+
{
|
|
193
|
+
name: 'Error name:',
|
|
194
|
+
value: error.name || 'Not defined',
|
|
195
|
+
},
|
|
196
|
+
])
|
|
197
|
+
.addFields([
|
|
198
|
+
{
|
|
199
|
+
name: 'Error message:',
|
|
200
|
+
value: error.message || 'Not defined',
|
|
201
|
+
},
|
|
202
|
+
]);
|
|
203
|
+
if (error.possibleSolutions !== undefined) {
|
|
204
|
+
error.possibleSolutions.forEach((solution) => {
|
|
205
|
+
embed.addFields([
|
|
206
|
+
{
|
|
207
|
+
name: 'Posible solution:',
|
|
208
|
+
value: solution,
|
|
209
|
+
},
|
|
210
|
+
]);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const stackFrames = ErrorStackParser.parse(error).filter((e) => !e.fileName.includes('node_modules') &&
|
|
214
|
+
!e.fileName.includes('node:internal'));
|
|
215
|
+
let stack = '';
|
|
216
|
+
const path1 = path.resolve('./');
|
|
217
|
+
const path2 = path1.replaceAll('\\', '/');
|
|
218
|
+
stackFrames.forEach((frame) => {
|
|
219
|
+
stack += `[${frame.fileName
|
|
220
|
+
.replace(path1, '')
|
|
221
|
+
.replace(path2, '')
|
|
222
|
+
.replace('file://', '')}:${frame.lineNumber}](https://zumito.ga/redirect?url=vscode://file/${frame.fileName.replace('file://', '')}:${frame.lineNumber}) ${frame.functionName}()\n`;
|
|
223
|
+
});
|
|
224
|
+
if (error.stack !== undefined) {
|
|
225
|
+
embed.addFields([
|
|
226
|
+
{
|
|
227
|
+
name: 'Call stack:',
|
|
228
|
+
value: stack || error.stack || error.stack.toString(),
|
|
229
|
+
},
|
|
230
|
+
]);
|
|
231
|
+
}
|
|
232
|
+
if (error.details !== undefined) {
|
|
233
|
+
error.details.forEach((detail) => {
|
|
234
|
+
embed.addFields([
|
|
235
|
+
{
|
|
236
|
+
name: 'Detail:',
|
|
237
|
+
value: detail,
|
|
238
|
+
},
|
|
239
|
+
]);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
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`;
|
|
243
|
+
const requestUrl = `https://github.com/ZumitoTeam/Zumito/issues/new?body=${encodeURIComponent(body)}`;
|
|
244
|
+
const row = new ActionRowBuilder().addComponents(new ButtonBuilder()
|
|
245
|
+
.setStyle(ButtonStyle.Link)
|
|
246
|
+
.setLabel('Report error')
|
|
247
|
+
.setEmoji('975645505302437978')
|
|
248
|
+
.setURL(requestUrl));
|
|
249
|
+
return {
|
|
250
|
+
embeds: [embed],
|
|
251
|
+
components: [row],
|
|
252
|
+
allowedMentions: {
|
|
253
|
+
repliedUser: false,
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
parseError(error) {
|
|
258
|
+
error.possibleSolutions = [];
|
|
259
|
+
if (/(?:^|(?<= ))(EmbedBuilder|Discord|ActionRowBuilder|ButtonBuilder|MessageSelectMenu)(?:(?= )|$) is not defined/gm.test(error.message)) {
|
|
260
|
+
error.possibleSolutions.push('const { ' +
|
|
261
|
+
error.message.split(' ')[0] +
|
|
262
|
+
" } = require('discord.js');");
|
|
263
|
+
}
|
|
264
|
+
else if (error.message.includes('A custom id and url cannot both be specified')) {
|
|
265
|
+
error.possibleSolutions.push('Remove .setCustomId(...) or .setURL(...)');
|
|
266
|
+
}
|
|
267
|
+
return error;
|
|
268
|
+
}
|
|
269
|
+
}
|
package/dist/baseModule/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
/* eslint-disable check-file/filename-naming-convention */
|
|
1
2
|
import { Module } from '../types/Module.js';
|
|
2
|
-
import { InteractionCreate } from './events/discord/
|
|
3
|
-
import { MessageCreate } from './events/discord/
|
|
3
|
+
import { InteractionCreate } from './events/discord/InteractionCreate.js';
|
|
4
|
+
import { MessageCreate } from './events/discord/MessageCreate.js';
|
|
4
5
|
import { Guild } from './models/Guild.js';
|
|
5
6
|
export class baseModule extends Module {
|
|
6
7
|
constructor(modulePath, framework) {
|
|
@@ -10,7 +11,7 @@ export class baseModule extends Module {
|
|
|
10
11
|
this.events.set('interactionCreate', new InteractionCreate());
|
|
11
12
|
this.events.set('messageCreate', new MessageCreate());
|
|
12
13
|
this.events.forEach((event) => {
|
|
13
|
-
this.
|
|
14
|
+
this.registerEvent(event, 'discord');
|
|
14
15
|
});
|
|
15
16
|
}
|
|
16
17
|
async registerModels() {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class EventManager {
|
|
2
|
+
eventEmitters: Map<string, any>;
|
|
3
|
+
addEventEmitter(name: string, eventEmitter: any): void;
|
|
4
|
+
getEventEmitter(name: string): any;
|
|
5
|
+
removeEventEmitter(name: string): void;
|
|
6
|
+
emitEvent(eventName: string, eventEmitterName: string, ...args: any): void;
|
|
7
|
+
addEventListener(eventEmitterName: string, eventName: string, callback: any, params?: any): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class EventManager {
|
|
2
|
+
eventEmitters = new Map();
|
|
3
|
+
addEventEmitter(name, eventEmitter) {
|
|
4
|
+
this.eventEmitters.set(name, eventEmitter);
|
|
5
|
+
}
|
|
6
|
+
getEventEmitter(name) {
|
|
7
|
+
return this.eventEmitters.get(name);
|
|
8
|
+
}
|
|
9
|
+
removeEventEmitter(name) {
|
|
10
|
+
this.eventEmitters.delete(name);
|
|
11
|
+
}
|
|
12
|
+
emitEvent(eventName, eventEmitterName, ...args) {
|
|
13
|
+
const eventEmitter = this.getEventEmitter(eventEmitterName);
|
|
14
|
+
if (!eventEmitter)
|
|
15
|
+
throw new Error(`EventEmitter ${eventEmitterName} not found`);
|
|
16
|
+
eventEmitter.emit(eventName, ...args);
|
|
17
|
+
}
|
|
18
|
+
addEventListener(eventEmitterName, eventName, callback, params) {
|
|
19
|
+
const eventEmitter = this.getEventEmitter(eventEmitterName);
|
|
20
|
+
if (!eventEmitter)
|
|
21
|
+
throw new Error(`EventEmitter ${eventEmitterName} not found`);
|
|
22
|
+
const method = params?.once ? 'once' : 'on';
|
|
23
|
+
eventEmitter[method](eventName, (...args) => {
|
|
24
|
+
callback(...args);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/types/Module.d.ts
CHANGED
|
@@ -16,7 +16,8 @@ export declare abstract class Module {
|
|
|
16
16
|
onErrorLoadingCommand(error: Error): void;
|
|
17
17
|
getCommands(): Map<string, Command>;
|
|
18
18
|
registerEvents(): Promise<void>;
|
|
19
|
-
|
|
19
|
+
registerEventsFolder(folder: string): Promise<void>;
|
|
20
|
+
registerEvent(frameworkEvent: FrameworkEvent, emitterName: string): void;
|
|
20
21
|
parseEventArgs(args: any[]): any;
|
|
21
22
|
getEvents(): Map<string, FrameworkEvent>;
|
|
22
23
|
registerTranslations(subpath?: string): Promise<void>;
|
package/dist/types/Module.js
CHANGED
|
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import boxen from 'boxen';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import { ButtonInteraction, CommandInteraction, StringSelectMenuInteraction, } from 'discord.js';
|
|
6
|
+
import { ButtonInteraction, CommandInteraction, ModalSubmitInteraction, StringSelectMenuInteraction, } from 'discord.js';
|
|
7
7
|
export class Module {
|
|
8
8
|
path;
|
|
9
9
|
framework;
|
|
@@ -90,42 +90,40 @@ export class Module {
|
|
|
90
90
|
return;
|
|
91
91
|
const files = fs.readdirSync(path.join(this.path, 'events'));
|
|
92
92
|
for (const file of files) {
|
|
93
|
-
if
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
93
|
+
// if file is folder
|
|
94
|
+
if (fs.lstatSync(path.join(this.path, 'events', file)).isDirectory()) {
|
|
95
|
+
console.log('registering events folder ' + file);
|
|
96
|
+
this.registerEventsFolder(file);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async registerEventsFolder(folder) {
|
|
101
|
+
const folderPath = path.join(this.path, 'events', folder);
|
|
102
|
+
if (!fs.existsSync(folderPath))
|
|
103
|
+
throw new Error(`Folder ${folder} doesn't exist`);
|
|
104
|
+
const files = fs.readdirSync(folderPath);
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
if (file.endsWith('.js') || file.endsWith('.ts')) {
|
|
107
|
+
let event = await import('file://' + path.join(folderPath, file)).catch((e) => {
|
|
108
|
+
console.error(`[🔄🔴 ] Error loading ${file.slice(0, -3)} event on module ${this.constructor.name}`);
|
|
109
|
+
});
|
|
110
|
+
event = Object.values(event)[0];
|
|
111
|
+
event = new event();
|
|
112
|
+
this.events.set(event.constructor.name.toLowerCase(), event);
|
|
113
|
+
this.registerEvent(event, folder);
|
|
111
114
|
}
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
|
-
|
|
117
|
+
registerEvent(frameworkEvent, emitterName) {
|
|
115
118
|
if (frameworkEvent.disabled)
|
|
116
119
|
return;
|
|
120
|
+
const once = frameworkEvent.once;
|
|
117
121
|
const eventName = frameworkEvent.constructor.name.charAt(0).toLowerCase() +
|
|
118
122
|
frameworkEvent.constructor.name.slice(1);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
emitter[once ? 'once' : 'on'](eventName, (...args) => frameworkEvent.execute(this.parseEventArgs(args))); // Run the event using the above defined emitter (client)
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
console.log(error, error.message, error, name);
|
|
127
|
-
console.error(error.stack); // If there is an error, console log the error stack message
|
|
128
|
-
}
|
|
123
|
+
this.framework.eventManager.addEventListener(emitterName, eventName, (...args) => {
|
|
124
|
+
const finalArgs = this.parseEventArgs(args);
|
|
125
|
+
frameworkEvent.execute(finalArgs);
|
|
126
|
+
}, { once });
|
|
129
127
|
}
|
|
130
128
|
parseEventArgs(args) {
|
|
131
129
|
const finalArgs = {
|
|
@@ -137,7 +135,8 @@ export class Module {
|
|
|
137
135
|
});
|
|
138
136
|
const interaction = args.find((arg) => arg instanceof StringSelectMenuInteraction ||
|
|
139
137
|
arg instanceof CommandInteraction ||
|
|
140
|
-
arg instanceof ButtonInteraction
|
|
138
|
+
arg instanceof ButtonInteraction ||
|
|
139
|
+
arg instanceof ModalSubmitInteraction);
|
|
141
140
|
if (interaction) {
|
|
142
141
|
finalArgs['interaction'] = interaction;
|
|
143
142
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface SelectMenuIdParams {
|
|
2
|
+
id: string;
|
|
3
|
+
params?: string[];
|
|
4
|
+
}
|
|
5
|
+
export declare class InteractionIdGenerator {
|
|
6
|
+
module: string;
|
|
7
|
+
command: string;
|
|
8
|
+
constructor(module?: string, command?: string);
|
|
9
|
+
protected generatePrefix(): string;
|
|
10
|
+
generateSelectMenuId(id: string, params?: string[]): string;
|
|
11
|
+
generateButtonId(id: string, params?: string[]): string;
|
|
12
|
+
generateModalId(id: string, params?: string[]): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class InteractionIdGenerator {
|
|
2
|
+
module;
|
|
3
|
+
command;
|
|
4
|
+
constructor(module, command) {
|
|
5
|
+
this.module = module;
|
|
6
|
+
this.command = command;
|
|
7
|
+
}
|
|
8
|
+
generatePrefix() {
|
|
9
|
+
const components = [];
|
|
10
|
+
if (this.module)
|
|
11
|
+
components.push(this.module);
|
|
12
|
+
if (this.command)
|
|
13
|
+
components.push(this.command);
|
|
14
|
+
return components.join('.');
|
|
15
|
+
}
|
|
16
|
+
generateSelectMenuId(id, params) {
|
|
17
|
+
let components = [this.generatePrefix()];
|
|
18
|
+
if (id)
|
|
19
|
+
components.push(id);
|
|
20
|
+
if (params)
|
|
21
|
+
components = components.concat(params);
|
|
22
|
+
return components.join('.');
|
|
23
|
+
}
|
|
24
|
+
generateButtonId(id, params) {
|
|
25
|
+
let components = [this.generatePrefix()];
|
|
26
|
+
if (id)
|
|
27
|
+
components.push(id);
|
|
28
|
+
if (params)
|
|
29
|
+
components = components.concat(params);
|
|
30
|
+
return components.join('.');
|
|
31
|
+
}
|
|
32
|
+
generateModalId(id, params) {
|
|
33
|
+
let components = [this.generatePrefix()];
|
|
34
|
+
if (id)
|
|
35
|
+
components.push(id);
|
|
36
|
+
if (params)
|
|
37
|
+
components = components.concat(params);
|
|
38
|
+
return components.join('.');
|
|
39
|
+
}
|
|
40
|
+
}
|