Since my bots new update is releasing soon I have been trying to figure out how to make the slash commands go global. I keep hitting bumps and I have no idea what to do next. Below is some of my code of creating the commands.
ready.js
const {prefix} = require("../../botconfig.json")
const {createCmd} = require("../../dataHandler.js")
module.exports = bot => {
console.log(`${bot.user.username} is online`);
setInterval(() => bot.user.setActivity(`!help | Switch Support`, { type: "WATCHING" }), 15000)
createCmd(bot, '781631298749726730') //second param is guildId
};
DataHandler(Creates the data for the comamnds)
async function createCmd(Client, guildId) {
const data = [
//ping cmd
{
name: 'test',
description: 'test slash command'
}
]
await Client.guilds.cache.get(guildId).commands.set(data)
}
module.exports = {createCmd}
How will I use these to make the commands global so every server can use these commands when the update is released.
See the documentation here. All the code below is taken from there.
In your createCmd function add
const rest = new REST({ version: '9' }).setToken("your discord token");
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationCommands("REPLACE_WITH_CLIENT_ID"),
{ body: data },
);
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
Require these at the beginning of your file
const { REST } = require('#discordjs/rest');
const { Routes } = require('discord-api-types/v9');
Just a note: Global commands take up to an hour to refresh across Discord. Keep that in mind when updating.
Related
After calling the refresh token endpoint to refresh the user's auth tokens, the local storage does not update the token field consistently. Sometimes, the local storage is updated properly and the app works well, other times the token and admin/student fields are deleted from local storage despite no error being logged and the endpoint returning a success response. How do I fix this? Code below
import { parseTokens, parseAdmin, parseUser } from "../utils/auth-parser";
import { adminAuthFetch } from "../config/axios/axios-admin.config";
import { studentAuthFetch } from "../config/axios/axios-user.config";
export const refresher = async () => {
const admin = parseAdmin();
const student = parseUser();
const token = parseTokens();
if (!admin && !student) {
return;
}
if (admin && !student) {
console.log(
"==========================refreshing token==================",
new Date().getMilliseconds()
);
try {
const response = await adminAuthFetch.post(`/auth/refresh-tokens`, {
refresh_token: token.refresh,
});
const data = response?.data;
console.log(data);
localStorage.setItem(
"tokens",
JSON.stringify({
access: data?.data?.auth_token,
refresh: data?.data?.refresh_token,
})
);
} catch (error) {
console.log(error);
localStorage.removeItem("tokens");
localStorage.removeItem("admin");
}
} else if (student && !admin) {
console.log(
"==========================refreshing student token==================",
new Date().getMilliseconds()
);
try {
const response = await studentAuthFetch.post(`/auth/refresh-tokens`, {
refresh_token: token.refresh,
});
const data = response?.data;
console.log(data);
localStorage.setItem(
"tokens",
JSON.stringify({
access: data?.data?.auth_token,
refresh: data?.data?.refresh_token,
})
);
} catch (error) {
console.log(error)
localStorage.removeItem("tokens");
localStorage.removeItem("student");
}
}
};
Here's the Effect that is called from the root app
const refreshFunction = () => {
if (!refreshRef.current) {
refreshRef.current = true;
refresher();
} else {
refreshRef.current = false;
}
};
useEffect(() => {
const timer = setInterval(refreshFunction, 1000 * 60 * 2);
return () => clearInterval(timer);
}, []);
Despite receiving a success response from the endpoint and ensuring refresher function is called only once with the useref check, the token field in the local storage doesn't update consistently. Sometimes the values are updated, sometimes they are deleted without an error being logged to the console. Tried removing strict mode but it still does not work
Without being certain about how everything in your code works, it's possible that despite your best intentions, the refresher function is rendering twice.
Could you share more context around the React version you're using? If you're using version 17, try doing something like this:
let log = console.log
at the top level of your code, and use it for logging instead. My working theory is that some form of console.log suppression is happening on a second render, which is why you're not getting the logs, even though the localStorage removeItem call is still executing.
Let me know the React version, and we can continue debugging.
I am following the docs at DiscrodJS step by step and have been able to invite my bot just fine to my test server. My problem comes in when I try to add the slash commands.
Here is my index.js file.
const { Client, GatewayIntentBits } = require('discord.js')
require('dotenv').config()
const client = new Client({
intents: [
GatewayIntentBits.GuildMessages,
],
})
client.once('ready', () => {
console.log('Ready!')
})
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return
const { commandName } = interaction
const command = interaction.client.commands.get(commandName)
if (!command) {
console.error(`No command matching ${commandName} was found.`)
return
}
try {
await command.execute(interaction)
}
catch (error) {
console.error(error)
await interaction.reply({ ontent: 'There was an error while executing this command', ephemeral: true })
}
})
client.login(process.env.DISCORD_PRIVATE_TOKEN)
Here is my deploy-commands.js file.
const fs = require('node:fs')
const { Routes } = require('discord.js')
const { REST } = require('#discordjs/rest')
require('dotenv').config()
const {
DISCORD_PRIVATE_TOKEN,
DISCORD_CLIENT_ID,
DISCORD_GUILD_ID,
} = process.env
const commands = []
// Grab all the command files from the commands directory you created earlier
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'))
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const command = require(`./commands/${file}`)
commands.push(command.data.toJSON())
}
// Construct and prepare an instance of the REST module
const rest = new REST({ version: '10' }).setToken(DISCORD_PRIVATE_TOKEN)
// and deploy your commands!
const deployCommand = async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`)
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands(DISCORD_CLIENT_ID, DISCORD_GUILD_ID),
{ body: commands },
)
console.log(`Successfully reloaded ${data.length} application (/) commands.`)
}
catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error)
}
}
deployCommand()
There error that I get back from this is the following:
requestBody: { files: undefined, json: [ [Object] ] },
rawError: { message: 'Missing Access', code: 50001 },
code: 50001,
status: 403,
method: 'PUT',
url: 'https://discord.com/api/v10/applications/[client_id]/guilds/[guild_id]/commands'
}
In the bot settings
I've set auth method to In-app Authorization.
I've set the scope to bot and applications.commands then included Use Slash Commands (also tried this with ALL including Administrator bot permissions.
I've also done this through the URL Generator path.
In my discord server
I've put my Discord server into Developer mode to test my bot.
I've invited my bot to my test server.
In my code
I've ran the one time script for deploy-commands.js.
I am trying out discord.js version 13.1.0 and I am following discord's guide on how to setup a bot. I have followed all the steps. When I have the bot running, and I send a message or a command into the server, nothing gets printed to the console. Any ideas?
My code:
const { Client, Intents } = require("discord.js");
const client = new Client({ intents: [Intents.FLAGS.GUILDS] });
client.once("ready", () => {
console.log("Ready!");
});
client.on("interactionCreate", (interaction) => {
console.log(interaction);
});
client.login(process.env.TOKEN);
From the Discord Developer Portal:
An Interaction is the message that your application receives when a user uses an application command or a message component.
By an application command is meant a special type of command, that you have to register against the Discord API. To learn more about application commands, read this.
From the discord.js docs main page, how to register an application (slash) command:
const { REST } = require('#discordjs/rest');
const { Routes } = require('discord-api-types/v9');
const commands = [{
name: 'ping',
description: 'Replies with Pong!'
}];
const rest = new REST({ version: '9' }).setToken('token');
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID),
{ body: commands },
);
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
})();
However if you are looking for a way to reply to (or just capture) a normal message sent in a TextChannel, you are probably looking for the messageCreate event.
client.on("messageCreate", (message) => {
console.log(message);
});
Also note that to receive messageCreate event, you need GUILD_MESSAGES intent.
const client = new Discord.Client({ intents: ["GUILDS", "GUILD_MESSAGES"] });
I'm creating a global function that checks whether the jwt token is expired or not.
I call this function if I'm fetching data from the api to confirm the user but I'm getting the error that I cannot update during an existing state transition and I don't have a clue what it means.
I also notice the the if(Date.now() >= expiredTime) was the one whose causing the problem
const AuthConfig = () => {
const history = useHistory();
let token = JSON.parse(localStorage.getItem("user"))["token"];
if (token) {
let { exp } = jwt_decode(token);
let expiredTime = exp * 1000 - 60000;
if (Date.now() >= expiredTime) {
localStorage.removeItem("user");
history.push("/login");
} else {
return {
headers: {
Authorization: `Bearer ${token}`,
},
};
}
}
};
I'm not sure if its correct but I call the function like this, since if jwt token is expired it redirect to the login page.
const config = AuthConfig()
const productData = async () => {
const { data } = await axios.get("http://127.0.0.1:5000/product", config);
setProduct(data);
};
I updated this peace of code and I could login to the application but when the jwt expires and it redirect to login using history.push I till get the same error. I tried using Redirect but its a little slow and I could still navigate in privateroutes before redirecting me to login
// old
let expiredTime = exp * 1000 - 60000;
if (Date.now() >= expiredTime)
// change
if (exp < Date.now() / 1000)
i would start from the beginning telling you that if this is a project that is going to production you always must put the auth token check in the backend especially if we talk about jwt authentication.
Otherwise if you have the strict necessity to put it in the React component i would suggest you to handle this with Promises doing something like this:
const config = Promise.all(AuthConfig()).then(()=> productData());
I would even consider to change the productData function to check if the data variable is not null before saving the state that is the reason why the compiler is giving you that error.
const productData = async () => {
const { data } = await axios.get("http://127.0.0.1:5000/product", config);
data && setProduct(data);
};
Finally consider putting this in the backend. Open another question if you need help on the backend too, i'll be glad to help you.
Have a nice day!
I'm still not sure how your code is used within a component context.
Currently your API and setProduct are called regardless whether AuthConfig() returns any value. During this time, you are also calling history.push(), which may be the reason why you encountered the error.
I can recommend you to check config for value before you try to call the API.
const config = AuthConfig()
if (config) {
const productData = async () => {
const { data } = await axios.get("http://127.0.0.1:5000/product", config);
setProduct(data);
};
}
I'm assuming that AuthConfig is a hook, since it contains a hook. And that it's consumed in a React component.
Raise the responsibility of redirecting to the consumer and try to express your logic as effects of their dependencies.
const useAuthConfig = ({ onExpire }) => {
let token = JSON.parse(localStorage.getItem("user"))["token"];
const [isExpired, setIsExpired] = useState(!token);
// Only callback once, when the expired flag turns on
useEffect(() => {
if (isExpired) onExpire();
}, [isExpired]);
// Check the token every render
// This doesn't really make sense cause the expired state
// will only update when the parent happens to update (which
// is arbitrary) but w/e
if (token) {
let { exp } = jwt_decode(token);
let expiredTime = exp * 1000 - 60000;
if (Date.now() >= expiredTime) {
setIsExpired(true);
return null;
}
}
// Don't make a new reference to this object every time
const header = useMemo(() => !isExpired
? ({
headers: {
Authorization: `Bearer ${token}`,
},
})
: null, [isExpired, token]);
return header;
};
const Parent = () => {
const history = useHistory();
// Let the caller decide what to do on expiry,
// and let useAuthConfig just worry about the auth config
const config = useAuthConfig({
onExpire: () => {
localStorage.removeItem("user");
history.push("/login");
}
});
const productData = async (config) => {
const { data } = await axios.get("http://127.0.0.1:5000/product", config);
setProduct(data);
};
useEffect(() => {
if (config) {
productData(config);
}
}, [config]);
};
I am trying to get a Discord.js bot to grab images from Reddit and post them in a channel but It keeps saying that there is no content to send, I was wondering if someone could point out what I did wrong.
(I am using a command handler)
Code:
const randomPuppy = require('random-puppy');
const snekfetch = require('snekfetch');
module.exports = {
name: "reddit",
category: "info",
description: "Sends subreddit images",
run: async (client, Message, args, subreddit) => {
let reddit = [
"dankmemes",
"meme"
]
randomPuppy(subreddit).then(url => {
snekfetch.get(url).then(async res => {
await Message.channel.send({
file: [{
attachment: res.body,
name: 'image.png'
}]
});
}).catch(err => console.error(err));
});
}
}
When I tried using random-puppy to get images from r/dankmemes, a lot of them were GIFs or videos. Make sure you're using the right extension and increasing the restRequestTimeout to allow time for the file to send.
The property file in Discord.js' MessageOptions was deprecated in v11 and was removed in v12. You should use files instead.
snekfetch is deprecated and you should use node-fetch instead.
Use this to initialise your client which will set the timeout to 1 minute instead of the default 15 seconds:
const client = new Client({restRequestTimeout: 60000})
In your command file:
// returns .jpg, .png, .gif, .mp4 etc
// for more info see https://nodejs.org/api/path.html#path_path_extname_path
const {extname} = require('path')
const fetch = require('node-fetch')
/* ... */
// in your run function
// you can use thens if you want but I find this easier to read
try {
const url = await randomPuppy(subreddit)
const res = await fetch(url)
await Message.channel.send({
files: [{
attachment: res.body,
name: `image${extname(url)}`
}]
})
} catch (err) {
console.error(err)
}