Guide discord.js
Sujets populaires

Réactions

Réagir aux messages

L'une des premières choses que beaucoup de gens veulent savoir est comment réagir avec des emojis, personnalisés et "ordinaires" (Unicode). Il existe différentes routes que vous devez emprunter pour chacun d'entre eux, alors regardons les deux.

Voici le code de base que nous utiliserons :

reactionsample.js
const { Client, Events, GatewayIntentBits } = require('discord.js');

const client = new Client({
	intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions],
});

client.once(Events.ClientReady, (readyClient) => {
	console.log(`Ready! Logged in as ${readyClient.user.tag}`);
});

client.on(Events.InteractionCreate, (interaction) => {
	// ...
});

client.login('your-token-goes-here');

Emojis Unicode

Pour réagir avec un emoji Unicode, vous aurez besoin du caractère Unicode réel de l'emoji. Il y a de nombreuses façons d'obtenir un caractère Unicode d'un emoji, mais le moyen le plus facile serait par Discord lui-même. Si vous envoyez un message avec un emoji Unicode (tel que :smile:, par exemple) et mettez un \ avant, il va "echapper" l'emoji et afficher le caractère Unicode au lieu de l'image emoji standard.

Pour réagir avec un emoji, vous devez utiliser la méthode message.react(). Une fois que vous avez le caractère emoji, tout ce que vous devez faire est de le copier-coller en tant que chaîne dans la méthode .react() !

reactionsample.js
client.on(Events.InteractionCreate, async (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	const { commandName } = interaction;

	if (commandName === 'react') {
		const response = await interaction.reply({ content: 'Vous pouvez réagir avec des emojis Unicode !', withResponse: true });
		response.resource.message.react('😄'); 
	}
});

Emojis personnalisés

Pour les emojis personnalisés, il existe plusieurs façons de réagir. Comme les emojis Unicode, vous pouvez également échapper aux emojis personnalisés. Cependant, lorsque vous échappez à un emoji personnalisé, le résultat sera différent.

Ce format est essentiellement le nom de l'emoji, suivi de son ID. Copiez et collez l'ID dans la méthode .react() en tant que chaîne.

reactionsample.js
client.on(Events.InteractionCreate, async (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	const { commandName } = interaction;

	if (commandName === 'react-custom') {
		const response = await interaction.reply({ content: 'Vous pouvez réagir avec des emojis personnalisés !', withResponse: true });
		response.resource.message.react('😄'); 
		response.resource.message.react('123456789012345678'); 
	}
});

Vous pouvez également transmettre différents formats de l'emoji à la méthode .react().
Dans le format de mention emoji, les emojis animés sont préfixés par un a :

message.react('<:blobreach:123456789012345678>');
message.react('blobreach:123456789012345678');
message.react('<a:animated_blobreach:123456789012345678>'); 
message.react('a:animated_blobreach:123456789012345678');

Formidable ! Cependant, cette voie ne sera pas toujours disponible pour vous. Parfois, vous devrez réagir avec un emoji par programmation. Pour ce faire, vous devrez récupérer l'objet emoji.

Deux des façons les plus faciles de récupérer un emoji seraient :

  • Utilisez .find() sur une Collection d'Emojis.
  • Utilisez .get() sur la Collection client.emojis.cache.

Deux ou plusieurs emojis peuvent avoir le même nom, et l'utilisation de .find() ne retournera que l'entrée première qu'elle trouve. En tant que tel, cela peut causer des résultats inattendus.

En utilisant .find(), votre code ressemblerait à quelque chose comme ceci :

reactionsample.js
if (commandName === 'react-custom') {
	const response = await interaction.reply({ content: 'Vous pouvez réagir avec des emojis personnalisés !', withResponse: true });
	const message = response.resource.message;
	const reactionEmoji = message.guild.emojis.cache.find((emoji) => emoji.name === 'blobreach'); 
	message.react(reactionEmoji);
}

En utilisant .get(), votre code ressemblerait à quelque chose comme ceci :

reactionsample.js
if (commandName === 'react-custom') {
	const response = await interaction.reply({ content: 'Vous pouvez réagir avec des emojis personnalisés !', withResponse: true });
	const reactionEmoji = client.emojis.cache.get('123456789012345678'); 
	response.resource.message.react(reactionEmoji);
}

