聊天窗口关键词回复、定时发送、获取手机号发送到服务器

代码逻辑介绍:

isUserTyping记录用户是否输入消息,用setInterval定时50秒检测并改变用户状态为false,发送消息、或者自动打印一轮完毕后,置为true。

sendMessage是处理发送消息的函数;

appendMessage是将消息追加到聊天框的函数;

sendUserDataToServer是将用户信息发送大服务器的函数;

getCurrentTime为时间戳字符串拼接函数,为了格式的统一,建议客户端、服务器(windows开发环境/linux上线)均使用此格式。

preventDefault阻止默认事件,回车时不添加换行。

javascript 复制代码
messageInput.addEventListener('keydown', function (event) {
  if (event.keyCode === 13) { // 回车键
    event.preventDefault();
    sendMessage();
  }
});

以下为全部代码:

javascript 复制代码
document.addEventListener('DOMContentLoaded', function () {
  // 关键词-回复映射表
  const replies = {
    "你好": "你好,请问有什么可以帮助您的?",
    "订单查询": "请提供订单号,我将为您查询订单信息。",
    "产品问题": "请详细描述您遇到的问题,我会尽快帮您解决。",
    "投诉": "抱歉让您不满意,请您提供详细信息,我们将尽快处理。",
    "钱": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
    "价格": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
    "成考": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
    "国家开放大学": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
    "国开": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
    "电大": "您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
    "成人大学": "这边是成人学历提升咨询窗口,目前是报考高峰期,您可以先留下【手机+姓名】,老师给您转发入学申请条件,院校专业,报考流程,优惠信息,费用明细等相关信息。",
    "在的": "请您放心,我们不会透露您的个人隐私,请问您的【手机号】是?方便我们进一步向您介绍",
    "再见": "再见,祝您生活愉快!"
  };
  const over20s = [
    '您好,还在吗?',
    '那我将院校专业、报考条件、报名时间、报考流程和费用明细发您',
    '您先了解看看,报不报名再决定哈,您接收资料的【手机号码或者微信】是?',
    '免费短信,您说下您的【手机号】,建议您看看,是系统发不会打扰您的',
    '是这样的,内容是保存在短信系统的,我复制不了,只是发给您先了解,您了解后有需要再联系我们。',
    '我们是正规机构,不会泄露您的信息,可以放心的,您的【手机号码】是?',
  ];

  // 获取聊天容器、消息输入框和发送按钮元素
  const chatContainer = document.querySelector('.chat_history');
  const messageInput = document.querySelector('.inputWindow');
  const sendButton = document.querySelector('.btn');

  let isUserTyping = false;

  // 监听发送按钮点击事件
  sendButton.addEventListener('click', sendMessage);
  // 监听消息输入框的键盘事件
  messageInput.addEventListener('keydown', function (event) {
    if (event.keyCode === 13) { // 回车键
      event.preventDefault();
      sendMessage();
    }
  });

  // 发送消息的函数
  function sendMessage() {
    isUserTyping = true;
    let message = messageInput.innerHTML.trim();
    const regex = /\s+/g; // \s 匹配任何空白字符,包括空格、制表符、换页符等,+ 表示匹配一个或多个  
    message = message.replace(regex, '');

    if (message === '') {
      return; // 如果消息为空则不处理
    }
    appendMessage('user', message); // 添加用户发送的消息到聊天窗口

    // 检测用户输入的是否是手机号
    if (message.match(/\d{11}/)) {
      let reply = "感谢您留下手机号,我们的专业顾问会尽快与您联系,为您提供详细的咨询服务。";
      appendMessage('agent', reply);
      sendUserDataToServer(message); //将手机号发送到服务器
    } else {
      const reply = getReply(message); // 获取客服回复的消息
      setTimeout(() => { appendMessage('agent', reply); }, 1500); // 延迟一秒后添加客服回复的消息
    }

    messageInput.innerHTML = ''; // 清空输入框
  }

  // 根据用户消息获取客服回复的函数
  function getReply(message) {
    for (const keyword in replies) { // 遍历关键词-回复映射表
      if (message.includes(keyword)) { // 如果用户消息包含关键词
        return replies[keyword]; // 返回对应的回复消息
      }
    }
    return "抱歉,是这样的,内容是保存在短信系统的,我复制不了,只是发给您先了解,您了解后有需要再联系我们。所以您的【电话号码】是?"; // 默认回复消息
  }

  /**
   * 添加消息到聊天窗口的函数
   * chatContainer(.chat_history)
   *    messageDiv(消息框)
   *        avatarImg(头像)
   *        messageContentDiv(消息容器)
   *            timestampSpan(时间戳)
   *            textDiv(文本信息)
   * 
   * @param sender 发送消息的类型,user或者agent
   * @param text 需要添加到聊天窗口的字符串 
   * 
   */
  function appendMessage(sender, text) {
    const messageDiv = document.createElement('div'); // 创建消息容器元素
    messageDiv.classList.add('box-message', sender + '-message');

    const avatarImg = document.createElement('img'); // 创建头像元素
    avatarImg.src = sender === 'user' ? './image/beauty.png' : './image/beauty.png';
    messageDiv.appendChild(avatarImg);

    const messageContentDiv = document.createElement('div'); // 创建消息内容容器元素
    messageContentDiv.classList.add('message-content');
    messageDiv.appendChild(messageContentDiv);

    const timestampSpan = document.createElement('span');
    timestampSpan.classList.add('timestamp');
    timestampSpan.textContent = getCurrentTime();
    messageContentDiv.appendChild(timestampSpan);

    const textDiv = document.createElement('div');
    textDiv.textContent = text;
    messageContentDiv.appendChild(textDiv);

    chatContainer.appendChild(messageDiv);
    chatContainer.scrollTop = chatContainer.scrollHeight;
  }

  /**
   * 如果用户超过20秒没有发送消息,自动打印消息
   */
  let printOver20 = setInterval(fn, 20 * 1000);
  function fn() {
    if (!isUserTyping) {
      clearInterval(printOver20); // 先暂停检测定时器,避免在打印时间内也计入检测时间
      let index = 0;

      // 间歇性定时器是非阻塞的
      const replyPrintIntervalId = setInterval(() => {
        if (isUserTyping) { // 如果用户开始输入,那么停止打印
          console.log("isUserTyping === true");
          clearInterval(replyPrintIntervalId);
        }
        appendMessage('agent', over20s[index++]);

        if (index === over20s.length) { // 停止循环打印
          clearInterval(replyPrintIntervalId);
          console.log('定时器结束...');
        }
      }, 5000);

      setTimeout(() => { // 打印结束后重新开启定时器
        isUserTyping = true; // 循环走完用户输入状态为true
        printOver20 = setInterval(fn, 30 * 1000);
      }, 25 * 1000);
    }
  }

  /**
   * 定时检查用户输入
  */
  setInterval(() => {
    if (messageInput.innerHTML === "" && isUserTyping !== false) {
      isUserTyping = false;
    }
  }, 50 * 1000)

  function getCurrentTime() {
    const date = new Date(); // 获取当前时间
    const year = date.getFullYear();
    const month = `${date.getMonth() + 1}`.padStart(2, '0'); // 月份是从0开始的,所以要加1  
    const day = `${date.getDate()}`.padStart(2, '0');
    const hours = date.getHours();
    const minutes = `${date.getMinutes()}`.padStart(2, '0');
    const seconds = `${date.getSeconds()}`.padStart(2, '0');

    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  }

  // 发送用户信息到服务器端
  function sendUserDataToServer(userData) {
    const nowTime = getCurrentTime();
    // 将用户对象转换为JSON字符串
    let userDataString = {
      phone: `${userData}`
    };
    userDataString = JSON.stringify(userDataString);
    console.log(userDataString);
    console.log("This is in typeof ---", typeof userDataString);

    // 发送POST请求到服务器  
    fetch('http://xxx.xx.xx.xxx:3333', {
      mode: 'no-cors',
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
      },
      body: userDataString,
    })
      .then(response => {
        if (!response.ok) {
          // throw new Error('Network response was not ok');
          console.log('then')
        }
        return response.json();
      })
      .then(data => {
        console.log('Success:', data);
        // 这里可以添加其他成功处理逻辑,比如更新UI等  

      })
      .catch(error => {
        // console.error('Error:', error);
        console.log('catch')
        // 这里可以添加错误处理逻辑,比如显示错误消息等  
      });
  }
});

代码中有三个定时器:

一个定时器负责间隔5s打印一次;

一个定时器负责每隔25检测一下用户是否输入;

一个定时器负责50s检查一下将用户输入状态变为false。

相关推荐
musk12127 分钟前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘35 分钟前
js代码09
开发语言·javascript·ecmascript
万少1 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL1 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl021 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang1 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景2 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼2 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
蓝婷儿2 小时前
每天一个前端小知识 Day 21 - 浏览器兼容性与 Polyfill 策略
前端
百锦再2 小时前
Vue中对象赋值问题:对象引用被保留,仅部分属性被覆盖
前端·javascript·vue.js·vue·web·reactive·ref