Is there a way to make an Event (registerApplicationCommands) 'wait' before triggering?

Okay so I created a handler named unregisterer.js I'm calling it in another handler named bootstrap.js bootstrap.js is then imported into index.js then called as such await handlers() on ready event I use a custom structure named SlashCommand imported in every command file (to replace Command of @sapphire/framework): this only serves to prevent spamming registerApplicationCommands in every single command file meanwhile Command is already called within the structure itself to serve it's purpose The expected behavior is to first unregister all the Global commands, afterwards register them once again, acts as a reload for the existing commands There's 3 types of different behaviors when booting up the bot in a row, I've embedded a video
192 Replies
Favna
Favna•2mo ago
Sapphire Framework
Registering Application Commands outside a Command | Sapphire
You are able to register your application commands inside of their command class, but also outside of the class! Whether
Zue
ZueOP•2mo ago
that's not what I need "The expected behavior is to first unregister all the Global commands, afterwards register them once again, acts as a reload for the existing commands"
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');

const { time, clientId } = require('../utils/Common.js');
const { yellow, red, magenta } = require('colors');

const Unregisterer = {
async method(isGuildSpecific, guildId) {
const rest = new REST({ version: '9' }).setToken(process.env.token);
const commands = await rest.get(
isGuildSpecific
? Routes.applicationGuildCommands(clientId, guildId)
: Routes.applicationCommands(clientId)
);

try {
for(const command of commands) {
const logname = isGuildSpecific ? 'SLASH_COMMANDS_GUILD' : 'SLASH_COMMANDS_GLOBAL';
if(!command) return;

await rest.delete(Routes.applicationCommand(clientId, command.id));
console.log(time(), yellow(logname), 'Unregistered', magenta(command.name));
}
} catch(err) {
console.log(time(), red('SLASH_COMMANDS_UREGISTERER'), err.message);
}
},

async unregisterGlobalCommands() {
return await this.method(false);
},

async unregisterGuildCommands(client) {
const guildId = client.guilds.cache.map(guild => guild.id);
return await this.method(true, guildId);
}
}

module.exports = Unregisterer;
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');

const { time, clientId } = require('../utils/Common.js');
const { yellow, red, magenta } = require('colors');

const Unregisterer = {
async method(isGuildSpecific, guildId) {
const rest = new REST({ version: '9' }).setToken(process.env.token);
const commands = await rest.get(
isGuildSpecific
? Routes.applicationGuildCommands(clientId, guildId)
: Routes.applicationCommands(clientId)
);

try {
for(const command of commands) {
const logname = isGuildSpecific ? 'SLASH_COMMANDS_GUILD' : 'SLASH_COMMANDS_GLOBAL';
if(!command) return;

await rest.delete(Routes.applicationCommand(clientId, command.id));
console.log(time(), yellow(logname), 'Unregistered', magenta(command.name));
}
} catch(err) {
console.log(time(), red('SLASH_COMMANDS_UREGISTERER'), err.message);
}
},

async unregisterGlobalCommands() {
return await this.method(false);
},

async unregisterGuildCommands(client) {
const guildId = client.guilds.cache.map(guild => guild.id);
return await this.method(true, guildId);
}
}

module.exports = Unregisterer;
Unregisterer.unregisterGlobalCommands() is executed in applicationCommandRegistriesInitialising when the event it's triggered
const { Listener, ApplicationCommandRegistries: registry, RegisterBehavior } = require('@sapphire/framework');
const Unregisterer = require('../handlers/unregisterer.js');

const { time, logs } = require('../utils/Common.js');
const { yellow, red } = require('colors');

class SlashInitialize extends Listener {
/**
* @param {Listener.Context} context
* @param {Listener.Options} options
*/
constructor(context, options) {
super(context, {
...options,
event: 'applicationCommandRegistriesInitialising'
})
}

async run() {
const client = this.container.client;
const guilds = client.guilds.cache.map(guild => guild.id);

try {
// registry.getDefaultBehaviorWhenNotIdentical(RegisterBehavior.Overwrite);
registry.setDefaultGuildIds(guilds);
await Unregisterer.unregisterGlobalCommands();
} catch(err) {
console.log(time(), red('SLASH_COMMANDS_INIT'), err.message)
}

logs.slashInit
? console.log(time(), yellow('SLASH_COMMANDS_INIT'), 'Initializing')
: null;
}
}

module.exports = SlashInitialize;
const { Listener, ApplicationCommandRegistries: registry, RegisterBehavior } = require('@sapphire/framework');
const Unregisterer = require('../handlers/unregisterer.js');

const { time, logs } = require('../utils/Common.js');
const { yellow, red } = require('colors');

class SlashInitialize extends Listener {
/**
* @param {Listener.Context} context
* @param {Listener.Options} options
*/
constructor(context, options) {
super(context, {
...options,
event: 'applicationCommandRegistriesInitialising'
})
}

async run() {
const client = this.container.client;
const guilds = client.guilds.cache.map(guild => guild.id);

try {
// registry.getDefaultBehaviorWhenNotIdentical(RegisterBehavior.Overwrite);
registry.setDefaultGuildIds(guilds);
await Unregisterer.unregisterGlobalCommands();
} catch(err) {
console.log(time(), red('SLASH_COMMANDS_INIT'), err.message)
}

logs.slashInit
? console.log(time(), yellow('SLASH_COMMANDS_INIT'), 'Initializing')
: null;
}
}

