Random gif and icon - discord

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')] });
}
});

Related

Deleting old message when Discord.JS sends new message

I'm trying to make a music bot with DiscordJS. It sends a message again when it switches to new music. When it is too much, it causes pollution. How can I set the old message to be deleted and the new message to remain when I switch to a new song or skip a song?
Code:
const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, Client } = require('discord.js');
const ms = require('ms');
/**
* #param {Client} client
*/
module.exports.run = async (client, player, track) => {
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId('loop')
.setEmoji(`🔁`)
.setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId('volume-')
.setEmoji(`🔉`)
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId('p/p')
.setEmoji(`⏯️`)
.setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId('volume+')
.setEmoji(`🔊`)
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId('skip')
.setEmoji(`⏭️`)
.setStyle(ButtonStyle.Secondary),
);
const embed = new EmbedBuilder()
.setAuthor({
name: `Now Playing`,
iconURL: track.info.requester.displayAvatarURL(),
})
.setColor('Blue')
.setDescription(
`
**Track**: [${track.info.title}](${track.info.uri})
`,
)
const channel = client.channels.cache.get(player.textChannel)
await channel?.send({ embeds: [embed], components: [row] })
};
When I switch to the new song, I want the old message to be deleted. I tried removing some lines but it didn't work.
You could store an object that contains channel IDs and message IDs somewhere else in your code:
let songMessages = {
'channel-id-here':'message-id-here',
'another-channel-id':'another-message-id',
// etc
}
Instead of sending a new message at the bottom of your run() function, you could first check for an existing message in the current channel, and if there is one, you can delete it.
// assuming you're able to access the songMessages object globally
const channel = client.channels.cache.get(player.textChannel)
if(songMessages[channel.id]) {
let oldMessage = await channel.messages.fetch(songMessages[channel.id])
await oldMessage.delete()
}
let message = await channel?.send({ embeds: [embed], components: [row] })
songMessages[channel.id] = message.id
You'll have to make sure you remove the data from the object after the bot stops playing music as well.

discord.js canvas image manipulation

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"));

Multistep command and message collector discord.js

