基于WhatsApp ID实现Twilio-Node.js多用户聊天机器人会话隔离
解决WhatsApp多用户会话混乱问题
你的问题核心是用了全局变量(isViewingProducts、isReportingIssue、userData)存储会话状态,这些变量会被所有用户共享,导致多用户交互时互相干扰。要实现单用户独立会话,必须把每个用户的会话状态和其WhatsApp ID绑定,存在用户专属的存储中(比如数据库的User文档)。
具体修改方案
1. 扩展User模型(messageSchema.js)
给User文档添加会话状态字段,用来存储每个用户的独立会话信息:
const mongoose = require('mongoose'); const messageSchema = new mongoose.Schema({ smsMessageSid: String, numMedia: String, profileName: String, smsSid: String, smsStatus: String, body: String, to: String, numSegments: String, referralNumMedia: String, messageSid: String, accountSid: String, from: String, apiVersion: String, }); const userSchema = new mongoose.Schema({ waId: { type: String, unique: true }, messages: [messageSchema], // 添加会话状态字段 sessionState: { isViewingProducts: { type: Boolean, default: false }, isReportingIssue: { type: Boolean, default: false }, userData: { type: Object, default: {} } } }); module.exports = mongoose.model('User', userSchema);
2. 修改主路由代码,移除全局变量
把原来的全局变量替换为从User文档中读取/更新的状态,确保每个用户的状态独立:
const express = require('express') const router = express.Router() const session = require('express-session'); const mongoose = require('mongoose'); const { MessagingResponse } = require('twilio').twiml; const User = require('./models/messageSchema') const mongooseURI = 'mongodb+srv://rishavjhavinayan:RVtzjjTUSf11MB98@cluster0.ct9hcdc.mongodb.net/' mongoose.connect(mongooseURI); const db = mongoose.connection; router.use(session({ secret: 'secret_key', resave: false, saveUninitialized: true })); const products = [ { name: "Product 1", description: "Description of Product 1" }, { name: "Product 2", description: "Description of Product 2" }, { name: "Product 3", description: "Description of Product 3" }, { name: "Product 4", description: "Description of Product 4" } ]; const showProducts = () => { let response = "---- Available Products ----\n\n"; products.forEach((product, index) => { response += `${index + 1}. ${product.name}\n`; }); return response; } const getProductDetails = (index) => { if (index >= 1 && index <= products.length) { const product = products[index - 1]; return `--- ${product.name} Details ---\n\n${product.description}`; } else { return "Invalid product index."; } }; // 修改reportIssue,接收并返回更新后的userData和状态 const reportIssue = (userMessage, userData) => { const updatedData = { ...userData }; let response = ""; let isReportingCompleted = false; if (!updatedData.name) { updatedData.name = userMessage; response = "Thanks for providing your name. Please provide your email address:"; } else if (!updatedData.email) { updatedData.email = userMessage; response = "Thanks for providing your email address. Please describe the issue you're experiencing:"; } else if (!updatedData.issue) { updatedData.issue = userMessage; response = "Issue Submitted. Thank you for reporting!"; isReportingCompleted = true; } return { response, updatedData, isReportingCompleted }; }; const showDefaultMessage = () => { return ` 💥 Welcome to Vinayan India 🙏 --- All --- 1 - See our Products 2 - Report Issue 3 - Talk to our sales team ` } const showHelpDesk = () => { return ` 🙏 Thanks for contacting Vinayayan India Help Desk You can contact this number for any help regarding our products: 8527988811 ` } const footer = ` Created With ❤️ by Vinayan India (vinayan.net) ` router.post('/', async function (req, res, next) { const twiml = new MessagingResponse(); const userMessage = req.body.Body.trim(); const waId = req.body.From; try { // 获取或创建用户 let user = await User.findOne({ waId }); if (!user) { user = new User({ waId }); } // 保存用户消息到数据库 user.messages.push({ smsMessageSid: req.body.SmsMessageSid, numMedia: req.body.NumMedia, profileName: req.body.ProfileName, smsSid: req.body.SmsSid, smsStatus: req.body.SmsStatus, body: req.body.Body, to: req.body.To, numSegments: req.body.NumSegments, referralNumMedia: req.body.ReferralNumMedia, messageSid: req.body.MessageSid, accountSid: req.body.AccountSid, from: req.body.From, apiVersion: req.body.ApiVersion, }); let response = null; // 从用户文档获取当前会话状态 const { isViewingProducts, isReportingIssue, userData } = user.sessionState; if (isViewingProducts) { const index = parseInt(userMessage); response = getProductDetails(index); // 更新会话状态:退出查看产品模式 user.sessionState.isViewingProducts = false; } else if (isReportingIssue) { // 处理问题上报,获取更新后的状态和响应 const issueResult = reportIssue(userMessage, userData); response = issueResult.response; user.sessionState.userData = issueResult.updatedData; // 如果上报完成,切换状态 if (issueResult.isReportingCompleted) { user.sessionState.isReportingIssue = false; user.sessionState.userData = {}; // 清空上报数据 } } else { switch (userMessage.toLowerCase()) { case "1": response = showProducts(); // 更新会话状态:进入查看产品模式 user.sessionState.isViewingProducts = true; break; case "2": // 重置上报数据并切换状态 user.sessionState.userData = {}; user.sessionState.isReportingIssue = true; response = "Please provide your name to report the issue:"; break; case "3": response = showHelpDesk(); break; default: response = showDefaultMessage(); } } // 保存机器人回复到数据库 user.messages.push({ body: response, from: 'Your Application' }); // 保存更新后的用户数据(包含会话状态) await user.save(); twiml.message(response + '\n' + footer); res.status(200).send(twiml.toString()); } catch (error) { next(error); } }); module.exports = router;
关键修改点说明
- 移除全局状态变量:不再用全局变量存储会话状态,避免多用户共享
- 会话状态绑定到用户:把
isViewingProducts、isReportingIssue、userData存在每个User文档的sessionState字段中,每个用户的状态完全独立 - 状态持久化:会话状态存在数据库中,即使服务器重启,用户的会话进度也不会丢失
- 函数无状态化:修改
reportIssue函数,改为接收当前用户数据并返回更新后的数据,避免依赖全局变量
这样修改后,每个用户的会话流程完全独立,不会再出现互相干扰的问题。
内容的提问来源于stack exchange,提问作者Rishav jha
相关产品推荐
相关产品推荐