module.exports = SlashInitialize;
However structures/SlashCommands.js will also proceed to register commands at the same time, I can't come up with a combo to make it "wait" to complete unregistering all older global commands this is structures/SlashCommand.js structure that I wrote to individually register each command without repasting the same code in every single command
const { Command } = require('@sapphire/framework');

const { cyan } = require('colors');
const { time, logs } = require('../utils/Common.js');

class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options
});
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand(builder => {
logs.slash
? console.log(time(), cyan('SLASH_REGISTRY'), this.name)
: null;

return builder
.setName(this.name)
.setDescription(this.description);
});
}
}

module.exports = SlashCommand;
const { Command } = require('@sapphire/framework');

const { cyan } = require('colors');
const { time, logs } = require('../utils/Common.js');

class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options
});
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand(builder => {
logs.slash
? console.log(time(), cyan('SLASH_REGISTRY'), this.name)
: null;

return builder
.setName(this.name)
.setDescription(this.description);
});
}
}

module.exports = SlashCommand;
and this is how I use SlashCommand structure in my command files
const SlashCommand = require('../../structures/SlashCommand.js');

class TestCommand extends SlashCommand {
/**
* @param {Listener.Context} context
* @param {Listener.Options} options
*/
constructor(context) {
super(context, {
name: 'test',
description: 'Test the basic layout of a command as is.',
preconditions: ['Developer']
});
}

/**
* @param {Interaction} interaction
*/
run(interaction) {
interaction.reply({ content: 'Um ok? >:3' });
}
}

module.exports = TestCommand;
const SlashCommand = require('../../structures/SlashCommand.js');

class TestCommand extends SlashCommand {
/**
* @param {Listener.Context} context
* @param {Listener.Options} options
*/
constructor(context) {
super(context, {
name: 'test',
description: 'Test the basic layout of a command as is.',
preconditions: ['Developer']
});
}

/**
* @param {Interaction} interaction
*/
run(interaction) {
interaction.reply({ content: 'Um ok? >:3' });
}
}

