Make a dog command in Discord.JS - javascript

I'm trying to figure out how to make a command that makes a GET request to this dog API and return the image in an embed. Here's the code I've tried:
const Discord = require('discord.js');
const fetch = require('node-fetch');
module.exports = {
name: 'afv!dog',
description: 'Grab a cute doggo from dog.ceo',
execute(msg, args, bot) {
const prevmsg = msg
const fetchEmbed = new Discord.MessageEmbed()
.setColor('#e3dcd3')
.setTitle(':dog: Woof! Let me find you a doggo! <a:AFVloading:748218375909539923>')
.setDescription("This shouldn't take long...")
msg.reply(fetchEmbed).then(msg => {
const { message } = await fetch('https://dog.ceo/api/breeds/image/random').then(response => response.text());
console.log(message)
const doneEmbed = new Discord.MessageEmbed()
.setColor('#e3dcd3')
.setTitle(':dog: Woof! Found one!')
.setImage(message)
msg.delete();
prevmsg.channel.send(doneEmbed);
})
},
};
This happens when I try to run index.js:
const { message } = await fetch('https://dog.ceo/api/breeds/image/random').then(response => response.json());
^^^^^
SyntaxError: await is only valid in async function

You need to use response.json() instead of response.text(). .text() is for standard html plain text, however the dog API you are using returns an object.

You're not awaiting prevmsg.channel.send(doneEmbed);, if I see it correctly
Edit: I am wrong

Related

Passing multiple Embeds through a helper function for Discord Bot

I have refactored my code to remove some redundancy, part of which was making helper functions for the generation of embeds that my bot sends as a reply. For one of the slash commands, the bot is supposed to send back multiple embeds.
mercHelper.js (Houses the helper function inside the utils folder)
const { MessageEmbed } = require('discord.js');
const { request } = require('undici');
const { MERC_URL, BASE_URL } = require('./utils');
const { mercAliases } =require('./mercAliases')
async function getMerc( mercName ){
if (mercName in mercAliases){ mercName = mercAliases[mercName] };
const { body } = await request(MERC_URL + encodeURIComponent(mercName));
const { mercs } = await body.json();
const [mercAnswer] = mercs;
const mercEmbed = new MessageEmbed()
.setTitle(mercAnswer.name)
.setDescription('Mentor notes will go here.')
.setImage(BASE_URL + mercAnswer.screenshot)
const mercLeaderEmbed = new MessageEmbed()
.setImage(BASE_URL+'/commanders/'+ mercAnswer.commander_id+'/screenshot')
.setDescription('Name of mercenary group leader: '+ mercAnswer.bossname)
const mercTroopEmbed = new MessageEmbed()
.setImage(BASE_URL+'/commanders/'+ mercAnswer.unit_id+'/screenshot')
.setDescription('Number of units: '+ mercAnswer.nrunits)
return [mercEmbed, mercLeaderEmbed, mercTroopEmbed];
}
module.exports = { getMerc }
merc.js (slash command file)
const { SlashCommandBuilder } = require('#discordjs/builders');
const { getMerc } = require('../utils/mercHelper');
module.exports = {
data: new SlashCommandBuilder()
.setName('merc')
.setDescription('Replies with information about a merc')
.addStringOption(option => option.setName('merc_name').setDescription('Enter the name of the mercenary').setRequired(true)),
async execute(interaction) {
let mercName = interaction.options.getString('merc_name');
const mercEmbed = await getMerc( mercName );
const mercLeaderEmbed = await getMerc( mercName );
console.log('mercLeaderEmbed'+mercLeaderEmbed)
const mercTroopEmbed = await getMerc( mercName );
console.log('mercTroopEmbed'+mercTroopEmbed)
await interaction.reply({ embeds: [mercEmbed, mercLeaderEmbed, mercTroopEmbed] });
},
};
When I run the code I have currently, I get the following error message: DiscordAPIError: Invalid Form Body and then empty data.embeds.
I assume this is happening because when I 'return' embeds from the helper function, it is now going through somehow. I added console.logs to the helper file - all the information was present.
When I tried to do the same in the slash command file (merc.js), I get mercLeaderEmbed[object Object],[object Object],[object Object] and the same for the second console.log.
Maybe the return is working properly, but I am not calling the information correctly, which is resulting in the return of [object Object], but I am not sure on how to solve that issue/how to rewrite the code to avoid this.
You're calling await getMerc(mercName) three times and assign the same value to three different variables (mercEmbed, mercLeaderEmbed, and mercTroopEmbed). If you return an array of the three embeds ([mercEmbed, mercLeaderEmbed, mercTroopEmbed]) from getMerc, you don't need to call it more than once.
When you log 'mercLeaderEmbed'+mercLeaderEmbed, you receive mercLeaderEmbed[object Object],[object Object],[object Object] because mercLeaderEmbed is already an array of the three objects (and they get stringified because you concatenate the array with a string).
I think what you want is either destructuring the returned array like this:
async execute(interaction) {
let mercName = interaction.options.getString('merc_name');
let [mercEmbed, mercLeaderEmbed, mercTroopEmbed] = await getMerc(mercName);
await interaction.reply({
embeds: [mercEmbed, mercLeaderEmbed, mercTroopEmbed],
});
}
Or just sending the returned array of embeds like this:
async execute(interaction) {
let mercName = interaction.options.getString('merc_name');
let embeds = await getMerc(mercName);
await interaction.reply({ embeds });
}

