使用Node.js/Discord.js实现Discord机器人解析XML并返回标签内容
Discord机器人XML解析及斜杠命令问题修复
问题背景
需要开发Discord机器人的/lookup斜杠命令:用户选择分类和名称后,机器人读取对应预准备的XML文件,解析标签内容并以Embed形式返回。例如选择spell分类和acid splash名称时,需从对应XML文件读取法术详情并返回。
示例XML文件结构:
<spell> <name>Acid Splash</name> <level>0</level> <school>C</school> <ritual>NO</ritual> <time>1 action</time> <range>60 feet</range> <components>V, S</components> <duration>Instantaneous</duration> <classes>Fighter (Eldritch Knight), Rogue (Arcane Trickster), Sorcerer, Wizard, Artificer</classes> <text>You hurl a bubble of acid. Choose one creature you can see within range, or choose two creatures you can see within range that are within 5 feet of each other. A target must succeed on a Dexterity saving throw or take 1d6 acid damage.</text> <text> This spells damage increases by 1d6 when you reach 5th Level (2d6), 11th level (3d6) and 17th level (4d6).</text> <text/> <text>Source: Player's Handbook p. 211</text> <roll>1d6</roll> </spell>
尝试A:机器人返回undefined
问题现象
代码无报错,控制台能打印测试XML字符串,但机器人返回内容显示undefined。
代码片段
const { SlashCommandBuilder } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('test5') .setDescription('returns the contents of an xml file'), async execute(interaction){ fs = require('fs'); var parser = require('xml2json-light'); fs.readFile( 'xml/a.xml', function(err, data) { // Attempt A // This version doesn't error, but it also doesn't produce a response... var xml = '<person><name>John Doe</name></person>'; console.log("to json ->", xml); interaction.reply("to json ->", xml); // // Attempt B // // This version produces the attached error... // var json = parser.xml2json(data); // console.log("to json ->", data); // interaction.reply("to json ->", data); }); } }
控制台日志
// NOTE: I CAN'T GET ANYTHING AFTER THE -> TO PRODUCE AS A RESPONSE FROM THE BOT BECAUSE IT'S REGARDED AS 'UNDEFINED'... to json -> <person><name>John Doe</name></person>
尝试B:xml2json-light解析触发TypeError
问题现象
调用parser.xml2json(data)时触发错误:TypeError: xmlStr.replace is not a function
报错信息
*************************Smashmouth-2\node_modules\xml2json-light\xml2json.js:67 xmlStr = xmlStr.replace( /<!--[\s\S]*?-->/g, '' ); //remove commented lines ^ TypeError: xmlStr.replace is not a function at cleanXML (C:\Users\snake\OneDrive\Documents\discord\Smashmouth-2\node_modules\xml2json-light\xml2json.js:11:14) at Object.xml2json (C:\Users\snake\OneDrive\Documents\discord\Smashmouth-2\node_modules\xml2json-light\xml2json.js:11:14) at C:\Users\snake\OneDrive\Documents\discord\Smashmouth-2\commands\utility\test5.js:23:23 at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read_file_context:68:3)
问题修复方案
1. 修复尝试A的返回undefined问题
interaction.reply()仅接受单个字符串参数或配置对象,传入两个参数会导致第二个参数被忽略,正确写法是拼接字符串后传入:
// 错误写法 interaction.reply("to json ->", xml); // 正确写法 interaction.reply(`to json -> ${xml}`);
同时建议改用fs.promises.readFile替代回调式读取,避免嵌套并更好适配async函数。
2. 修复尝试B的xml2json-light解析错误
fs.readFile默认返回Buffer对象,而xml2json-light需要字符串格式的XML内容,有两种解决方式:
- 读取时指定编码为
utf8:
fs.readFile('xml/a.xml', 'utf8', function(err, data) { var json = parser.xml2json(data); });
- 将Buffer转换为字符串:
var json = parser.xml2json(data.toString('utf8'));
3. 完整实现代码(含Embed返回)
整合修复后的完整命令代码,实现读取XML、解析并返回Discord Embed:
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js'); const fs = require('fs').promises; const parser = require('xml2json-light'); module.exports = { data: new SlashCommandBuilder() .setName('lookup') .setDescription('查询指定分类的内容') .addStringOption(option => option.setName('category') .setDescription('选择分类(如spell)') .setRequired(true) .addChoices( { name: '法术', value: 'spell' }, // 可添加更多分类选项 )) .addStringOption(option => option.setName('name') .setDescription('输入内容名称(如Acid Splash)') .setRequired(true)), async execute(interaction) { // 延迟回复避免超时 await interaction.deferReply(); const category = interaction.options.getString('category'); const name = interaction.options.getString('name').toLowerCase(); // 构建XML文件路径(实际可根据分类映射到不同文件) const filePath = `xml/${category}.xml`; try { // 读取XML文件(字符串格式) const xmlContent = await fs.readFile(filePath, 'utf8'); // 解析XML为JSON const jsonData = parser.xml2json(xmlContent); const spell = jsonData.spell; if (!spell) { return interaction.editReply(`未找到名称为${name}的${category}内容`); } // 合并多段text内容 let description = ''; if (Array.isArray(spell.text)) { description = spell.text.filter(text => text).join('\n\n'); } else if (spell.text) { description = spell.text; } // 构建Embed const embed = new EmbedBuilder() .setTitle(spell.name) .setColor('#0099ff') .addFields( { name: '等级', value: spell.level, inline: true }, { name: '学派', value: spell.school, inline: true }, { name: '仪式', value: spell.ritual, inline: true }, { name: '施法时间', value: spell.time, inline: true }, { name: '射程', value: spell.range, inline: true }, { name: '组件', value: spell.components, inline: true }, { name: '持续时间', value: spell.duration, inline: true }, { name: '可用职业', value: spell.classes }, { name: '伤害骰', value: spell.roll, inline: true } ) .setDescription(description) .setFooter({ text: '数据来源:预准备XML文件' }); await interaction.editReply({ embeds: [embed] }); } catch (error) { console.error('查询出错:', error); await interaction.editReply('查询过程中发生错误,请检查日志'); } } };
额外优化建议
- 若XML文件包含多个条目,需解析后遍历查找对应名称的内容
- 添加名称模糊匹配功能,提升用户体验
- 缓存已解析的XML数据,避免重复读取文件,加快响应速度
内容的提问来源于stack exchange,提问作者Jake Livsey
相关产品推荐
相关产品推荐