module.exports = TestCommand;
not even sure if the way I wrote Unregisterer func even works as intended 'cus I see duplicated list of commands, both global & guild registered are displayed
Zue
ZueOP•2mo ago
not to mention, if I include a piece of code in applicationCommandRegistriesInitialising eventlistener, execution is delayed as console.log came late, async/await in this scenario serves 0 purpose, I might not be a master at js but frankly I'm on a stale like this
Favna
Favna•2mo ago
honestly you've kind of lost me... but to answer the question of
Is there a way to make an Event (registerApplicationCommands) 'wait' before triggering?
afaik no. @vladdy?
Zue
ZueOP•2mo ago
take ur time to read this, I'm not expecting an immediate answer I also noticed what the cause of the delay is however I still have doubts by the way I'm handling this
vladdy
vladdy•2mo ago
Outside the fact you should not do this (delete and recreate to update commands, use commands.set), why are you manually handling command registration and also telling sapphire what to do? It really sounds like all you need is to set the default method to bulkoverwrite, and setting the default guild ids somewhere before calling login and thats all And the answer to the wait for x before an event handler is ran, the answer is no.
Zue
ZueOP•2mo ago
does sapphire handle the deregistration of unexistent commands by itself?
vladdy
vladdy•2mo ago
If you use BulkOverwrite, yes Otherwise no BulkOverwrite tells sapphire "you handle the entire state of the applications commands, any external changes can be overwritten/removed" Whereas the other modes tell sapphire to handle only what its told about
Zue
ZueOP•2mo ago
so is it fine if I do registry.getDefaultBehaviorWhenNotIdentical(RegisterBehavior.BulkOverwrite); in applicationCommandRegistriesInitialising eventlistener
vladdy
vladdy•2mo ago
Bulkoverwrite, and you call it before login And not get ApplicationCommandRegistries.setDefaultBehaviorWhenNotIdentical
Zue
ZueOP•2mo ago
I see where my mistake was thanks
vladdy
vladdy•2mo ago
which applies globally :salute:
Zue
ZueOP•2mo ago
yeah but thing is I want to prevent global commands from being defined lol that's what registry.setDefaultGuildIds(guilds); is for
vladdy
vladdy•2mo ago
So then also call ApplicationCommandRegistries.setDefaultGuildIds before login too
Zue
ZueOP•2mo ago
ye xD before login huh
vladdy
vladdy•2mo ago
ya
Zue
ZueOP•2mo ago
I'll import them as handlers before login then
Zue
ZueOP•2mo ago
thanks for the support
vladdy
vladdy•2mo ago
You dont need it to be a handler tbh
Zue
ZueOP•2mo ago
I just want to make the core file as clean as possible yk so I can rid of stuff like unregisterer.js and just directly use the BulkOverwrite provided by sapphire
vladdy
vladdy•2mo ago
mhm
Zue
ZueOP•2mo ago
I knew it had some automation but hell I read docs and I was this clueless 😭
vladdy
vladdy•2mo ago
Just keep in mind that if you create a cmd outside sapphire, and then you start with Bulkoverwrite, that cmd will vanish And the only commands that will stick are whatever is in the command registries
Zue
ZueOP•2mo ago
I'll give it a try rn and will give a feedback later gotta write some code and start testing a thing tho, if I want to turn off the ApplicationCommandRegistry[command.name] logs that popup when a new command is registered, seems like loadApplicationCommandRegistriesStatusListeners set to false can't do that by itself Seems like ApplicationCommandRegistry.setDefaultGuildIds is unrecognized as a function in my use case Ima find another approach
vladdy
vladdy•2mo ago
it does exist show code Not registry
Zue
ZueOP•2mo ago
I know it does exist
vladdy
vladdy•2mo ago
Registries
vladdy
vladdy•2mo ago
not that one
Zue
ZueOP•2mo ago
oh
vladdy
vladdy•2mo ago
ApplicationCommandRegistries Registry is an instance per command
Zue
ZueOP•2mo ago
ah this is way better now now there's only 2-3 things I need to do before I actually work on the commands wait an hour to see if BulkOverwrite did the job and global commands get removed so no more 'duplicates'
vladdy
vladdy•2mo ago
its not an hour
Zue
ZueOP•2mo ago
second is to find out why Preconditions does not want to work
vladdy
vladdy•2mo ago
just refresh your client
Zue
ZueOP•2mo ago
restart the bot or wym
vladdy
vladdy•2mo ago
no, ctrl r in discord
Zue
ZueOP•2mo ago
oh I did that but still dupe earlier I'll try quitting discord
vladdy
vladdy•2mo ago
and do both run?
Zue
ZueOP•2mo ago
obv not p sure the one that doesn't run is the global command
vladdy
vladdy•2mo ago
check command ids i guess
Zue
ZueOP•2mo ago
this way?
vladdy
vladdy•2mo ago
naw well
Zue
ZueOP•2mo ago
lol
vladdy
vladdy•2mo ago
log command.commandRegistry or i think applicationCommandRegistry
Zue
ZueOP•2mo ago
ye applicationCommandRegistry
vladdy
vladdy•2mo ago
yee, that should show ids and all
Zue
ZueOP•2mo ago
nope, it's only a batch no duplicates
vladdy
vladdy•2mo ago
hmm?
Zue
ZueOP•2mo ago
it must be my discord then
vladdy
vladdy•2mo ago
screenshot pls
Zue
ZueOP•2mo ago
[
ApplicationCommandRegistry {
chatInputCommands: Set(0) {},
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(0) {},
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(0) [Map] {},
guildIdToChatInputCommandIds: Collection(0) [Map] {},
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [],
commandName: 'reload'
},
ApplicationCommandRegistry {
chatInputCommands: Set(0) {},
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(0) {},
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(0) [Map] {},
guildIdToChatInputCommandIds: Collection(0) [Map] {},
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [],
commandName: 'test'
},
ApplicationCommandRegistry {
chatInputCommands: Set(0) {},
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(0) {},
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(0) [Map] {},
guildIdToChatInputCommandIds: Collection(0) [Map] {},
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [],
commandName: 'avatar'
}
]
[
ApplicationCommandRegistry {
chatInputCommands: Set(0) {},
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(0) {},
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(0) [Map] {},
guildIdToChatInputCommandIds: Collection(0) [Map] {},
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [],
commandName: 'reload'
},
ApplicationCommandRegistry {
chatInputCommands: Set(0) {},
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(0) {},
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(0) [Map] {},
guildIdToChatInputCommandIds: Collection(0) [Map] {},
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [],
commandName: 'test'
},
ApplicationCommandRegistry {
chatInputCommands: Set(0) {},
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(0) {},
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(0) [Map] {},
guildIdToChatInputCommandIds: Collection(0) [Map] {},
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [],
commandName: 'avatar'
}
]
vladdy
vladdy•2mo ago
..empty? that..is not right
Zue
ZueOP•2mo ago
cus I'm console logging this before they're being registered that's why it is empty
vladdy
vladdy•2mo ago
OH
Zue
ZueOP•2mo ago
let me log it after it's registered sec I got an event for that
[
ApplicationCommandRegistry {
chatInputCommands: Set(2) { 'reload', '1342621488737554514' },
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(1) { '1294025686532751511' },
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(1) [Map] {
'1294025686532751511' => '1342621488737554514'
},
guildIdToChatInputCommandIds: Collection(1) [Map] { '1294025686532751511' => [Set] },
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [ [Object] ],
commandName: 'reload'
},
ApplicationCommandRegistry {
chatInputCommands: Set(2) { 'test', '1342621488737554515' },
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(1) { '1294025686532751511' },
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(1) [Map] {
'1294025686532751511' => '1342621488737554515'
},
guildIdToChatInputCommandIds: Collection(1) [Map] { '1294025686532751511' => [Set] },
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [ [Object] ],
commandName: 'test'
},
ApplicationCommandRegistry {
chatInputCommands: Set(2) { 'avatar', '1342621488737554516' },
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(1) { '1294025686532751511' },
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(1) [Map] {
'1294025686532751511' => '1342621488737554516'
},
guildIdToChatInputCommandIds: Collection(1) [Map] { '1294025686532751511' => [Set] },
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [ [Object] ],
commandName: 'avatar'
}
]
[
ApplicationCommandRegistry {
chatInputCommands: Set(2) { 'reload', '1342621488737554514' },
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(1) { '1294025686532751511' },
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(1) [Map] {
'1294025686532751511' => '1342621488737554514'
},
guildIdToChatInputCommandIds: Collection(1) [Map] { '1294025686532751511' => [Set] },
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [ [Object] ],
commandName: 'reload'
},
ApplicationCommandRegistry {
chatInputCommands: Set(2) { 'test', '1342621488737554515' },
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(1) { '1294025686532751511' },
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(1) [Map] {
'1294025686532751511' => '1342621488737554515'
},
guildIdToChatInputCommandIds: Collection(1) [Map] { '1294025686532751511' => [Set] },
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [ [Object] ],
commandName: 'test'
},
ApplicationCommandRegistry {
chatInputCommands: Set(2) { 'avatar', '1342621488737554516' },
contextMenuCommands: Set(0) {},
guildIdsToFetch: Set(1) { '1294025686532751511' },
globalCommandId: null,
globalChatInputCommandIds: Set(0) {},
globalContextMenuCommandIds: Set(0) {},
guildCommandIds: Collection(1) [Map] {
'1294025686532751511' => '1342621488737554516'
},
guildIdToChatInputCommandIds: Collection(1) [Map] { '1294025686532751511' => [Set] },
guildIdToContextMenuCommandIds: Collection(0) [Map] {},
apiCalls: [ [Object] ],
commandName: 'avatar'
}
]
vladdy
vladdy•2mo ago
yay its on the client showing it wrong thank lord LMAO
Zue
ZueOP•2mo ago
wait what 😭 that's what I said give me 2 min I quit discord
vladdy
vladdy•2mo ago
yeah no sapphire handled it right, only shows the guild one, i was worried smth was fucking up XD
Zue
ZueOP•2mo ago
I came migrating over from raw commands with raw commands I could do anything 😭 but decided few days ago it's time to move on... so I did a LOT of trial & error with slash commands obv sapphire framework cus Imo best
vladdy
vladdy•2mo ago
Realistically this should work, MIGHT have caching issues because discord but in the long term it'll be better (i'd assume)
Zue
ZueOP•2mo ago
If there's something that doesn't work as intended Is that preconditions don't work with slash commands šŸ’€ at least for me
vladdy
vladdy•2mo ago
show the preconditions
Zue
ZueOP•2mo ago
const { Precondition } = require('@sapphire/framework');
const { list, emoji } = require('../utils/Common.js');

