How do I use an API in Discord.js? - javascript

I was looking for a cool way I could automate some interesting news articles on my discord server. I wanted to use webhooks at first but then decided to go with APIs. I looked all around and saw that I should go with New York Times API but when I went to code it, it came up with a few errors.
const Discord = require("discord.js");
const client = new Discord.Client();
const token = require("./token.js");
const fetch = require('node-fetch');
const prefix = '!';
const trim = (str, max) => str.length > max ? `${str.slice(0, max - 3)}...` : str;
client.once('ready', () => {
console.log('Ready!');
});
client.on('message', async message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'news') {
const { file } = await fetch('https://api.nytimes.com/svc/topstories/v2/technology.json?api-key=').then(response => response.json());
message.channel.sendMessage(file);
}
}).then((state) => {
assert(state.action === 'DONE', 'should change state');
})
.catch((error) => {
assert.isNotOk(error, 'Promise error');
});
throw
client.login(token);
This is my code, I know this is probably riddled with mistakes but I am just starting out with node.js
I looked at the example from the discord.js website and took some stuff from there. I don't know what I should do and if you could explain it out a little to help me learn that would be great. I keep getting the Unhandled Rejection Promise Warning and the Cannot send an empty message errors. I am using Visual Studio Code.

You can use the async/await to get the results array from the API response, then send the details in embeds. You can either send the first article, a random article or more than one articles.
The following should work, it sends the first three articles:
const { Client, MessageEmbed } = require('discord.js');
const fetch = require('node-fetch');
const token = require("./token.js");
const client = new Client();
const API_KEY = 'QerEdX953-NOT-REAL-hdvgx7UPs';
const prefix = '!';
client.on('message', async (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'news') {
try {
const response = await fetch(
`https://api.nytimes.com/svc/topstories/v2/technology.json?api-key=${API_KEY}`,
);
const data = await response.json();
if (data.results.length === 0) {
return message.channel.send('There are no top stories for you today 🙊');
}
const embed = new MessageEmbed();
// you could also get the number of stories from args[0]
const MAX_STORIES = 3;
data.results.slice(0, MAX_STORIES).forEach((result) => {
embed.setTitle(result.title);
embed.setDescription(result.abstract);
embed.setURL(result.url);
embed.setTimestamp(result.published_date);
if (result.multimedia.length && result.multimedia[2]) {
embed.setThumbnail(result.multimedia[2].url);
}
message.channel.send(embed);
});
} catch (error) {
message.channel.send('Oops, there was an error fetching the API');
console.log(error);
}
}
});

The response from the Top Stories API does not have a file key and so is undefined. You want to destructure results to access the articles array.
The error message says that you cannot call sendMessage with an empty message (undefined in this case).
I'm not sure of the message you want to send but you can send the link to the first article with the following code, for example.
const res = await fetch(`https://api.nytimes.com/svc/topstories/v2/technology.json?api-key=${NYTIMES_API_KEY}`)
const { results } = await res.json()
if (results.length) {
message.channel.sendMessage(results[0].url)
} else {
message.channel.sendMessage('No articles found')
}

Related

(Discord Bot) Get message that follows command prefix

