如何让discord.js机器人重启后保留对话记忆?
Discord机器人对话记忆持久化问题解决方案
问题说明
我和朋友用discord.js开发了一款能根据输入生成消息的机器人,但有个问题:机器人关闭重启后会丢失之前的对话记忆。尝试添加Node.js的I/O功能但没成功,想知道是操作有误还是需要其他方案,以下是我的代码:
const Discord = require("discord.js"); const { Client, Intents, MessageEmbed } = require('discord.js'); const client = new Client({ intents: [ Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MEMBERS, Intents.FLAGS.GUILD_BANS, Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS, Intents.FLAGS.GUILD_INTEGRATIONS, Intents.FLAGS.GUILD_WEBHOOKS, Intents.FLAGS.GUILD_INVITES, Intents.FLAGS.GUILD_VOICE_STATES, Intents.FLAGS.GUILD_PRESENCES, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MESSAGE_REACTIONS, Intents.FLAGS.GUILD_MESSAGE_TYPING, Intents.FLAGS.DIRECT_MESSAGES, Intents.FLAGS.DIRECT_MESSAGE_REACTIONS, Intents.FLAGS.DIRECT_MESSAGE_TYPING ] }); const TOKEN = "" const fetch = require('node-fetch') const possibleAnswers = ['Im sad :(', 'Im annoyed', 'Im happy!','Im okay'] const possibleMeals = ["Eat some fruit!","Eat some icecream!","Eat a salad!","Eat some spahgetti!","Go out to a restaurant of your choice"] const possibleGreetings =["Hi","Hello","What is up?","Wassup","Greetings!"] const possibleCodeInfo = [ "Arrays indexes start at 0 in most languages, but some start at 1, like Julia!", "Else if statements allow for mutliple conditions!", "Many AI's require a database during its creation!", "Lua is a frontend and backend language!", "C# is 22 years old!", "People used to use punchcards to code!", "Objects store data in properties!", "React allows you to write javascript in HTML!", "HTML is not a coding language, its a markup language!", "Unity uses C# for its engine!" ] let chain = {} function generateChain(str){ const textArr = str.split(' ') let x = textArr.indexOf("<@998859763385389137>") if (x != -1) textArr.splice(x, 1) for (let i = 0; i < textArr.length; i++) { let word = textArr[i].toLowerCase().replace(/[\W_]/, "") if (!chain[word]) chain[word] = [] if (textArr[i + 1]) chain[word].push(textArr[i + 1].toLowerCase().replace(/[\W_]/, "")) } } function generateSentence(chain){ const words = Object.keys(chain) let word = words[Math.floor(Math.random() * words.length)] let output = '' for (let i = 0; i < words.length; i++){ output += word + ' ' word = words[Math.floor(Math.random() * words.length)] if (!word || !chain.hasOwnProperty(word)) { word = words[Math.floor(Math.random() * words.length)] } } return output } client.on("ready", () => { console.log(`Logged in as ${client.user.tag}!`) // slash command stuff const guildId = '847395379019513876' const guild = client.guilds.cache.get(guildId) let commands if (guild) { commands = guild.commands } else { commands = client.application?.commands } }) client.on("message", msg => { if (msg.mentions.users.first() == client.user) { generateChain(msg.content) msg.reply(generateSentence(chain)) } switch (msg.content){ case('how are you?'): msg.reply(possibleAnswers[Math.floor(Math.random()*possibleAnswers.length)]); break; case('what should i eat?'): msg.reply(possibleMeals[Math.floor(Math.random()*possibleMeals.length)]); break; case('hi'): msg.reply(possibleGreetings[Math.floor(Math.random()*possibleGreetings.length)]); break; case('tell me a code fact'): msg.reply(possibleCodeInfo[Math.floor(Math.random()*possibleCodeInfo.length)]); break; //end break; } }) client.on('interactionCreate', async (interaction) => { if(!interaction.isCommand()) return; }) client.login(TOKEN)
问题原因
你的chain对象存储在内存中,机器人重启后内存会被清空,所以之前的对话记忆全部丢失。要保留数据,必须把chain持久化到文件或数据库中。
解决方法
方案一:用Node.js原生fs模块实现文件持久化
这是最直接的方案,把chain数据保存为JSON文件,启动时读取,更新后写入。
步骤1:引入依赖
在代码顶部添加:
const fs = require('fs'); const path = require('path'); // 定义保存对话记忆的文件路径 const CHAIN_FILE = path.join(__dirname, 'conversation-chain.json');
步骤2:初始化时读取数据
替换原来的let chain = {}为:
let chain = {}; // 启动时读取保存的对话数据 try { const savedData = fs.readFileSync(CHAIN_FILE, 'utf8'); chain = JSON.parse(savedData); } catch (err) { // 文件不存在或读取失败,初始化空对象 console.log('未找到历史对话数据,初始化空记忆'); }
步骤3:更新后保存数据
修改generateChain函数,每次更新chain后写入文件:
function generateChain(str){ const textArr = str.split(' ') let x = textArr.indexOf("<@998859763385389137>") if (x != -1) textArr.splice(x, 1) for (let i = 0; i < textArr.length; i++) { let word = textArr[i].toLowerCase().replace(/[\W_]/, "") if (!chain[word]) chain[word] = [] if (textArr[i + 1]) chain[word].push(textArr[i + 1].toLowerCase().replace(/[\W_]/, "")) } // 写入文件保存更新后的记忆 try { fs.writeFileSync(CHAIN_FILE, JSON.stringify(chain, null, 2)); } catch (err) { console.error('保存对话记忆失败:', err); } }
步骤4:退出时确保数据保存
添加退出信号监听,防止强制退出时丢失数据:
// 监听机器人退出信号,保存数据 process.on('SIGINT', () => { try { fs.writeFileSync(CHAIN_FILE, JSON.stringify(chain, null, 2)); console.log('对话记忆已保存,机器人退出'); process.exit(0); } catch (err) { console.error('退出时保存对话记忆失败:', err); process.exit(1); } });
方案二:用轻量级数据库(如lowdb)
如果觉得原生fs操作麻烦,可以用lowdb,它是基于JSON文件的简单数据库,API更友好。
安装依赖:
npm install lowdb
使用示例:
const { Low, JSONFile } = require('lowdb'); const path = require('path'); // 初始化数据库 const adapter = new JSONFile(path.join(__dirname, 'db.json')); const db = new Low(adapter); // 启动时读取数据 async function initDB() { await db.read(); db.data = db.data || { chain: {} }; await db.write(); } // 在ready事件中初始化数据库 client.on("ready", async () => { console.log(`Logged in as ${client.user.tag}!`) await initDB(); // 其他代码... }); // 修改generateChain函数 function generateChain(str){ const textArr = str.split(' ') let x = textArr.indexOf("<@998859763385389137>") if (x != -1) textArr.splice(x, 1) const chain = db.data.chain; for (let i = 0; i < textArr.length; i++) { let word = textArr[i].toLowerCase().replace(/[\W_]/, "") if (!chain[word]) chain[word] = [] if (textArr[i + 1]) chain[word].push(textArr[i + 1].toLowerCase().replace(/[\W_]/, "")) } // 保存到数据库 db.write(); }
方案三:用专业数据库(如SQLite、MongoDB)
如果后续机器人用户增多,对话数据量大,建议用专业数据库,性能更优,适合大规模数据管理。
额外修复建议
- 原代码中
possibleMeals数组缺少闭合的],已在上面的代码中修复。 - 原代码中
const fetch = import('node-fetch')是动态导入,在CommonJS环境下改为const fetch = require('node-fetch'),避免fetch成为Promise对象无法直接使用。
内容的提问来源于stack exchange,提问作者kenny555
相关产品推荐
相关产品推荐