Refactoring Embed message generation for Discord Bot

I am trying to refactor the code that I am using to generate embeds (user input makes an API call for info, which I display through the embed) with my bot in Discord. I have both a slash command and prefix command version of the code (so that people can either ask the bot with a prefix command or use the slash commands) and I am trying to pull the duplicated code for both those requests into a helper file.
The helper file: (/utils/itemHelper.js)
const { MessageEmbed } = require('discord.js');
const { request } = require('undici');
const { ITEM_URL, BASE_URL } = require('./utils');
const { itemAliases } =require('./itemAliases')
async function getItem( itemName ){
if (itemName in itemAliases){ itemName = itemAliases[itemName] };
const { body } = await request(ITEM_URL + encodeURIComponent(itemName));
const { items } = await body.json();
const [itemAnswer] = items;
const itemEmbed = new MessageEmbed()
.setTitle(itemAnswer.name)
.setDescription('Mentor notes will go here.')
.setImage(BASE_URL + itemAnswer.screenshot)
console.log(itemEmbed);
}
module.exports = { getItem }
At this point, the console.log(itemEmbed) gives me what I want:
MessageEmbed {
type: 'rich',
title: 'Frost Brand',
description: 'Mentor notes will go here.',
url: null,
color: null,
timestamp: null,
fields: [],
thumbnail: null,
image: { url: 'https://dom5api.illwiki.com/items/14/screenshot' },
video: null,
author: null,
provider: null,
footer: null
}
But by the time I call the function within the file were I create the slash command, I get an error message (which is discord telling me that the embed I want to send is lacking the description field):
data.embeds[0].description: This field is required.
The slash command file where I call the async function: (/commands/item.js)
const { SlashCommandBuilder } = require('#discordjs/builders');
const { MessageEmbed } = require('discord.js');
const { getItem } = require('../utils/itemHelper')
module.exports = {
data: new SlashCommandBuilder()
.setName('item')
.setDescription('Replies with information about an item')
.addStringOption(option => option.setName('item_name').setDescription('Enter the name of the item').setRequired(true)),
async execute(interaction) {
let itemName = interaction.options.getString('item_name');
const itemEmbed = getItem( itemName );
console.log(`itemEmbed:`+ itemEmbed);
await interaction.reply({ embeds: [itemEmbed] });
},
};
The console.log output is
itemEmbed:[object Promise]
My hunch is that I am somehow misunderstanding/misapplying how async functions and promises work (and that the information I need is hidden somewhere inside [object Promise]). I have tried to do 'return itemEmbed' at the end of the helper file, but that didn't solve the issue. I have also considered if I might be missing a param in my async function, but I don't think I need another param, or if I did need one what role it would fill.
Thanks for any/all help in advance!
In your getItem function, there's two problems:
You made it async. When a function is asynchronous, it is considered as a Promise that you have to use the await keyword on await getItem(..) So that it will wait for the fetch to finish to finally continue executing your code.
You don't return anything on your function. You log the embed in your console.
Try using it like so :
const { MessageEmbed } = require('discord.js');
const { request } = require('undici');
const { ITEM_URL, BASE_URL } = require('./utils');
const { itemAliases } =require('./itemAliases')
function getItem( itemName ){
if (itemName in itemAliases){ itemName = itemAliases[itemName] };
const { body } = await request(ITEM_URL + encodeURIComponent(itemName));
const { items } = await body.json();
const [itemAnswer] = items;
const itemEmbed = new MessageEmbed()
.setTitle(itemAnswer.name)
.setDescription('Mentor notes will go here.')
.setImage(BASE_URL + itemAnswer.screenshot)
return itemEmbed
}
module.exports = { getItem }
const itemEmbed = await getItem(itemName);

