You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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)

如果后续机器人用户增多,对话数据量大,建议用专业数据库,性能更优,适合大规模数据管理。

额外修复建议

  1. 原代码中possibleMeals数组缺少闭合的],已在上面的代码中修复。
  2. 原代码中const fetch = import('node-fetch')是动态导入,在CommonJS环境下改为const fetch = require('node-fetch'),避免fetch成为Promise对象无法直接使用。

内容的提问来源于stack exchange,提问作者kenny555

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 09:33:15