So i tried following the https://discordjs.guide/additional-info/rest-api.html guide before making my own. But I can’t get either to work.
Firstly with /cat it crashes and the console returns with:
SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) at getJSONResponse (BOTLOCATIONindex.js:77:14) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async Client.<anonymous> (BOTLOCATIONindex.js:90:20)
And /urban works but no matter what term I enter it returns with NULL.
Here is the code, its nearly identical from the guides apart from the added SlashCommandBuilder and REST.
const { request } = require('undici'); const clientId = 'CLIENTID_HERE'; const guildId = 'GUILDID_HERE'; const { SlashCommandBuilder } = require('@discordjs/builders'); const { REST } = require('@discordjs/rest'); const { Routes } = require('discord-api-types/v9'); const commands = [ new SlashCommandBuilder().setName('cat').setDescription('Cat thing idk'), new SlashCommandBuilder().setName('urban').setDescription('Urban Dictionary Thing'), ] .map(command => command.toJSON()); const rest = new REST({ version: '9' }).setToken("TOKEN_HERE"); rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands }) //rest.put(Routes.applicationGuildCommands(clientId), { body: commands }) .then(() => console.log('Successfully registered application commands.')) .catch(console.error); const trim = (str, max) => (str.length > max ? `${str.slice(0, max - 3)}...` : str); async function getJSONResponse(body) { let fullBody = ''; for await (const data of body) { fullBody += data.toString(); } return JSON.parse(fullBody); } client.on('interactionCreate', async interaction => { if (!interaction.isCommand()) return; const { commandName } = interaction; await interaction.deferReply(); if (commandName === 'cat') { const catResult = await request('https://aws.random.cat/meow'); const { file } = await getJSONResponse(catResult.body); interaction.reply({ files: [{ attachment: file, name: 'cat.png' }] }); } else if (commandName === 'urban') { const term = interaction.options.getString('term'); const query = new URLSearchParams({ term }); const dictResult = await request(`https://api.urbandictionary.com/v0/define?${query}`); const { list } = await getJSONResponse(dictResult.body); if (!list.length) { return interaction.editReply(`No results found for **${term}**.`); } const [answer] = list; const embed = new MessageEmbed() .setColor('#EFFF00') .setTitle(answer.word) .setURL(answer.permalink) .addFields( { name: 'Definition', value: trim(answer.definition, 1024) }, { name: 'Example', value: trim(answer.example, 1024) }, { name: 'Rating', value: `${answer.thumbs_up} thumbs up. ${answer.thumbs_down} thumbs down.`, }, ); interaction.editReply({ embeds: }); } });
Advertisement
Answer
So for the cat command since there is a deferReply first we need to use editReply since deferReply counts as the first/initial reply.
await interaction.deferReply(); const catResult = await request('https://aws.random.cat/meow').catch((err) => { console.log(err); });; const { file } = await getJSONResponse(catResult.body).catch((err) => { console.log(err); }); return await interaction.editReply({ files: [{ attachment: file, name: 'cat.png' }] });
I also added a .catch
to the end of each await, this was just for testing however I recommend it.
Now with the urban command, the reason it is using null is since you don’t have the string option’s text. We can check for it by adding an if statement.
await interaction.deferReply(); const term = interaction.options.getString('term'); if (!term) return await interaction.editReply('Please provide a term.'); // We need to add this check to see if the user provided the term option or not. const query = new URLSearchParams({ term }); const dictResult = await request(`https://api.urbandictionary.com/v0/define?${query}`); const { list } = await getJSONResponse(dictResult.body); if (!list.length) { return interaction.editReply(`No results found for **${term}**.`); } const [answer] = list; const embed = new MessageEmbed() .setColor('#EFFF00') .setTitle(answer.word) .setURL(answer.permalink) .addFields( { name: 'Definition', value: trim(answer.definition, 1024) }, { name: 'Example', value: trim(answer.example, 1024) }, { name: 'Rating', value: `${answer.thumbs_up} thumbs up. ${answer.thumbs_down} thumbs down.`, }, ); return await interaction.editReply({ embeds: });
IMPORTANT: When you are building your slash command you are not setting a string option. In the commands
array, when creating the second slash command called urban
we will add the support for the string option there. (An example using the string option, discord.js guide all command options)
This is how we can do this:
const commands = [ new SlashCommandBuilder().setName('cat') .setDescription('Cat thing idk'), new SlashCommandBuilder() .setName('urban') .setDescription('Urban Dictionary Thing') .addStringOption((option) => option.setName('term').setDescription('term')) // We first add the string option then set the name to 'term' which is what the code calls for and then the description. ].map((command) => command.toJSON());
If you would like to make the term input required, add .setRequired(true)
which will not allow the command to be ran without entering the term to search.
Once you do that you should be all good! Tested the code and it’s working once that’s fixed