Discord Bot添加Reaction按钮更新MySQL数据时遇Unknown Emoji错误
Discord Bot问题:Reaction报错与功能实现需求
问题描述
我开发的Discord Bot从MySQL拉取数据并发送Embed消息,尝试添加Reaction功能更新数据库时,一直触发DiscordAPIError[10014]: Unknown Emoji错误;就算只发送Embed消息,也会在消息发送前报错。需要实现:单命令发送多条Embed消息,且通过Reaction按钮更新MySQL数据库。
报错信息
throw new DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData); ^ DiscordAPIError[10014]: Unknown Emoji at SequentialHandler.runRequest (D:\BotDc\node_modules\@discordjs\rest\dist\index.js:667:15) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async SequentialHandler.queueRequest (D:\BotDc\node_modules\@discordjs\rest\dist\index.js:464:14) at async REST.request (D:\BotDc\node_modules\@discordjs\rest\dist\index.js:910:22) at async MessageManager.react (D:\BotDc\node_modules\discord.js\src\managers\MessageManager.js:242:5) at async Message.react (D:\BotDc\node_modules\discord.js\src\structures\Message.js:760:5) at async Query.onResult (D:\BotDc\index.js:48:11) { rawError: { message: 'Unknown Emoji', code: 10014 }, code: 10014, status: 400, method: 'PUT', url: 'https://discord.com/api/v10/channels/745223942976962590/messages/1056802702929186836/reactions/1%3F%3F/@me' }
现有代码
const Discord = require('discord.js'); const fs = require('fs'); const path = require('path'); const mysql = require('mysql2'); const { token } = require('./config.json'); const client = new Discord.Client({ intents: [Discord.GatewayIntentBits.Guilds] }); const connection = mysql.createConnection({ host: 'localhost', user: 'root', password: '', database: 'continue_web' }); connection.connect((err) => { if (err) throw err; console.log('Connected to MySQL database'); }); client.once(Discord.Events.ClientReady, c => { client.commands = new Discord.Collection(); client.on(Discord.Events.InteractionCreate, async interaction => { if (!interaction.isChatInputCommand()) return; if (interaction.commandName === 'ping') { connection.query("SELECT * , CASE sex WHEN 1 THEN 'Laki-Laki' WHEN 2 THEN 'Perempuan' END AS gender_text FROM accounts WHERE gauth_status='pending'", async (error, results) => { if (error) throw error; for (const result of results) { const exampleEmbed = new Discord.EmbedBuilder() .setColor(0x0099FF) .setTitle(result.nickname) .setURL('https://discord.js.org/') .setAuthor({ name: result.nickname , iconURL: 'https://continue-rp.my.id/forum/data/avatars/m/0/1.jpg?1671348397', url: 'https://discord.js.org' }) .setDescription('Request Character Story') .addFields({ name: 'Story:', value: result.ucpmail , inline: false }) .addFields({ name: 'Umur:', value: result.age + ' Tahun' , inline: true }) .addFields({ name: 'Gender:', value: result.gender_text , inline: true }) .addFields({ name: 'Actions:', value: ':one: Approve\n:two: Reject', inline: true }) .setTimestamp() .setFooter({ text: 'Character Dibuat:'+ '\n'+ result.create_date , iconURL: 'https://continue-rp.my.id/forum/data/avatars/m/0/1.jpg?1671348397' }); await interaction.channel.send({ embeds: [exampleEmbed] }); } }); } }); console.log(`Ready! Logged in as ${c.user.tag}`); }); client.login(token);
解决方案
1. 修复Unknown Emoji错误
报错原因是添加Reaction时使用了:one:这类Discord表情别名,而Discord API需要原生表情字符或自定义表情ID。对于原生数字表情,直接用1️⃣、2️⃣这类字符即可。发送Embed后需保存消息对象,再调用react()方法添加反应。
2. 补充必要的Intents
要监听Reaction事件,必须添加GuildMessageReactions权限,若需处理消息内容可补充MessageContent,修改客户端初始化代码:
const client = new Discord.Client({ intents: [ Discord.GatewayIntentBits.Guilds, Discord.GatewayIntentBits.GuildMessageReactions, Discord.GatewayIntentBits.MessageContent ] });
3. 关联Reaction与数据库记录
用Collection存储消息ID和数据库记录ID的映射,确保点击Reaction时能找到对应的数据库条目。也可将记录ID嵌入Embed的footer中作为标识。
4. 监听Reaction事件更新数据库
添加messageReactionAdd事件监听,判断用户点击的表情,执行对应的数据库更新操作,并同步修改Embed状态。
修改后的完整代码
const Discord = require('discord.js'); const mysql = require('mysql2'); const { token } = require('./config.json'); // 补充必要的Intents const client = new Discord.Client({ intents: [ Discord.GatewayIntentBits.Guilds, Discord.GatewayIntentBits.GuildMessageReactions, Discord.GatewayIntentBits.MessageContent ] }); const connection = mysql.createConnection({ host: 'localhost', user: 'root', password: '', database: 'continue_web' }); connection.connect((err) => { if (err) throw err; console.log('Connected to MySQL database'); }); // 存储消息ID与数据库account ID的映射 const messageAccountMap = new Discord.Collection(); client.once(Discord.Events.ClientReady, c => { console.log(`Ready! Logged in as ${c.user.tag}`); client.on(Discord.Events.InteractionCreate, async interaction => { if (!interaction.isChatInputCommand()) return; if (interaction.commandName === 'ping') { // 先回复用户避免命令超时 await interaction.deferReply({ ephemeral: true }); connection.query("SELECT * , CASE sex WHEN 1 THEN 'Laki-Laki' WHEN 2 THEN 'Perempuan' END AS gender_text FROM accounts WHERE gauth_status='pending'", async (error, results) => { if (error) { console.error(error); return interaction.editReply('查询数据库出错'); } if (results.length === 0) { return interaction.editReply('没有待处理的请求'); } for (const result of results) { const exampleEmbed = new Discord.EmbedBuilder() .setColor(0x0099FF) .setTitle(result.nickname) .setAuthor({ name: result.nickname , iconURL: 'https://continue-rp.my.id/forum/data/avatars/m/0/1.jpg?1671348397' }) .setDescription('Request Character Story') .addFields( { name: 'Story:', value: result.ucpmail , inline: false }, { name: 'Umur:', value: `${result.age} Tahun` , inline: true }, { name: 'Gender:', value: result.gender_text , inline: true }, { name: 'Actions:', value: '1️⃣ Approve\n2️⃣ Reject', inline: true } ) .setTimestamp() .setFooter({ text: `Character Dibuat: ${result.create_date} | ID: ${result.id}`, iconURL: 'https://continue-rp.my.id/forum/data/avatars/m/0/1.jpg?1671348397' }); // 发送消息并保存对象 const sentMessage = await interaction.channel.send({ embeds: [exampleEmbed] }); // 存储映射关系 messageAccountMap.set(sentMessage.id, result.id); // 添加反应 await sentMessage.react('1️⃣'); await sentMessage.react('2️⃣'); } await interaction.editReply(`已发送 ${results.length} 条待处理请求`); }); } }); // 监听Reaction添加事件 client.on(Discord.Events.MessageReactionAdd, async (reaction, user) => { // 忽略机器人自身的反应 if (user.bot) return; // 获取对应的account ID const accountId = messageAccountMap.get(reaction.message.id); if (!accountId) return; // 判断点击的表情 let newStatus; if (reaction.emoji.name === '1️⃣') { newStatus = 'approved'; } else if (reaction.emoji.name === '2️⃣') { newStatus = 'rejected'; } else { return; // 忽略其他表情 } // 更新数据库 connection.query('UPDATE accounts SET gauth_status = ? WHERE id = ?', [newStatus, accountId], (error, results) => { if (error) { console.error(error); user.send(`更新ID为${accountId}的记录时出错`).catch(console.error); return; } // 更新Embed状态 const embed = reaction.message.embeds[0]; const updatedEmbed = Discord.EmbedBuilder.from(embed) .setColor(newStatus === 'approved' ? 0x00FF00 : 0xFF0000) .addFields({ name: 'Status:', value: newStatus.charAt(0).toUpperCase() + newStatus.slice(1), inline: true }); reaction.message.edit({ embeds: [updatedEmbed] }); // 移除映射避免重复处理 messageAccountMap.delete(reaction.message.id); // 移除所有反应 reaction.message.reactions.removeAll().catch(console.error); user.send(`已成功将ID为${accountId}的请求设置为${newStatus}`).catch(console.error); }); }); }); client.login(token);
关键说明
- 用
interaction.deferReply()避免命令超时,因为查询数据库和发送多条消息可能耗时较长。 messageAccountMap确保Reaction操作能精准对应到数据库条目。- 处理完Reaction后更新Embed状态、移除反应和映射,防止重复操作。
内容的提问来源于stack exchange,提问作者Nanang Ma'ruf
相关产品推荐
相关产品推荐