I'm trying to reference the previous message sent by a client using discord.js
How would I go about getting the last message sent?
client.on("messageCreate", (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'set-reminder'){
channelName = client.channels.cache.find(channel => channel.name === "spam");
message.channel.send("Command Works");
channelName.messages.fetch({ limit: 1 }).then(messages => {
// find "command works" message (previous message from client)
let lastMessage = messages.first();
console.log(lastMessage.content);
})
}
})```
The message.channel.send function is async
You can await the send function or use .then
The send function also returns the message, you don't need a query to get it
await
let message = await message.channel.send("Command Works");
console.log(message.content);
then
message.channel.send("Command Works").then((message) => {
console.log(message.content);
});
(async means not-synchronous : you have to wait to get its result)

Adding a global timer for tmi.js integrated discord.js

I'm trying to do a discord bot that listens to multiple twitch chat for commands, then run them on discord, using both tmi.js and discord.js. Currently it is working, but I can't seem to add a global cooldown on the commands itself to prevent spamming. At first I've tried adding a cd timer to each command, but I'm unable to get it working, hence the decision to try to make a global cd but still to no avail. Am I doing something wrongly?
twitch.on('message', (channel, tags, message, self) => {
if(!message.startsWith(prefix) || self) return;
const args = (message.slice(prefix.length).trim().split(/ +/));
const commandName = args.shift().toLowerCase();
if (!twitch.commands.has(commandName)) return;
const command = twitch.commands.get(commandName);
}
try {
command.execute(bot, botChannel, vcChannel, isReady);
} catch (error){
console.error(error);
}
});
just to update, I basically took a leaflet from async await functions here: https://stackoverflow.com/a/54772517/14637034
Then, I modified the code as such:
const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
setTimeout(() => {
cb();
resolve();
}, timeout);
});
const doStuffAsync = async () => {
await setAsyncTimeout(() => {
isReady = true;
console.log(isReady);
}, 10000);};
twitch.on('message', (channel, tags, message, self) => {
if(!message.startsWith(prefix) || self) return;
const args = (message.slice(prefix.length).trim().split(/ +/));
const commandName = args.shift().toLowerCase();
if (!twitch.commands.has(commandName)) return;
if (isReady){
const command = twitch.commands.get(commandName);
isReady = false;
try {
command.execute(bot, botChannel, vcChannel);
} catch (error){
console.error(error);
}
doStuffAsync();
}
});
Seemed to work for now, as 10s is a long enough time for bot to leave discord properly without causing timeout. I'm still open to better suggestions for optimization though!

discord.js node.js code object promise problem

client.on('message', message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();
let member = message.guild.members;
if (command === 'ping') {
message.channel.send(`${srv.getServerName().then()}`)
}
});
I have a coding like this, when I write ping from the channel, it should write servername but it writes object promise. How can I solve it?
Sorry my english not good, i using google translate.
It seems srv.getServerName() is a Promise, and you're sending it instead of the resolved value to your channel. That's why you see [object Promise].
You can get the server name first and send the message from its .then() method:
if (command === 'ping') {
srv
.getServerName()
.then(name => message.channel.send(name))
}
Or you can make the callback an async function and await the response before you send it:
client.on('message', async (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();
const member = message.guild.members;
if (command === 'ping') {
const serverName = await srv.getServerName();
const embed = new MessageEmbed()
.setTitle('Embed title')
.setDescription(`This is the "${serverName}" server`);
message.channel.send(embed);
}
});

Fetch data containing a variable

So my goal is to get the uuid from a Minecraft username via the Mojang api and then run it through the Hypixel Skyblock api. My problem is that I cant use the variable ${jsonsbid.id} inside JSON.stringify. I'm quite new to coding and I cant seem to get it working.
I'm using the Discord.js api to send out the JSON result as message.
import { key } from './config.json';
client.on('message', message => {
const prefix = '!skyblock ';
const skyblock = message.content.slice(prefix.length);
const fetch = require('node-fetch');
if (!message.content.startsWith(prefix) || message.author.bot) return; {
try {
fetch(`https://api.mojang.com/users/profiles/minecraft/${skyblock}`)
.then(response => response.json())
.then(jsonsbid => fetch(`https://api.hypixel.net/Skyblock/profiles?uuid=${jsonsbid.id}&key=${key}`))
.then(responsesb => responsesb.json())
.then(jsonsb => message.channel.send(`Coins: ${JSON.stringify(jsonsb.profiles[0].members.${jsonsbid.id}.coins_purse)}`)) //this is the line i need to get working
} catch (err) {
message.channel.send('A error occured')
}
};
Well I hope you can help me so I can learn more about coding and js.
You could create a cache called jsonsbidCache in your bot that stores the jsonsbid JSON result by the key skyblock. You can then later access it in the final .then call.
If you only need to reference jsonsbid.id, then you could modify this to only cache said id value.
import { key } from './config.json';
const mojangApiUrl = 'https://api.mojang.com';
const skyblockApiUrl = 'https://api.hypixel.net/Skyblock';
const displayCoins = (member) => {
return `Coins: ${JSON.stringify(member.coins_purse)}`;
};
const jsonsbidCache = new Map(); // Cache
client.on('message', message => {
const prefix = '!skyblock ';
const skyblock = message.content.slice(prefix.length);
const fetch = require('node-fetch');
if (!message.content.startsWith(prefix) || message.author.bot) {
return;
}
try {
fetch(`${mojangApiUrl}/users/profiles/minecraft/${skyblock}`)
.then(response => response.json())
.then(jsonsbid => {
jsonsbidCache.set(skyblock, jsonsbid); // Cache it
return fetch(`${skyblockApiUrl}/profiles?uuid=${jsonsbid.id}&key=${key}`)
})
.then(responsesb => responsesb.json())
.then(jsonsb => {
const memberId = jsonsbidCache.get(skyblock).id;
const member = jsonsb.profiles[0].members[memberId];
message.channel.send(displayCoins(member))
})
} catch (err) {
message.channel.send('A error occured');
}
}

Discord.js fetching 5 particular messages with a 404 fails completely

I only want to fetch these particular messages not everything in between that's why I'm not using
fetch({limit: 10})
starting point is :
const discord = require('discord.js'); //#12.2.0
const bot = new discord.Client();
const config = require('./../../config.json');
messagesToCache = [
"720062178790473769",
"720062188991283296",
"720062198323347486", //missing message
"720062209824391278"
]
let promises = [];
const logit = () =>{
console.log(promises)
}
bot.once('ready', async ()=>{
console.log("ready");
channel = await bot.channels.fetch("712405355740790835")
let i = 0;
for(const id of messagesToCache){
console.log("fetching ", id)
promises.push(channel.messages.fetch(id).then((msg)=>{
console.log(msg.content);
logit();
i++;
}).catch(err =>{
console.error(err);
logit();
i++;
}))
}
})
bot.login(config.token);
but event tho the 404 is handled it prevents the 4th promise which is already pending from resolving. any advice ?

Categories