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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:25:58