Bien sûr, si vous avez déjà l'ID de l'emoji, vous devriez le placer directement dans la méthode .react(). Mais si vous voulez faire d'autres choses avec les données d'emoji plus tard (par exemple, afficher le nom ou l'URL de l'image), il est préférable de récupérer l'objet emoji complet.

Réagir dans l'ordre

Si vous mettez simplement un message.react() sous un autre, il ne réagira pas toujours dans l'ordre. C'est parce que .react() est une opération asynchrone. Cela signifie que react('🍊') n'attendra pas que react('🍎') se termine avant de faire l'appel API et causera une condition de course. Si vous exécutez ce code plusieurs fois, vous devriez pouvoir observer cet ordre incohérent :

reactionsample.js
client.on(Events.InteractionCreate, async (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	const { commandName } = interaction;

	if (commandName === 'fruits') {
		const response = await interaction.reply({ content: 'Réaction avec des fruits !', withResponse: true });
		const { message } = response.resource;
		message.react('🍎');
		message.react('🍊');
		message.react('🍇');
	}
});

Comme vous pouvez le voir, si vous le laissez comme ça, il ne s'affichera pas comme vous le souhaitez. Il a pu réagir correctement au premier essai mais réagit différemment à chaque fois après.

Heureusement, il y a deux solutions faciles. La première serait de chaîner les .then()s dans l'ordre que vous voulez afficher.

reactionsample.js
client.on(Events.InteractionCreate, async (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	const { commandName } = interaction;

	if (commandName === 'fruits') {
		const response = await interaction.reply({ content: 'Réaction avec des fruits !', withResponse: true });

		response.resource.message
			.react('🍎')
			.then(() => message.react('🍊'))
			.then(() => message.react('🍇'))
			.catch((error) => console.error('L\'un des emojis n\'a pas pu réagir :', error));
	}
});

L'autre serait d'utiliser les mots-clés async/await.

reactionsample.js
client.on(Events.InteractionCreate, async (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	const { commandName } = interaction;

	if (commandName === 'fruits') {
		const response = await interaction.reply({ content: 'Réaction avec des fruits !', withResponse: true });
		const { message } = response.resource;

		try {
			await message.react('🍎'); 
			await message.react('🍊');
			await message.react('🍇');
		} catch (error) {
			console.error('L\'un des emojis n\'a pas pu réagir :', error);
		}
	}
});

Si vous réessayez avec l'un des blocs de code ci-dessus, vous obtiendrez le résultat que vous aviez initialement voulu !

Si vous n'êtes pas familier avec les Promises ou async/await, vous pouvez en savoir plus à ce sujet MDN ou notre page de guide sur async/await !

Gérer plusieurs réactions si l'ordre n'a pas d'importance

Cependant, si l'ordre dans lequel les emojis réagissent ne vous dérange pas, vous pouvez profiter de Promise.all(), comme ceci :

reactionsample.js
if (commandName === 'fruits') {
	const message = await interaction.reply({ content: 'Réaction avec des fruits !' });
	Promise.all([message.react('🍎'), message.react('🍊'), message.react('🍇')]).catch((error) =>
		console.error('L\'un des emojis n\'a pas pu réagir :', error),
	);
}

Cette petite optimisation vous permet d'utiliser .then() pour gérer le moment où toutes les Promises ont été résolues, ou .catch() quand l'une d'elles échoue. Vous pouvez aussi l'await pour faire des choses après que toutes les réactions aient été résolues puisqu'elle retourne une Promise.

Supprimer les réactions

Maintenant que vous savez comment ajouter des réactions, vous vous posez peut-être la question, comment les supprimer ? Dans cette section, vous apprendrez comment supprimer toutes les réactions, supprimer les réactions par utilisateur et supprimer les réactions par emoji.

Toutes ces méthodes nécessitent les permissions ManageMessages. Assurez-vous que votre bot a les permissions avant de tenter d'utiliser l'une de ces méthodes, car elle affichera une erreur sinon.

Supprimer toutes les réactions

Supprimer toutes les réactions d'un message est le plus facile, l'API vous permet de le faire via un seul appel. Cela peut être fait via la méthode message.reactions.removeAll().

message.reactions.removeAll().catch((error) => console.error('Échec de l\'effacement des réactions :', error)); 

Supprimer les réactions par emoji

Supprimer les réactions par emoji se fait facilement en utilisant MessageReaction#remove.

