discord.js canvas image manipulation - discord

I wan't to make a command that put the user avatar in my wanted image
const { createCanvas, loadImage } = require('canvas')
const { MessageAttachment } = require('discord.js');
const Command = require('../../structures/Command')
module.exports = class extends Command {
constructor(client) {
super(client, {
name: 'wanted',
description: 'Coloca a cabeça de um usuário valendo $2000.',
options: [
{
name: 'user',
description: 'Usuário que terá a cabeça posta num cartaz de procurado',
type: 'USER',
required: true
}
]
})
}
run = async (interaction) => {
const canvas = createCanvas(239, 338)
const ctx = canvas.getContext('2d')
const wantedImg = await loadImage("imgs/wanted.png")
const userAv = interaction.options.getUser('user')
const Avatar = userAv.avatarURL()
interaction.reply(wantedImg)
}
}
I put the wantedImg in interaction.reply to see if the client return in chat the wanted image, but it's getting error...
Cannot find module
'C:\Users\pulse\OneDrive\Documentos\Cynthia\JS\src\commands\imgs\imgs'

I would recommend using jimp instead:
const user = message.mentions.users.first() //get The first user mentioned
if (!user) return message.reply("Who is wanted?")//return if no user was mentioned
var wantedImage = "wanted image url goes here"
var pfp = user.avatarURL({ format: 'png', dynamic: true, size: 128 }) //Get link of profile picture
var image = await Jimp.read(pfp)//read the profile picture, returns a Jimp object
//Composite resized bars on profile picture
image.composite((await Jimp.read(bars)).resize(128, 128), 0, 0)
//Create and attachment using buffer from edited picture and sending it
var image = new Discord.MessageAttachment(await image.getBufferAsync(Jimp.MIME_PNG))
message.reply(image) //replies to original command with image
It has far more options than canvas.

Make sure your path is correct, it seems that the path won't get resolved correctly. To be sure about your paths, consider using.
const {join} = require("path");
const wantedImg = loadImage(join(__dirname, "imgs/wanted.png"));

Related

Random gif and icon

I made a script that, when a user updates their profile, sends their new avatar to a channel based on the image extension. It's working when the extension is .png, but when it's .gif it sends it in the same channel where it sends the .pngs. My code is below:
Versao Pt
fiz um script pra quando um usuario alterar seu avatar o bot enviar ele em um canal, ta funcionando mas quando é .png o bot envia certinho, mas qunado é .gif ele envia no canal do .png e na mesma embed.
client.on('userUpdate', (oldUser, newUser) => {
const oldAvatarURL = oldUser.displayAvatarURL({ size: 2048, dynamic: true });
const newAvatarURL = newUser.displayAvatarURL({ size: 2048, dynamic: true });
if (oldAvatarURL === newAvatarURL) return;
const avatarExtension = newAvatarURL.split('.').pop();
const canalgif = client.channels.cache.get("1076541628405272667");
const canalicon = client.channels.cache.get("1076541589062692874");
//-----------------------------// GIFS //-----------------------------//
if (avatarExtension === 'gif') {
const embedgif = new Discord.EmbedBuilder()
.setImage(newAvatarURL)
.setColor('#360d60')
.setTitle('Teste gif');
canalgif.send({ embeds: [embedgif] });
} else {
//-----------------------------// Icon //-----------------------------//
const embedicon = new Discord.EmbedBuilder()
.setImage(newAvatarURL)
.setColor('#360d60')
.setTitle('teste icon');
canalicon.send({ embeds: [embedicon] });
}
});
you just need to remove the size parameter of both oldAvatarURL and newAvatarURL variable.
Indeed, with your code, a gif will be shown as gif?size=4096...
If you use .displayAvatarURL() with the size option, the URL returned will append a query string at the end. It means the avatar won't end with .gif, .png, .webp etc but with .webp?size=2048.
The way you're currently getting the extension of the URL won't work as you just split that by a . and check the last part.
// ⛔️ won't work
let url = `https://cdn.discordapp.com/avatars/827997777303699467/55b85c4fe590a54c53183990b030f128.webp?size=2048`
let extension = url.split('.').pop();
console.assert(extension === 'webp', `extension is not "webp", but "${extension}"`);
Instead of removing the size option where you end up with the default size of 128px, you could update the way you grab the extension. First, you could split by that . you already tried, then you could split again by ?:
// ✅ works as expected
let url1 = `https://cdn.discordapp.com/avatars/827997777303699467/55b85c4fe590a54c53183990b030f128.webp?size=2048`;
let url2 = `https://cdn.discordapp.com/avatars/827997777303699467/55b85c4fe590a54c53183990b030f128.png`;
let extension1 = url1.split('.').pop().split('?')[0];
let extension2 = url2.split('.').pop().split('?')[0];
console.log(extension1);
console.log(extension2);
So, the following should work as expected:
client.on('userUpdate', (oldUser, newUser) => {
const options = { size: 2048, dynamic: true };
const oldAvatarURL = oldUser.displayAvatarURL(options);
const newAvatarURL = newUser.displayAvatarURL(options);
if (oldAvatarURL === newAvatarURL) return;
const avatarExtension = newAvatarURL.split('.').pop().split('?')[0];
const embed = new Discord.EmbedBuilder()
.setImage(newAvatarURL)
.setColor('#360d60');
if (avatarExtension === 'gif') {
const canalGif = client.channels.cache.get('1076541628405272667');
canalGif.send({ embeds: [embed.setTitle('Teste gif')] });
} else {
const canalIcon = client.channels.cache.get('1076541589062692874');
canalIcon.send({ embeds: [embed.setTitle('Teste icon')] });
}
});