class Developer extends Precondition {
/**
* @param {Interaction} interaction
*/
run(interaction) {
if (!interaction.member) return;
return list.includes(interaction.author.id)
? this.ok()
: this.error(interaction.reply(`${emoji.SilverPleads} Surry but this command is ONLY for the **Developer**!`));
}
}

module.exports = { Developer }
const { Precondition } = require('@sapphire/framework');
const { list, emoji } = require('../utils/Common.js');

class Developer extends Precondition {
/**
* @param {Interaction} interaction
*/
run(interaction) {
if (!interaction.member) return;
return list.includes(interaction.author.id)
? this.ok()
: this.error(interaction.reply(`${emoji.SilverPleads} Surry but this command is ONLY for the **Developer**!`));
}
}

module.exports = { Developer }
vladdy
vladdy•2mo ago
oh duh yeah that will never work its chatInputRun for slash cmds
Zue
ZueOP•2mo ago
shrug run should handle it the same way
vladdy
vladdy•2mo ago
messageRun and contextMenuRun respectively
Zue
ZueOP•2mo ago
no?
vladdy
vladdy•2mo ago
Run doesn't exist in sapphire anymore for Preconditions and Commands
Zue
ZueOP•2mo ago
cus I also use run for commands and rest of the eventlisteners then why does my thing work 😭 wtf
vladdy
vladdy•2mo ago
Edited in more context Commands and Preconditions use <type>Run because you might need diff handling for messages vs interactions Listeners n such just use run tho your cmd is also wrong, will never run without it being chatInputRun
Zue
ZueOP•2mo ago
uh
Zue
ZueOP•2mo ago
I thought this was a client issue šŸ’€
vladdy
vladdy•2mo ago
:Confusion: What sapphire version are you on
Zue
ZueOP•2mo ago
whatever gave me when I downloaded sapphire a few days ago, should be latest
^5.3.2
^5.3.2
,...unless it's not
vladdy
vladdy•2mo ago
Uh, we definitely set globals to empty array if commands are registered as guild only by def Can you share more code, not just snippets pls
Zue
ZueOP•2mo ago
Is there a chance cus of my SlashCommand constructor
const { Command } = require('@sapphire/framework');

const { cyan } = require('colors');
const { time, logs } = require('../utils/Common.js');

class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options
});
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand(builder => {
logs.slash
? console.log(time(), cyan('SLASH_COMMAND'), this.name)
: null;

builder
.setName(this.name)
.setDescription(this.description);

/*
if(this.options) builder
.options(this.options.forEach(option => {
if(option.type === 'STRING') builder.addStringOption(item => item.setType(option.type));
if(option.type === 'INTEGER') builder.addIntegerOption(item => item.setType(option.type));
}));
*/
return builder;
});
}
}

module.exports = SlashCommand;
const { Command } = require('@sapphire/framework');

const { cyan } = require('colors');
const { time, logs } = require('../utils/Common.js');

class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options
});
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand(builder => {
logs.slash
? console.log(time(), cyan('SLASH_COMMAND'), this.name)
: null;

builder
.setName(this.name)
.setDescription(this.description);

/*
if(this.options) builder
.options(this.options.forEach(option => {
if(option.type === 'STRING') builder.addStringOption(item => item.setType(option.type));
if(option.type === 'INTEGER') builder.addIntegerOption(item => item.setType(option.type));
}));
*/
return builder;
});
}
}

module.exports = SlashCommand;
vladdy
vladdy•2mo ago
naw that looks fine
Zue
ZueOP•2mo ago
oh ok
vladdy
vladdy•2mo ago
Can you show your main file
Zue
ZueOP•2mo ago
const { SapphireClient } = require('@sapphire/framework');
const { GatewayIntentBits, Partials } = require('discord.js');

const { time, red, green } = require('./utils/Common.js');

require('dotenv').config();
const { token } = process.env;