I'm trying to create a multistep command and after the last edit of the embed I want to collect what the user reply. I tried await message , message collector but I did not succeed.
This is the last part of the command. Thanks for your help
.then(collected => {
var date = collected.first().emoji.name
switch (date){
case '1️⃣':
message.channel.send(`:white_check_mark: Tu as choisis d'ajouter un devoir pour lundi`).then(m => m.delete({ timeout: 2000 }));
var date = 'lundi'
break;
case '2️⃣':
message.channel.send(`:white_check_mark: Tu as choisis d'ajouter un devoir pour mardi`).then(m => m.delete({ timeout: 2000 }));
var date = 'mardi'
break;
case '3️⃣':
message.channel.send(`:white_check_mark: Tu as choisis d'ajouter un devoir pour mercredi`).then(m => m.delete({ timeout: 2000 }));
var date = 'mercredi'
break;
case '4️⃣':
message.channel.send(`:white_check_mark: Tu as choisis d'ajouter un devoir pour jeudi`).then(m => m.delete({ timeout: 2000 }));
var date = 'jeudi'
break;
case '5️⃣':
message.channel.send(`:white_check_mark: Tu as choisis d'ajouter un devoir pour vendredi`).then(m => m.delete({ timeout: 2000 }));
var date = 'vendredi'
break;
}setTimeout(() => 5000)
m.reactions.removeAll()
const newEmbed = new Discord.MessageEmbed()
.setColor('RED')
.setTitle('Entrer le descriptif du devoir')
setTimeout(() => 7000)
m.edit(newEmbed)
const filter = m => m.author.id === message.author.id;
message.channel.awaitMessages(filter,{time:10000}).then(collected =>{
console.log(m)
})
The TextChannel.awaitMessages() method returns a Collection (a Map).
Since you want the output to be an array of strings, you can do something like this:
message.channel.awaitMessages(filter,{time:10000}).then(collected =>{
const list = [];
collected.forEach((message) => {
list.push(message.content);
});
console.log(list); // ["hello", "world"]
});
If you only want a single message, you can use the MessageCollectorOptions's max property and set it to 1:
message.channel.awaitMessages(filter,{time:10000, max:1}).then(collected =>{
// in case the user did not answer
if (typeof collected.first() === "undefined") {
return;
}
const string = collected.first().content;
console.log(string); // "hello"
});

Change avatar size Discord.js

I want to change the size of the profile photo when the photo is a gif, how can I do it?
I have tried user.displayAvatarURL ({size: 2048, dynamic: true}); but the bot crashes D:
the error is the following:: (node:15836) UnhandledPromiseRejectionWarning: TypeError: user.displayAvatarURL is not a function
module.exports = {
nombre: "avatar",
alias: ["foto"],
descripcion: "Este comando muestra la foto de perfil de un usuario",
run: (client, message, args) => {
const user = message.mentions.users.first() || message.author;
const avatarEmbed = new Discord.RichEmbed()
avatarEmbed.setColor(0x333333)
avatarEmbed.setAuthor(user.username)
avatarEmbed.setImage(user.displayAvatarURL);
message.channel.send(avatarEmbed);
}
}
I have found a way to fix the crash, I attach code :D
const user = message.mentions.users.first() || message.author;
if(user.displayAvatarURL.endsWith(".gif")){
const avatarEmbed = new Discord.RichEmbed()
avatarEmbed.setColor(0x333333)
avatarEmbed.setAuthor(user.username)
avatarEmbed.setImage(user.displayAvatarURL + "?size=1024");
message.channel.send(avatarEmbed);
} else {
const avatarEmbed = new Discord.RichEmbed()
avatarEmbed.setColor(0x333333)
avatarEmbed.setAuthor(user.username)
avatarEmbed.setImage(user.displayAvatarURL);
message.channel.send(avatarEmbed);
}

React Hooks- forEach of an array doesn't work

I'm working in a project using React hooks. However I try to use a variable without useState hook to handle a variable. The thing is that when I'm running a function that contains an array operation, it seems like that is not took into account and skip that step.
I did the change that way (you'll see that soon in the code) because I'm using an array that is continuos adding more values, so I think that is easier to do in that way.
Can you please help me to fix that issue or explain me why is that doesn't working?
Here is the code:
let platosRegVent:any = []; // This is how I'm declaring my array
const agregarPlato = async () => {
if(validarCamposPlato()){
try{
let valorParcial = 0;
let platoExist = await encontrarPlato(config, rvPlato);
if(platoExist === true){
setAgregadoMin(true);
platoCodigo = await obtenerCodigoPlato(config, rvPlato);
platosRegVent.push({codigoPlato: platoCodigo, cantidad: rvCantidad}); // Here is where I'm adding more objects to my array.
let costoPlato = await obtenerValorPlato(config, rvPlato);
valorParcial = valorTotal;
setValorTotal(valorParcial += (costoPlato * parseInt(rvCantidad)));
setRvPlato('');
setRvCantidad('');
}
else{
toast.error('El plato ingresado no se ha encontrado.');
setRvPlato('');
}
}
catch(error){
toast.error('Un error inesperado ha ocurrido, por favor intente mas tarde.');
props.handleSubmit();
}
}
}
const finalizarRegVent = async () => {
console.log(agregadoMin);
if(validarCampos()){
try{
if(rvPlato !== '' || rvCantidad !== ''){
await agregarPlato();
}
if(agregadoMin === true){
rvCodigo = await crearRegistroVenta(config, valorTotal, fechaActual, regVentMesa);
platosRegVent.forEach( (plato : any) => { //Here is the operation of the array. It seems that this step is skipped.
crearRegVentPlato(config, rvCodigo, plato.codigoPlato, plato.cantidad);
});
setValorFinal(false);
}
else{
toast.error('Debe ingresar por lo menos un plato para completar el registro.');
}
}
catch(error){
toast.error('Un error inesperado ha ocurrido, por favor intente mas tarde.');
props.handleSubmit();
}
}
}
You should use useState() & useCallback() hook instead.
Because the mechanism of React only updated when detecting the change of state. If you create static variable, react can't update its value.
Alternatively, useRef() is solution for you.

Resources