discord.js api request command

const config = require(`${process.cwd()}/botconfig/config.json`)
var ee = require(`${process.cwd()}/botconfig/embed.json`)
const fetch = require("node-fetch");
const { MessageEmbed } = require(`discord.js`);
module.exports = {
//definition
name: "glifestats", //the name of the command
category: "⌨️ Programming", //the category this will be listed at, for the help cmd
aliases: [""], //every parameter can be an alias
cooldown: 4, //this will set it to a 4 second cooldown
usage: "glifestats <id>", //this is for the help command for EACH cmd
description: "check stats", //the description of the command
};
run: async (client, message, args, cmduser, text, prefix) => {
await interaction.deferReply();
const term = interaction.options.getString('term');
const query = new URLSearchParams({ term });
const { list } = await fetch(`https://api.gtaliferp.fr:8443/v1/extinction/profiles/main/${query}`)
.then(response => response.json());
}
When i try to do this command, it does nothing.
It needs to get the query from the user message and post it to the url, example:
.command 4443
bot returns the https://api.gtaliferp.fr:8443/v1/extinction/profiles/main/4443 data and postes it.
And also, i wanted to make the different data on an embed like this: data 1: data data 2: data .... but i cant do it, ( check the url provided for the data titles if you want to help with that)
So you seperated your module from the rest of the command with this line marked below
module.exports = {
//definition
name: "glifestats", //the name of the command
category: "⌨️ Programming", //the category this will be listed at, for the help cmd
aliases: [""], //every parameter can be an alias
cooldown: 4, //this will set it to a 4 second cooldown
usage: "glifestats <id>", //this is for the help command for EACH cmd
description: "check stats", //the description of the command
} //here
Also term is reqesting an option that is not defined
const term = interaction.options.getString('term');
Interaction is not defined
run: async (client, message, args, cmduser, text, prefix) => {
await interaction.deferReply();
const term = interaction.options.getString('term');
Try this
const config = require(`${process.cwd()}/botconfig/config.json`) // is this needed if not, delete
var ee = require(`${process.cwd()}/botconfig/embed.json`) // is this needed if not, delete
const fetch = require("node-fetch");
const {
MessageEmbed
} = require(`discord.js`); // is this needed if not, delete
module.exports = {
name: "glifestats", //the name of the command
category: "⌨️ Programming", //the category this will be listed at, for the help cmd
aliases: [""], //every parameter can be an alias
cooldown: 4, //this will set it to a 4 second cooldown
usage: "glifestats <id>", //this is for the help command for EACH cmd
description: "check stats", //the description of the command
options: [{
name: "term",
description: "Code to search",
required: true,
type: "STRING",
}],
run: async (client, interaction) => {
await interaction.deferReply();
const term = interaction.options.getString('term');
const url = `https://api.gtaliferp.fr:8443/v1/extinction/profiles/main/${term}`
const list = await fetch(url).then(response => response.json())
// Do something with list
console.log(list)
return interaction.followUp({
content: `List has been logged but can't be sent yet because it is an object and need to be further worked to get specific elements from it`
})
// Above seemed cleaner to me
/*
const query = new URLSearchParams({
term
});
const {
list
} = await fetch(`https://api.gtaliferp.fr:8443/v1/extinction/profiles/main/${query}`)
.then(response => response.json());
*/
}
}

How do I get the video url with ytdl-core?

const { YTSearcher } = require('ytsearcher');
const searcher = new YTSearcher({
key: config.YOUTUBE_API_KEY,
revealed: true
});
let result = await searcher.search(args.join(" "), { type: "video" })
var songInfo = await ytdl.getInfo(result.first.url)
var song = {
title: songInfo.videoDetails.title,
url: songInfo.videoDetails.video_url
};
I'm using ytdl-core to play music using a discord bot, when I try to play a song it sends the error "Cannot read property 'url' of underfined"

Bot readout for who used a command

Wanting to have a readout channel for my bot to keep track of what happens, just like a second console log. Want to be able to have it read out in the message the username of the person who used the command. Any ideas? Also, in a similar note, is there a way to copy the console readout and possibly just paste that instead?
var Scraper = require('images-scraper');
const google = new Scraper({
puppeteer: {
headless: true
},
})
module.exports = {
name: 'image',
description: 'Google image scraper',
async execute(message, args){
const readout = message.guild.channels.cache.find(c => c.name === 'bot-readout');
const image_query = args.join(' ');
if(!image_query) return message.channel.send('Please enter a valid image search.');
const image_results = await google.scrape(image_query, 1);
message.channel.send(image_results[0].url);
readout.send('Image sent');
}
}
I think you want
message.author.username (It gives username who sent the message)
or message.member (it gives user as a guildmember)
Just access the author property of the message object and include it via a template string into the message:
readout.send(`Image sent to ${message.author.username}`);
Ended up doing an embed system in a separate channel on my discord.
const Discord = require('discord.js');
module.exports = {
name: 'suggestionlog',
description: 'logs suggestion',
execute(message){
const readout = message.guild.channels.cache.find(c => c.name === 'bot-readout');
const embed = new Discord.MessageEmbed()
.setColor('FADF2E')
.setTitle(message.channel.name)
.setAuthor(message.author.username, message.author.displayAvatarURL({ dynamic: true }))
.setDescription(message);
readout.send(embed)
.catch((err)=>{
throw err;
});
}
}

How To Make Queue System - Discord.js

I am working on a music and I would like to know how to add a queue system to the command; I have been looking around for hours and not been able to find anything on it,
If anyone can help that would be great and I don't need a queue command but I do need to add a queue system so it would really help I will be checking back in an hour to see if anyone has given me or an idea or the answer to my problem
This is my code so far:
const Discord = require('discord.js');
const ytdl = require('ytdl-core');
const YoutubeSearcher = new QuickYtSearch({
YtApiKey: '',
});
module.exports={
name: 'play',
category: 'music',
description: 'Joins and plays the song',
aliases: ['p'],
usage: '.play <song name or URL>',
run: async(client, message, args)=>{
try{
if (message.member.voice.channel) {
let args = message.content.split(' ').slice(1).join(' ');
if (!args) {
const error = new Discord.MessageEmbed()
.setTitle(`🔴 Looks like there is an Issue!`)
.setColor(0x2f3136)
.setDescription(`You have to provide me at least, the name or the url.\n\nExample :
\`\`\`fix
.play <url>
OR
.play <name>\`\`\``)
return message.channel.send(error);
};
message.member.voice.channel.join()
.then(connection => {
if (YoutubeSearcher.isVideoUrl(args) === false) {
YoutubeSearcher.getVideo(args).then(video => {
const volume = { volume: 10 };
const dispatcher = connection.play(ytdl(video.url, { filter: 'audioonly' }, volume));
const play1 = new Discord.MessageEmbed()
.setTitle('Song info')
.setURL(video.url)
.setDescription(`Name: ${video.title}, By: ${video.channelTitle}`)
.setThumbnail(video.highThumbnail)
message.channel.send(play1);
dispatcher.on("finish", () => {
dispatcher.end();
message.reply('End of the song.');
message.member.guild.me.voice.channel.leave();
});
});
} else {
const volume = { volume: 10 };
const dispatcher = connection.play(ytdl(args, { filter: 'audioonly' }, volume));
message.reply('Now playing ' + args);
dispatcher.on("finish", () => {
dispatcher.end();
message.reply('End of the song.')
message.member.guild.me.voice.channel.leave();
});
};
});
} else {
message.reply('You need to join a voice channel.');
};
}catch(err) {
console.log(err)
return message.channel.send(`Error: ${err.message}`)
}
}
}
In theory you could use the queue data structure (the last element is taken out and when a new one is added it is added to the start) and in the queue hold the music that is requested to be played. This is how it might roughly look like:
client.on("message", (msg) => {
var arrOfMusic = [];
if(msg.content.startsWith("!queue")){
msg.channel.send(arrOfMusic.join(" , ")
}
if(msg.content.startsWith("!play")){
arrOfMusic.push(msg.content.slice(6))
// you don't need to play the music
}
// your code to play the end of the array all you do is play the last element you also //need to check once it is over and use pop to remove last element
if(msg.content.startsWith("clear")){
arrOfMusic = []
}
})

Resources