const client = new SapphireClient({
loadMessageCommandListeners: false,
loadApplicationCommandRegistriesStatusListeners: false,
intents: [ GatewayIntentBits.MessageContent, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildPresences, GatewayIntentBits.DirectMessages ],
partials: [ Partials.Message, Partials.Channel, Partials.Reaction ]
});

require('./handlers/bootstrap.js')();

client.login(token)
.then(() => console.log(time(), green('READY'), 'Nyasha booted up'))
.catch(time(), red('CLIENT_LOGIN'), 'Unable to use token');
const { SapphireClient } = require('@sapphire/framework');
const { GatewayIntentBits, Partials } = require('discord.js');

const { time, red, green } = require('./utils/Common.js');

require('dotenv').config();
const { token } = process.env;

const client = new SapphireClient({
loadMessageCommandListeners: false,
loadApplicationCommandRegistriesStatusListeners: false,
intents: [ GatewayIntentBits.MessageContent, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildPresences, GatewayIntentBits.DirectMessages ],
partials: [ Partials.Message, Partials.Channel, Partials.Reaction ]
});

require('./handlers/bootstrap.js')();

client.login(token)
.then(() => console.log(time(), green('READY'), 'Nyasha booted up'))
.catch(time(), red('CLIENT_LOGIN'), 'Unable to use token');
vladdy
vladdy•2mo ago
and in bootstrap?
Zue
ZueOP•2mo ago
const framework = require('./framework.js');

const { time, cyan } = require('../utils/Common.js');

module.exports = async function bootstrap() {
framework.loadOnGuilds(['1294025686532751511']);
framework.loadOverwrite();
}
const framework = require('./framework.js');

const { time, cyan } = require('../utils/Common.js');

module.exports = async function bootstrap() {
framework.loadOnGuilds(['1294025686532751511']);
framework.loadOverwrite();
}
const {
ApplicationCommandRegistries: Registry,
RegisterBehavior: Behavior
} = require('@sapphire/framework');
const { time, cyan } = require('../utils/Common.js');

module.exports = framework = {
loadOnGuilds(guildIds) {
Registry.setDefaultGuildIds(guildIds);
console.log(time(), cyan('REGISTRIES'), 'Set Guild IDs');
},

loadOverwrite() {
Behavior.BulkOverwrite;
console.log(time(), cyan('REGISTRIES'), 'Bulk Overwritten');
}
}
const {
ApplicationCommandRegistries: Registry,
RegisterBehavior: Behavior
} = require('@sapphire/framework');
const { time, cyan } = require('../utils/Common.js');