message.reactions.cache
	.get('123456789012345678')
	.remove() 
	.catch((error) => console.error('Échec de la suppression des réactions :', error));

Supprimer les réactions par utilisateur

Si vous n'êtes pas familier avec Collection#filter et Map.has() prenez le temps de comprendre ce qu'ils font, puis revenez.

Supprimer les réactions par un utilisateur n'est pas aussi simple que supprimer par emoji ou supprimer toutes les réactions. L'API ne fournit pas de méthode pour supprimer sélectivement les réactions d'un utilisateur. Cela signifie que vous devrez itérer parmi les réactions qui incluent l'utilisateur et les supprimer.

const userReactions = message.reactions.cache.filter((reaction) => reaction.users.cache.has(userId));

try {
	for (const reaction of userReactions.values()) {
		await reaction.users.remove(userId);
	}
} catch (error) {
	console.error('Échec de la suppression des réactions.');
}

En attente de réactions

Un cas d'utilisation courant pour les réactions dans les commandes est que l'utilisateur confirme ou refuse une action ou crée un système de sondage. Heureusement, nous avons réellement déjà une page de guide couvrant cela ! Vérifiez cette page si vous souhaitez une explication plus approfondie. Sinon, voici un exemple de base pour référence :

message.react('👍').then(() => message.react('👎'));

const collectorFilter = (reaction, user) => {
	return ['👍', '👎'].includes(reaction.emoji.name) && user.id === interaction.user.id;
};

message
	.awaitReactions({ filter: collectorFilter, max: 1, time: 60_000, errors: ['time'] })
	.then((collected) => {
		const reaction = collected.first();

		if (reaction.emoji.name === '👍') {
			message.reply('Vous avez réagi avec un pouce levé.');
		} else {
			message.reply('Vous avez réagi avec un pouce baissé.');
		}
	})
	.catch((collected) => {
		message.reply('Vous n\'avez réagi ni avec un pouce levé, ni avec un pouce baissé.');
	});

Écoute des réactions sur les anciens messages

Les messages envoyés avant le démarrage de votre bot ne sont pas en cache à moins que vous ne les récupériez d'abord. Par défaut, la bibliothèque n'émet pas d'événements client si les données reçues et en cache ne sont pas suffisantes pour construire des objets entièrement fonctionnels. Depuis la version 12, vous pouvez modifier ce comportement en activant les partiels. Pour une explication complète des partiels, consultez cette page.

Assurez-vous d'activer les structures partielles pour Message, Channel et Reaction lors de l'instanciation de votre client si vous souhaitez des événements de réaction sur les messages non en cache pour les canaux de serveur et de messages directs. Si vous ne souhaitez pas supporter les canaux de messages directs, vous pouvez exclure Channel.

Si vous utilisez les intents passerelle mais ne pouvez pas ou ne souhaitez pas utiliser l'intent privilégié GuildPresences, vous avez également besoin du partiel User.

old-reactions.js
const { Client, Events, GatewayIntentBits, Partials } = require('discord.js');

const client = new Client({
	intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions],
	partials: [Partials.Message, Partials.Channel, Partials.Reaction],
});

client.on(Events.MessageReactionAdd, async (reaction, user) => {
	// Lors de la réception d'une réaction, vérifiez si la structure est partielle
	if (reaction.partial) {
		// Si le message auquel cette réaction appartient a été supprimé, la récupération peut entraîner une erreur API qui devrait être gérée
		try {
			await reaction.fetch();
		} catch (error) {
			console.error('Une erreur s\'est produite lors de la récupération du message :', error);
			// Revenez car `reaction.message.author` peut être indéfini/null
			return;
		}
	}

	// Maintenant le message a été mis en cache et est complètement disponible
	console.log(`Le message de ${reaction.message.author} "${reaction.message.content}" a gagné une réaction !`);
	// La réaction est maintenant également complètement disponible et les propriétés seront reflétées avec précision :
	console.log(`${reaction.count} utilisateur(s) ont donné la même réaction à ce message !`);
});

Les structures partielles sont activées globalement. Vous ne pouvez pas les faire fonctionner uniquement pour un événement ou un cache spécifique, et vous devrez très probablement adapter d'autres parties de votre code qui accèdent aux données des caches pertinents. Tous les caches contenant le type de structure respectif peuvent également retourner des partiels ! Pour plus d'informations, consultez cette page.