Discord.js Adding Emojis to a send message and grab the message id

Hey Stack Overflow Community,
I have another question in regard to discord.js.I want to send a message and add an emoji to it, from which I later want to get the list of users who have reacted. In order to do so I have 2 questions:
-How can I add an emoji? Do I need a separate event listener for a message or can I do it within my interactionCreate event? I have tried pannel.react("👍") which gives me the error: 'TypeError: pannel.react is not a function'. Does anyone know how to let this work?
-My other question is if there is a way to access the message id from the send message, in order to check who has participated later on in another command?
I have an index file with my command and event handlers. The script is from my "setup.js" command in the "commands" folder:
const { SlashCommandBuilder } = require("#discordjs/builders");
const Discord = require("discord.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("setup")
.setDescription("Setup Tweet to be rewarded")
.addChannelOption(option =>
option
.setName('destination')
.setDescription('Select a channel for the reward pannel')
.setRequired(true)
)
.addStringOption(option =>
option
.setName("twitterlink")
.setDescription("Enter the Twitter Link")
.setRequired(false)
),
async execute(interaction) {
interaction.reply({
content: "Pannel send",
ephemeral: true
}).then( function () {
const channel = interaction.options.getChannel("destination");
const channelid = channel.id;
const twitterlink = interaction.options.getString("twitterlink");
const pannel = interaction.guild.channels.cache.get(channelid).send(twitterlink);
});
}
};
Thank you very much for your assistance in advance.
Cleaned it up a bit and this should work for you
const {
SlashCommandBuilder,
} = require("#discordjs/builders");
const Discord = require("discord.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("setup")
.setDescription("Setup Tweet to be rewarded")
.addChannelOption(option =>
option
.setName('destination')
.setDescription('Select a channel for the reward pannel')
.setRequired(true),
)
.addStringOption(option =>
option
.setName("twitterlink")
.setDescription("Enter the Twitter Link")
.setRequired(false),
),
async execute(interaction) { // Fixed below here and simplified it
const channel = interaction.guild.channels.cache.get(interaction.options.getChannel("destination").id);
const twitterlink = interaction.options.getString("twitterlink");
channel.send(twitterlink).then(msg => {
msg.react('🍎'),
});
return interaction.reply({
content: "Pannel send",
ephemeral: true,
});
},
};
Okay with the help from #Gh0st I was able to find a solution:
The problem in order to send a message is that the .get() function need the channel id. I have accessd it to interaction.options.getChannel("destination").id);.
I couldnt add a reaction because my .send command was not await: const pannel = await channel.send(twitterlink).
The message id is easiy to find by using .id on the variable of the message:
const pannelid = pannel.id.
The resulting code can be found below:
const { SlashCommandBuilder } = require("#discordjs/builders");
const Discord = require("discord.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("setup")
.setDescription("Setup Tweet to be rewarded")
.addChannelOption(option =>
option
.setName('destination')
.setDescription('Select a channel for the reward pannel')
.setRequired(true)
)
.addStringOption(option =>
option
.setName("twitterlink")
.setDescription("Enter the Twitter Link")
.setRequired(false)
),
async execute(interaction) { // Fixed below here and simplified it
const channel = interaction.guild.channels.cache.get(interaction.options.getChannel("destination").id);
const twitterlink = interaction.options.getString("twitterlink");
const pannel = await channel.send(twitterlink)
pannel.react('🍎');
const pannelid = pannel.id
return interaction.reply({
content: "Pannel send",
ephemeral: true,
});
},
};