module.exports = framework = {
loadOnGuilds(guildIds) {
Registry.setDefaultGuildIds(guildIds);
console.log(time(), cyan('REGISTRIES'), 'Set Guild IDs');
},

loadOverwrite() {
Behavior.BulkOverwrite;
console.log(time(), cyan('REGISTRIES'), 'Bulk Overwritten');
}
}
vladdy
vladdy•2mo ago
loadOverwrite is uh Invalid :KEKW:
Zue
ZueOP•2mo ago
šŸ¦• no way If I called it as a function it'd return undefined what
vladdy
vladdy•2mo ago
Registry.setDefaultBehaviorWhenNotIdentical(Behavior.BulkOverwrite) also drop async from that function, you dont need it and it may prevent race conditions XD
Zue
ZueOP•2mo ago
I love async cus it creates me more problems to worry about šŸ’€
vladdy
vladdy•2mo ago
not if you have eslint/ts yell for promises not being handled
Zue
ZueOP•2mo ago
you've no idea how many times some devs raped me to always use it 😭
vladdy
vladdy•2mo ago
async await is fine! just gotta be careful with it
Zue
ZueOP•2mo ago
EYYY btw one thing this
vladdy
vladdy•2mo ago
Those log messages cannot be turned off for bulk, woops LOL
Zue
ZueOP•2mo ago
I'm gonna commit šŸ¦• anyway let me see if commands now dupe
vladdy
vladdy•2mo ago
Make an issue for it, we can make it an event Like for normal registrations
Zue
ZueOP•2mo ago
In the end I just want it to customize the console get my point? I don't know what any other use would it serve
vladdy
vladdy•2mo ago
Use or make a logger and done
Zue
ZueOP•2mo ago
a logger to do what It wont prevent it from logging
vladdy
vladdy•2mo ago
to override the default one
Zue
ZueOP•2mo ago
oh well
vladdy
vladdy•2mo ago
then you can also ignore specific messages BUT
Zue
ZueOP•2mo ago
I don't know to use those
vladdy
vladdy•2mo ago
.
Zue
ZueOP•2mo ago
I did see it on ur docs tho on github? sure
vladdy
vladdy•2mo ago
Yeah, bulk was added after that setting :OMEGALUL:, and we always wanted to print something so people knew that smth happened
Zue
ZueOP•2mo ago
too much to write for a feature request šŸ’€
vladdy
vladdy•2mo ago
Just link this thread Give it a good title and link this thread Its fine XD
Zue
ZueOP•2mo ago
request: Add ApplicationCommandRegistries Event Listener šŸ¤·ā€ā™‚ļø
vladdy
vladdy•2mo ago
BulkOverwrite event listener but yes
Zue
ZueOP•2mo ago
why not just create a generic event that listens to every subevent of that type of event
vladdy
vladdy•2mo ago
bc this was added later :OMEGALUL:
Zue
ZueOP•2mo ago
😭
vladdy
vladdy•2mo ago
But yeah just make the issue, I already know how to handle this
Zue
ZueOP•2mo ago
:kekGasm: BulkOverwriteError before BulkOverwrite
vladdy
vladdy•2mo ago
yeah we only have an event for error, not info :OMEGALUL:
Zue
ZueOP•2mo ago
I mean It works out
vladdy
vladdy•2mo ago
hey but it works now :thanksForFlying:
Zue
ZueOP•2mo ago
yes It works so well I forgot to get back to testing lol Say, if I wanted to create an event listener for grabbing any sort of error that a command, a handler or an event listener may cause, does sapphire got any sort of integrity in that kind of exertise? Considering I had to use try/catch everywhere, engulfing everything within a function call I thought about it but it's bad practice Imo
vladdy
vladdy•2mo ago
yeah we have error events ListenerError, CommandError (split by type of commands), etc
Zue
ZueOP•2mo ago
the issue with precondition still persists
Zue
ZueOP•2mo ago
even after I replaced run with chatInputRun
vladdy
vladdy•2mo ago
mmm does it get called? and whats the file name of the precondition
Zue
ZueOP•2mo ago
developer.js Is it case sensitive so it must match the precondition's class name? tried console logging, it does not get triggered at all šŸ¤·ā€ā™‚ļø despite someone who isn't in that list using the command
vladdy
vladdy•2mo ago
yes if you dont pass in a constructor with name: "", it takes by the file name and case sensitive
Zue
ZueOP•2mo ago
bruh I was wondering why was I getting the whole array instead of just options the way it's stored 😭 I see, thanks question tho but the relevant properties stored in the commands is having name & description duped in options a bug or feature
vladdy
vladdy•2mo ago
wdym
Zue
ZueOP•2mo ago
[17:03:01::945] SLASH_COMMAND avatar
<ref *2> Avatar_Misc {
name: 'avatar',
description: 'Get avatar.',
options: { name: 'avatar', description: 'Get avatar.', options: [ [Object] ] },
}
[17:03:01::945] SLASH_COMMAND avatar
<ref *2> Avatar_Misc {
name: 'avatar',
description: 'Get avatar.',
options: { name: 'avatar', description: 'Get avatar.', options: [ [Object] ] },
}
😭 It's literally set to store things this way
vladdy
vladdy•2mo ago
uhhhhhh is that this.options? but i think its intended, that idk about LMAO
Zue
ZueOP•2mo ago
I want to get this.options yes but I had to do this.options.options to reach it šŸ’€ cus duped information is stacked within the options array can I report this as an issue
vladdy
vladdy•2mo ago
i think its intended
Zue
ZueOP•2mo ago
we already got name & description on command itself šŸ¤·ā€ā™‚ļø
vladdy
vladdy•2mo ago
Just check when it was added and why but iirc it was intended for smth
vladdy
vladdy•2mo ago
uh
Zue
ZueOP•2mo ago
well whatever
vladdy
vladdy•2mo ago
oh the core piece stores the options
vladdy
vladdy•2mo ago
Yeah this was intended, but you can extend the cmd class to store your commandOptions in a this.field
Zue
ZueOP•2mo ago
this.options.options If anyone asks me, I'll tell them which framework I'm using :kekw:
vladdy
vladdy•2mo ago
just store this.commandOptions = options.options in your constructor
Zue
ZueOP•2mo ago
that works out too Ig I made a handler for slash options, it works however I'm getting Unknown interaction every time
vladdy
vladdy•2mo ago
thats bc if your precondition doesnt return ok, it will reject and we emit an event for it so you need to handle it
Zue
ZueOP•2mo ago
It's not the precondition
vladdy
vladdy•2mo ago
its a command denied event you'll need to share code! :Prayge:
Zue
ZueOP•2mo ago
yes
vladdy
vladdy•2mo ago
otherwise cant help
Zue
ZueOP•2mo ago
sec this is the testing command, avatar.js
const SlashCommand = require('../../structures/SlashCommand.js');
const { color } = require('../../utils/Common.js');

const { EmbedBuilder } = require('discord.js');

class Avatar_Misc extends SlashCommand {
/**
* @param {Listener.Context} context
* @param {Listener.Options} options
*/
constructor(context) {
super(context, {
name: 'avatar',
description: 'Get avatar.',
options: [
{
name: 'user',
type: 'USER',
description: 'Mention user',
required: false
}
]
});
}

embed(user) {
return new EmbedBuilder()
.setTitle(`${user.tag}'s Avatar`)
.setImage(user.displayAvatarURL({ extension: 'png', size: 2048 }))
.setColor(color.chestnut);
}

/**
* @param {Interaction} interaction
*/
async chatInputRun(interaction) {
const user = interaction.options.getUser('user') || interaction.user;
await interaction.reply({ embeds: [this.embed(user)] });
}
}

module.exports = Avatar_Misc;
const SlashCommand = require('../../structures/SlashCommand.js');
const { color } = require('../../utils/Common.js');

const { EmbedBuilder } = require('discord.js');

class Avatar_Misc extends SlashCommand {
/**
* @param {Listener.Context} context
* @param {Listener.Options} options
*/
constructor(context) {
super(context, {
name: 'avatar',
description: 'Get avatar.',
options: [
{
name: 'user',
type: 'USER',
description: 'Mention user',
required: false
}
]
});
}

embed(user) {
return new EmbedBuilder()
.setTitle(`${user.tag}'s Avatar`)
.setImage(user.displayAvatarURL({ extension: 'png', size: 2048 }))
.setColor(color.chestnut);
}

/**
* @param {Interaction} interaction
*/
async chatInputRun(interaction) {
const user = interaction.options.getUser('user') || interaction.user;
await interaction.reply({ embeds: [this.embed(user)] });
}
}

