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ā€¢3w 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ā€¢3w 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ā€¢3w 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ā€¢3w 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ā€¢3w 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ā€¢3w 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ā€¢3w ago
does sapphire handle the deregistration of unexistent commands by itself?
vladdy
vladdyā€¢3w 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ā€¢3w ago
so is it fine if I do registry.getDefaultBehaviorWhenNotIdentical(RegisterBehavior.BulkOverwrite); in applicationCommandRegistriesInitialising eventlistener
vladdy
vladdyā€¢3w ago
Bulkoverwrite, and you call it before login And not get ApplicationCommandRegistries.setDefaultBehaviorWhenNotIdentical
Zue
ZueOPā€¢3w ago
I see where my mistake was thanks
vladdy
vladdyā€¢3w ago
which applies globally :salute:
Zue
ZueOPā€¢3w 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ā€¢3w ago
So then also call ApplicationCommandRegistries.setDefaultGuildIds before login too
Zue
ZueOPā€¢3w ago
ye xD before login huh
vladdy
vladdyā€¢3w ago
ya
Zue
ZueOPā€¢3w ago
I'll import them as handlers before login then
Zue
ZueOPā€¢3w ago
thanks for the support
vladdy
vladdyā€¢3w ago
You dont need it to be a handler tbh
Zue
ZueOPā€¢3w 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ā€¢3w ago
mhm
Zue
ZueOPā€¢3w ago
I knew it had some automation but hell I read docs and I was this clueless šŸ˜­
vladdy
vladdyā€¢3w 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ā€¢3w 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ā€¢3w ago
it does exist show code Not registry
Zue
ZueOPā€¢3w ago
I know it does exist
vladdy
vladdyā€¢3w ago
Registries
vladdy
vladdyā€¢3w ago
not that one
Zue
ZueOPā€¢3w ago
oh
vladdy
vladdyā€¢3w ago
ApplicationCommandRegistries Registry is an instance per command
Zue
ZueOPā€¢3w 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ā€¢3w ago
its not an hour
Zue
ZueOPā€¢3w ago
second is to find out why Preconditions does not want to work
vladdy
vladdyā€¢3w ago
just refresh your client
Zue
ZueOPā€¢3w ago
restart the bot or wym
vladdy
vladdyā€¢3w ago
no, ctrl r in discord
Zue
ZueOPā€¢3w ago
oh I did that but still dupe earlier I'll try quitting discord
vladdy
vladdyā€¢3w ago
and do both run?
Zue
ZueOPā€¢3w ago
obv not p sure the one that doesn't run is the global command
vladdy
vladdyā€¢3w ago
check command ids i guess
Zue
ZueOPā€¢3w ago
this way?
vladdy
vladdyā€¢3w ago
naw well
Zue
ZueOPā€¢3w ago
lol
vladdy
vladdyā€¢3w ago
log command.commandRegistry or i think applicationCommandRegistry
Zue
ZueOPā€¢3w ago
ye applicationCommandRegistry
vladdy
vladdyā€¢3w ago
yee, that should show ids and all
Zue
ZueOPā€¢3w ago
nope, it's only a batch no duplicates
vladdy
vladdyā€¢3w ago
hmm?
Zue
ZueOPā€¢3w ago
it must be my discord then
vladdy
vladdyā€¢3w ago
screenshot pls
Zue
ZueOPā€¢3w 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ā€¢3w ago
..empty? that..is not right
Zue
ZueOPā€¢3w ago
cus I'm console logging this before they're being registered that's why it is empty
vladdy
vladdyā€¢3w ago
OH
Zue
ZueOPā€¢3w 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ā€¢3w ago
yay its on the client showing it wrong thank lord LMAO
Zue
ZueOPā€¢3w ago
wait what šŸ˜­ that's what I said give me 2 min I quit discord
vladdy
vladdyā€¢3w ago
yeah no sapphire handled it right, only shows the guild one, i was worried smth was fucking up XD
Zue
ZueOPā€¢3w 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ā€¢3w 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ā€¢3w 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ā€¢3w ago
show the preconditions
Zue
ZueOPā€¢3w 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ā€¢3w ago
oh duh yeah that will never work its chatInputRun for slash cmds
Zue
ZueOPā€¢3w ago
shrug run should handle it the same way
vladdy
vladdyā€¢3w ago
messageRun and contextMenuRun respectively
Zue
ZueOPā€¢3w ago
no?
vladdy
vladdyā€¢3w ago
Run doesn't exist in sapphire anymore for Preconditions and Commands
Zue
ZueOPā€¢3w ago
cus I also use run for commands and rest of the eventlisteners then why does my thing work šŸ˜­ wtf
vladdy
vladdyā€¢3w 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ā€¢3w ago
uh
Zue
ZueOPā€¢3w ago
I thought this was a client issue šŸ’€
vladdy
vladdyā€¢3w ago
:Confusion: What sapphire version are you on
Zue
ZueOPā€¢3w 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ā€¢3w 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ā€¢3w 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ā€¢3w ago
naw that looks fine
Zue
ZueOPā€¢3w ago
oh ok
vladdy
vladdyā€¢3w ago
Can you show your main file
Zue
ZueOPā€¢3w 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ā€¢3w ago
and in bootstrap?
Zue
ZueOPā€¢3w 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ā€¢3w ago
loadOverwrite is uh Invalid :KEKW:
Zue
ZueOPā€¢3w ago
šŸ¦• no way If I called it as a function it'd return undefined what
vladdy
vladdyā€¢3w ago
Registry.setDefaultBehaviorWhenNotIdentical(Behavior.BulkOverwrite) also drop async from that function, you dont need it and it may prevent race conditions XD
Zue
ZueOPā€¢3w ago
I love async cus it creates me more problems to worry about šŸ’€
vladdy
vladdyā€¢3w ago
not if you have eslint/ts yell for promises not being handled
Zue
ZueOPā€¢3w ago
you've no idea how many times some devs raped me to always use it šŸ˜­
vladdy
vladdyā€¢3w ago
async await is fine! just gotta be careful with it
Zue
ZueOPā€¢3w ago
EYYY btw one thing this
vladdy
vladdyā€¢3w ago
Those log messages cannot be turned off for bulk, woops LOL
Zue
ZueOPā€¢3w ago
I'm gonna commit šŸ¦• anyway let me see if commands now dupe
vladdy
vladdyā€¢3w ago
Make an issue for it, we can make it an event Like for normal registrations
Zue
ZueOPā€¢3w 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ā€¢3w ago
Use or make a logger and done
Zue
ZueOPā€¢3w ago
a logger to do what It wont prevent it from logging
vladdy
vladdyā€¢3w ago
to override the default one
Zue
ZueOPā€¢3w ago
oh well
vladdy
vladdyā€¢3w ago
then you can also ignore specific messages BUT
Zue
ZueOPā€¢3w ago
I don't know to use those
vladdy
vladdyā€¢3w ago
.
Zue
ZueOPā€¢3w ago
I did see it on ur docs tho on github? sure
vladdy
vladdyā€¢3w ago
Yeah, bulk was added after that setting :OMEGALUL:, and we always wanted to print something so people knew that smth happened
Zue
ZueOPā€¢3w ago
too much to write for a feature request šŸ’€
vladdy
vladdyā€¢3w ago
Just link this thread Give it a good title and link this thread Its fine XD
Zue
ZueOPā€¢3w ago
request: Add ApplicationCommandRegistries Event Listener šŸ¤·ā€ā™‚ļø
vladdy
vladdyā€¢3w ago
BulkOverwrite event listener but yes
Zue
ZueOPā€¢3w ago
why not just create a generic event that listens to every subevent of that type of event
vladdy
vladdyā€¢3w ago
bc this was added later :OMEGALUL:
Zue
ZueOPā€¢3w ago
šŸ˜­
vladdy
vladdyā€¢3w ago
But yeah just make the issue, I already know how to handle this
Zue
ZueOPā€¢3w ago
:kekGasm: BulkOverwriteError before BulkOverwrite
vladdy
vladdyā€¢3w ago
yeah we only have an event for error, not info :OMEGALUL:
Zue
ZueOPā€¢3w ago
I mean It works out
vladdy
vladdyā€¢3w ago
hey but it works now :thanksForFlying:
Zue
ZueOPā€¢3w 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ā€¢3w ago
yeah we have error events ListenerError, CommandError (split by type of commands), etc
Zue
ZueOPā€¢3w ago
the issue with precondition still persists
Zue
ZueOPā€¢3w ago
even after I replaced run with chatInputRun
vladdy
vladdyā€¢3w ago
mmm does it get called? and whats the file name of the precondition
Zue
ZueOPā€¢3w 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ā€¢3w ago
yes if you dont pass in a constructor with name: "", it takes by the file name and case sensitive
Zue
ZueOPā€¢3w 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ā€¢3w ago
wdym
Zue
ZueOPā€¢3w 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ā€¢3w ago
uhhhhhh is that this.options? but i think its intended, that idk about LMAO
Zue
ZueOPā€¢3w 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ā€¢3w ago
i think its intended
Zue
ZueOPā€¢3w ago
we already got name & description on command itself šŸ¤·ā€ā™‚ļø
vladdy
vladdyā€¢3w ago
Just check when it was added and why but iirc it was intended for smth
vladdy
vladdyā€¢3w ago
uh
Zue
ZueOPā€¢3w ago
well whatever
vladdy
vladdyā€¢3w ago
oh the core piece stores the options
vladdy
vladdyā€¢3w ago
Yeah this was intended, but you can extend the cmd class to store your commandOptions in a this.field
Zue
ZueOPā€¢3w ago
this.options.options If anyone asks me, I'll tell them which framework I'm using :kekw:
vladdy
vladdyā€¢3w ago
just store this.commandOptions = options.options in your constructor
Zue
ZueOPā€¢3w 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ā€¢3w 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ā€¢3w ago
It's not the precondition
vladdy
vladdyā€¢3w ago
its a command denied event you'll need to share code! :Prayge:
Zue
ZueOPā€¢3w ago
yes
vladdy
vladdyā€¢3w ago
otherwise cant help
Zue
ZueOPā€¢3w 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ā€¢3w ago
and is your chatInputRun called?
Zue
ZueOPā€¢3w 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ā€¢3w ago
that usually means the response is slow, which is weird
Zue
ZueOPā€¢3w ago
Zue
ZueOPā€¢3w ago
this is the whole log I am thinking but perhaps interactionCreate.js is causing this issue? šŸ¤·
vladdy
vladdyā€¢3w ago
idk what that is
Zue
ZueOPā€¢3w 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ā€¢3w ago
why why why
Zue
ZueOPā€¢3w ago
xD
vladdy
vladdyā€¢3w ago
are you trying to do what sapphire handles for you
Zue
ZueOPā€¢3w ago
šŸ˜­ I don't remember what I used this for but Ig I don't need it
vladdy
vladdyā€¢3w ago
disable it and try again
Zue
ZueOPā€¢3w 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ā€¢3w ago
jeez how old of a version
Zue
ZueOPā€¢3w ago
god if I remember it's years ago
vladdy
vladdyā€¢3w ago
pre slash commands in general?
Zue
ZueOPā€¢3w ago
before slash command exist
vladdy
vladdyā€¢3w ago
that explains XD
vladdy
vladdyā€¢3w ago
yeah sapphire handles alllllll of this
Zue
ZueOPā€¢3w ago
btw I dm'd u earlier Ig u might not check ur dms often
vladdy
vladdyā€¢3w ago
oh i have message requests LOL
Zue
ZueOPā€¢3w ago
yo sorry to bother again does sapphire provide a way to handle options, suboptions, choices & rest?
vladdy
vladdyā€¢3w ago
wdym In slash commands its only what discord provides
Zue
ZueOPā€¢3w 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ā€¢3w ago
For subcommands we have a plugin to make handling it easier But otherwise no
Zue
ZueOPā€¢3w 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ā€¢3w ago
So you mean you want subcommands and choices?
Zue
ZueOPā€¢3w ago
I want to handle those situations for future cases
vladdy
vladdyā€¢3w 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ā€¢3w ago
hm I'll see what I can do on my end

Did you find this page helpful?