Random picture of cats discord.js Uncaught TypeError: Cannot read property 'pipe' of undefined

I am trying to make a bot that sends cat pictures from a cat API but when I run the code it gives an error that I have never seen.
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
function randomcat(){
let cat
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
cat = this.responseText.split(`"`)[9]
};
xhttp.open("GET", "https://api.thecatapi.com/v1/images/search", true);
xhttp.send();
return cat
}
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('message', async message=>{
console.log(randomcat());
message.channel.send("My Bot's message", {files: [randomcat()]});
})
When I run the code it returns Uncaught TypeError: Cannot read property 'pipe' of undefined.
Instead of the low-level xmlhttprequest you could use node-fetch to grab the response from the API. The API returns a JSON object as the response so you won't need to fiddle around with responseText.splits etc.
With fetch, it's pretty easy to get a response. Check out the snippet below:
async function getRandomCat() {
const response = await fetch('https://api.thecatapi.com/v1/images/search');
const json = await response.json();
return json[0]?.url;
}
getRandomCat().then(img => console.log('1', img))
getRandomCat().then(img => console.log('2', img))
getRandomCat().then(img => console.log('3', img))
Your full code would look like this. Don't forget to install node-fetch by running npm i node-fetch in your console.
Check out the working code below:
const fetch = require('node-fetch');
async function getRandomCat() {
const response = await fetch('https://api.thecatapi.com/v1/images/search');
const json = await response.json();
return json[0]?.url;
}
client.on('message', async (message) => {
if (message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'cat') {
try {
const cat = await getRandomCat();
if (cat) return message.channel.send({ files: [cat] });
} catch (error) {
console.log(error);
message.channel.send(
'There was an error fetching your random cat. Try again later!',
);
}
}
});

slack bot sending direct message to user using aws lambda function

I'm trying to send a direct message using slack web api to a user but I think my getSlackUser method which gets all the available users does not complete in time for when I call slackId;
the console.log(slackId) gives undefined meaning it doesn't complete my api call with bolt
how do I ensure getSlackUser method finishes (make it blocking) before it moves on to the rest?
const { WebClient } = require('#slack/web-api');
const { App } = require('#slack/bolt')
const rtm = new RTMClient(process.env.SLACK_OAUTH_TOKEN);
const web = new WebClient(process.env.SLACK_OAUTH_TOKEN);
const app = new App({
token: process.env.SLACK_OAUTH_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
exports.handler = async (event) => {
const slackId = await getSlackUser('example_real_name').id;
console.log(slackId);
await sendSlackMessage(slackId, 'Bot message');
}
sendSlackMessage = async (channel, message) => {
await web.chat.postMessage({
channel: channel,
text: message,
as_user: true
});
}
getSlackUser = async(real_name) => {
const result = await app.client.users.list({
token: process.env.SLACK_OAUTH_TOKEN
});
console.log(result);
return result.members.find((user) => user.real_name == real_name);
}
The problem is precedence on this line:
const slackId = await getSlackUser('example_real_name').id;
Since member access has a higher precedence (evaluated before) than await, it is effectively the same as:
const slackId = await (getSlackUser('example_real_name').id);
getSlackUser returns a Promise object, then its id member is undefined. Await waits for the undefined, which is undefined.
To fix this, make sure that the await is evaluated before the .id:
const slackId = (await getSlackUser('example_real_name')).id;

Categories

Resources