module.exports = Avatar_Misc;
vladdy
vladdy•2mo ago
and is your chatInputRun called?
Zue
ZueOP•2mo ago
and I wrote allit on SlashCommand structure to register Slash Command otions as well
const { Command } = require('@sapphire/framework');

const { cyan } = require('colors');
const { time, logs } = require('../utils/Common.js');

class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options
});
this.opts = options.options;
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand(builder => {
logs.slash
? console.log(time(), cyan('SLASH_COMMAND'), this.name)
: null;

builder
.setName(this.name)
.setDescription(this.description);

const args = (field) => (option) => {
return option
.setName(field.name)
.setDescription(field.description);
};

if(this.opts) this.opts.forEach(field => {
if(field.type === 'STRING') builder.addStringOption(args(field));
if(field.type === 'INTEGER') builder.addIntegerOption(args(field));
if(field.type === 'BOOLEAN') builder.addBooleanOption(args(field));
/* */
if(field.type === 'USER') builder.addUserOption(args(field));
if(field.type === 'ROLE') builder.addRoleOption(args(field));
if(field.type === 'MENTIONABLE') builder.addMentionableOption(args(field));
if(field.type === 'CHANNEL') builder.addChannelOption(args(field));
})

return builder;
});
}
}

module.exports = SlashCommand;
const { Command } = require('@sapphire/framework');

const { cyan } = require('colors');
const { time, logs } = require('../utils/Common.js');

class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options
});
this.opts = options.options;
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand(builder => {
logs.slash
? console.log(time(), cyan('SLASH_COMMAND'), this.name)
: null;

builder
.setName(this.name)
.setDescription(this.description);

const args = (field) => (option) => {
return option
.setName(field.name)
.setDescription(field.description);
};

if(this.opts) this.opts.forEach(field => {
if(field.type === 'STRING') builder.addStringOption(args(field));
if(field.type === 'INTEGER') builder.addIntegerOption(args(field));
if(field.type === 'BOOLEAN') builder.addBooleanOption(args(field));
/* */
if(field.type === 'USER') builder.addUserOption(args(field));
if(field.type === 'ROLE') builder.addRoleOption(args(field));
if(field.type === 'MENTIONABLE') builder.addMentionableOption(args(field));
if(field.type === 'CHANNEL') builder.addChannelOption(args(field));
})

return builder;
});
}
}

module.exports = SlashCommand;
command works yes even shows option to select user yes and gets their pfp but I still get at every interaction this
requestBody: { files: undefined, json: { type: 5, data: [Object] } },
rawError: { message: 'Unknown interaction', code: 10062 },
code: 10062,
status: 404,
method: 'POST',
url: 'https://discord.com/api/v10/interactions/1342903578414682262/aW50ZXJhY3Rpb246MTM0MjkwMzU3ODQxNDY4MjI2MjpNaFJ4NTVKN28zaHZocEpVVHRnUGN2aDFiWG5RUHQ4T3FHbTdiZXY0d3RHcHFXUEk2cW1FWUdDMEM0UU9NZFpteEVudHlXTjNSeHVTazFvMnZ5RjlFQlhhY1o2Y3hLU0tIN0h3OUVWTUppT3NkNlZMOUtrRUxrTDZqRU9sNkh6TQ/callback?with_response=false'
}
requestBody: { files: undefined, json: { type: 5, data: [Object] } },
rawError: { message: 'Unknown interaction', code: 10062 },
code: 10062,
status: 404,
method: 'POST',
url: 'https://discord.com/api/v10/interactions/1342903578414682262/aW50ZXJhY3Rpb246MTM0MjkwMzU3ODQxNDY4MjI2MjpNaFJ4NTVKN28zaHZocEpVVHRnUGN2aDFiWG5RUHQ4T3FHbTdiZXY0d3RHcHFXUEk2cW1FWUdDMEM0UU9NZFpteEVudHlXTjNSeHVTazFvMnZ5RjlFQlhhY1o2Y3hLU0tIN0h3OUVWTUppT3NkNlZMOUtrRUxrTDZqRU9sNkh6TQ/callback?with_response=false'
}
vladdy
vladdy•2mo ago
that usually means the response is slow, which is weird
Zue
ZueOP•2mo ago
Zue
ZueOP•2mo ago
this is the whole log I am thinking but perhaps interactionCreate.js is causing this issue? 🤷
vladdy
vladdy•2mo ago
idk what that is
Zue
ZueOP•2mo ago
const { Listener, Args } = require('@sapphire/framework');
const { Interaction } = require('discord.js');

const { time, red } = require('../utils/Common.js');

class InteractionCreate extends Listener {
/**
* @param {Listener.Context} context
* @param {Listener.Options} options
*/
constructor(context, options) {
super(context, {
...options,
event: 'interactionCreate'
})
}

/**
* @param {Interaction} interaction
* @param {Args} args
*/
async run(interaction, args) {
const client = this.container;
try {
if(!interaction.isChatInputCommand() && !interaction.deferred)
await interaction.deferReply({ flags: 0 });

const command = client.stores.get('commands').get(interaction.commandName);
if(!command) return interaction.followUp({ content: 'An error has occured!' });

// OLD CODE: This is for handling args when the slash command has options
/*
interaction.options.array().map(context => {
if(context.name) args.push(context.name);
if(context.value) args.push(context.value);
})
*/

await command.chatInputRun(interaction, args);
} catch(err) {
console.log(time(), red('interactionCreate'), err.message);
}
}
}

module.exports = InteractionCreate;
const { Listener, Args } = require('@sapphire/framework');
const { Interaction } = require('discord.js');

const { time, red } = require('../utils/Common.js');

class InteractionCreate extends Listener {
/**
* @param {Listener.Context} context
* @param {Listener.Options} options
*/
constructor(context, options) {
super(context, {
...options,
event: 'interactionCreate'
})
}

/**
* @param {Interaction} interaction
* @param {Args} args
*/
async run(interaction, args) {
const client = this.container;
try {
if(!interaction.isChatInputCommand() && !interaction.deferred)
await interaction.deferReply({ flags: 0 });

const command = client.stores.get('commands').get(interaction.commandName);
if(!command) return interaction.followUp({ content: 'An error has occured!' });

// OLD CODE: This is for handling args when the slash command has options
/*
interaction.options.array().map(context => {
if(context.name) args.push(context.name);
if(context.value) args.push(context.value);
})
*/

await command.chatInputRun(interaction, args);
} catch(err) {
console.log(time(), red('interactionCreate'), err.message);
}
}
}

module.exports = InteractionCreate;
It's this
vladdy
vladdy•2mo ago
why why why
Zue
ZueOP•2mo ago
xD
vladdy
vladdy•2mo ago
are you trying to do what sapphire handles for you
Zue
ZueOP•2mo ago
😭 I don't remember what I used this for but Ig I don't need it
vladdy
vladdy•2mo ago
disable it and try again
Zue
ZueOP•2mo ago
yep this was the cause I removed it entirely I come from a version of sapphire where I had to write allat 😭
vladdy
vladdy•2mo ago
jeez how old of a version
Zue
ZueOP•2mo ago
god if I remember it's years ago
vladdy
vladdy•2mo ago
pre slash commands in general?
Zue
ZueOP•2mo ago
before slash command exist
vladdy
vladdy•2mo ago
that explains XD
vladdy
vladdy•2mo ago
yeah sapphire handles alllllll of this
Zue
ZueOP•2mo ago
btw I dm'd u earlier Ig u might not check ur dms often
vladdy
vladdy•2mo ago
oh i have message requests LOL
Zue
ZueOP•2mo ago
yo sorry to bother again does sapphire provide a way to handle options, suboptions, choices & rest?
vladdy
vladdy•2mo ago
wdym In slash commands its only what discord provides
Zue
ZueOP•2mo ago
const { Command } = require('@sapphire/framework');

const { cyan } = require('colors');
const { time, logs } = require('../utils/Common.js');

class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options
});
this.opts = options.options;
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand(builder => {
logs.slash
? console.log(time(), cyan('SLASH_COMMAND'), this.name)
: null;

builder
.setName(this.name)
.setDescription(this.description);

const args = field => (option) => {
return option
.setName(field.name)
.setDescription(field.description);
};

if(this.opts) this.opts.forEach(field => {
if(field.type === 'STRING') builder.addStringOption(args(field));
if(field.type === 'INTEGER') builder.addIntegerOption(args(field));
if(field.type === 'BOOLEAN') builder.addBooleanOption(args(field));
if(field.type === 'USER') builder.addUserOption(args(field));
if(field.type === 'ROLE') builder.addRoleOption(args(field));
if(field.type === 'MENTIONABLE') builder.addMentionableOption(args(field));
if(field.type === 'CHANNEL') builder.addChannelOption(args(field));
})

return builder;
});
}
}

module.exports = SlashCommand;
const { Command } = require('@sapphire/framework');

const { cyan } = require('colors');
const { time, logs } = require('../utils/Common.js');

class SlashCommand extends Command {
constructor(context, options) {
super(context, {
...options
});
this.opts = options.options;
}

registerApplicationCommands(registry) {
registry.registerChatInputCommand(builder => {
logs.slash
? console.log(time(), cyan('SLASH_COMMAND'), this.name)
: null;

builder
.setName(this.name)
.setDescription(this.description);

const args = field => (option) => {
return option
.setName(field.name)
.setDescription(field.description);
};

if(this.opts) this.opts.forEach(field => {
if(field.type === 'STRING') builder.addStringOption(args(field));
if(field.type === 'INTEGER') builder.addIntegerOption(args(field));
if(field.type === 'BOOLEAN') builder.addBooleanOption(args(field));
if(field.type === 'USER') builder.addUserOption(args(field));
if(field.type === 'ROLE') builder.addRoleOption(args(field));
if(field.type === 'MENTIONABLE') builder.addMentionableOption(args(field));
if(field.type === 'CHANNEL') builder.addChannelOption(args(field));
})

return builder;
});
}
}

module.exports = SlashCommand;
as u can see
vladdy
vladdy•2mo ago
For subcommands we have a plugin to make handling it easier But otherwise no
Zue
ZueOP•2mo ago
with my method I can register up to 1 options, I could spam this to check for more suboptions, choices or menus but frankly I think that what I'm ab to do is bad practice what I need is for suboptions
vladdy
vladdy•2mo ago
So you mean you want subcommands and choices?
Zue
ZueOP•2mo ago
I want to handle those situations for future cases
vladdy
vladdy•2mo ago
I'd suggest reading up on Discord's system first, then looking into the builders methods for it But this is why we have a registerApplicationCommands function per command You'd probably be better off replacing the register methods on classes that need options
Zue
ZueOP•2mo ago
hm I'll see what I can do on my end

Did you find this page helpful?