基于wechaty实现聊天机器人(微信自动回复)

news2024/11/26 17:23:30

最终效果 返回前端一个二维码,扫码登录后,机器人将上线,根据后端node内代码实现自动回复等功能

初始化项目

npm init -y

安装 wechaty 核心包

npm i wechaty  wechaty-puppet  wechaty-puppet-padlocal

安装 qrcode-terminal 用于终端输出二维码

npm i qrcode-terminal

安装 express 实现接口

npm i express

创建bot.js 实现机器人

  • 注意 注释掉的 PuppetPadlocal 是可以传token的 申请地址http://pad-local.com/ 免费的7天
    这里注释掉是 需要登录的微信是17年之前注册的才行
const {
  dingDongBot,
  getMessagePayload,
  LOGPRE,
} = require("./helper");
const { log, ScanStatus, WechatyBuilder } = require("wechaty");

//const { PuppetPadlocal } = require("wechaty-puppet-padlocal");

/****************************************
 * 去掉注释,可以完全打开调试日志
 ****************************************/
// log.level("silly");

// const puppet = new PuppetPadlocal({
//   token: "自己申请的token",
// });

async function runBot() {
  return new Promise((resolve, reject) => {
    const bot = WechatyBuilder.build({
      name: "叮当猫机器",
      //puppet
    });

    bot.start().then(() => {
      log.info(LOGPRE, "机器开始启动.");
    });
    bot
      .on("scan", (qrcode, status) => {
        if (status === ScanStatus.Waiting && qrcode) {
          console.log('qrcode',qrcode)
          const qrcodeImageUrl = [
            "https://wechaty.js.org/qrcode/",
            encodeURIComponent(qrcode),
          ].join("");
          resolve(qrcode)
          log.info(
            LOGPRE,
            `扫码: ${ScanStatus[status]}(${status}) - ${qrcodeImageUrl}`
          );

          require("qrcode-terminal").generate(qrcode, { small: true });
        } else {
          log.info(LOGPRE, `扫码状态: ${ScanStatus[status]}(${status})`);
        }
      })

      .on("login", (user) => {
        log.info(LOGPRE, `${user} 登录成功`);
      })

      .on("logout", (user, reason) => {
        log.info(LOGPRE, `${user} 登出, 原因: ${reason}`);
      })

      .on("message", async (message) => {
        log.info(LOGPRE, `收到消息: ${message.toString()}`);

        await getMessagePayload(message);

        await dingDongBot(message);
      })

      .on("room-invite", async (roomInvitation) => {
        //处理邀请加入群聊
        log.info(LOGPRE, `on room-invite: ${roomInvitation}`);
      })

      .on("room-join", (room, inviteeList, inviter, date) => {
        //当有新成员加入一个群聊时,Wechaty会触发room-join事件
        log.info(
          LOGPRE,
          `新成员加入群聊, room:${room}, inviteeList:${inviteeList}, inviter:${inviter}, date:${date}`
        );
      })

      .on("room-leave", (room, leaverList, remover, date) => {
        //当有成员离开一个群聊时,Wechaty会触发room-leave事件
        log.info(
          LOGPRE,
          `有成员离开群聊, room:${room}, leaverList:${leaverList}, remover:${remover}, date:${date}`
        );
      })

      .on("room-topic", (room, newTopic, oldTopic, changer, date) => {
        //当一个群聊的名称发生变更时
        log.info(
          LOGPRE,
          `群聊的名称发生变更, room:${room}, newTopic:${newTopic}, oldTopic:${oldTopic}, changer:${changer}, date:${date}`
        );
      })

      .on("friendship", async (friendship) => {
        //friendship事件用于处理好友添加请求和确认的事件
        log.info(LOGPRE, `on friendship: ${friendship}`);
        /* if (friendship.type() === bot.Friendship.Type.Receive) {
        // 收到好友添加请求
        await friendship.accept();
        console.log(`接受了来自 ${friendship.contact().name()} 的好友添加请求`);
      } else if (friendship.type() === bot.Friendship.Type.Confirm) {
        // 好友添加请求已确认
        console.log(`已成为好友:${friendship.contact().name()}`);
      } */
      })

      .on("error", (error) => {
        log.error(LOGPRE, `程序产生错误: ${error}`);
      });
  });
}

module.exports = {
  runBot,
};

创建辅助的工具helper.js

const { log, Message } = require("wechaty");
const PUPPET = require("wechaty-puppet");
const dayjs = require("dayjs");
const fs = require("fs");

const LOGPRE = "[终端打印日志]";

async function getMessagePayload(message) {
  switch (message.type()) {
    case PUPPET.types.Message.Text:
      log.silly(LOGPRE, `get message text: ${message.text()}`);
      break;

    case PUPPET.types.Message.Attachment:
    case PUPPET.types.Message.Audio: {
      const attachFile = await message.toFileBox();

      const dataBuffer = await attachFile.toBuffer();

      log.info(LOGPRE, `get message audio or attach: ${dataBuffer.length}`);

      break;
    }

    case PUPPET.types.Message.Video: {
      const videoFile = await message.toFileBox();

      const videoData = await videoFile.toBuffer();

      log.info(LOGPRE, `get message video: ${videoData.length}`);

      break;
    }

    case PUPPET.types.Message.Emoticon: {
      const emotionFile = await message.toFileBox();

      const emotionJSON = emotionFile.toJSON();
      log.info(
        LOGPRE,
        `get message emotion json: ${JSON.stringify(emotionJSON)}`
      );

      const emotionBuffer = await emotionFile.toBuffer();

      log.info(LOGPRE, `get message emotion: ${emotionBuffer.length}`);

      break;
    }

    case PUPPET.types.Message.Image: {
      const messageImage = await message.toImage();

      const thumbImage = await messageImage.thumbnail();
      const thumbImageData = await thumbImage.toBuffer();

      log.info(LOGPRE, `get message image, thumb: ${thumbImageData.length}`);

      const hdImage = await messageImage.hd();
      const hdImageData = await hdImage.toBuffer();

      log.info(LOGPRE, `get message image, hd: ${hdImageData.length}`);

      const artworkImage = await messageImage.artwork();
      const artworkImageData = await artworkImage.toBuffer();

      log.info(
        LOGPRE,
        `get message image, artwork: ${artworkImageData.length}`
      );

      break;
    }

    case PUPPET.types.Message.Url: {
      const urlLink = await message.toUrlLink();
      log.info(LOGPRE, `get message url: ${JSON.stringify(urlLink)}`);

      const urlThumbImage = await message.toFileBox();
      const urlThumbImageData = await urlThumbImage.toBuffer();

      log.info(LOGPRE, `get message url thumb: ${urlThumbImageData.length}`);

      break;
    }

    case PUPPET.types.Message.MiniProgram: {
      const miniProgram = await message.toMiniProgram();

      log.info(LOGPRE, `MiniProgramPayload: ${JSON.stringify(miniProgram)}`);

      break;
    }
  }
}

async function dingDongBot(message) {
  if (message.to()?.self() && message.text().indexOf("ding") !== -1) {
    await message.talker().say(message.text().replace("ding", "dong"));
  }
}


module.exports = {
  dingDongBot,
  getMessagePayload,
  LOGPRE,
};

实现入口文件 app.js

const { LOGPRE } = require("./helper");
const { runBot } = require("./bot");
const express = require("express");
const app = express();
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  next();
});
let qrcode = "";
// 返回登录二维码的接口
app.get("/qrcode", (req, res) => {
  if (qrcode) {
    // 返回二维码地址给前端
    res.json({ qrcode });
  } else {
    res.status(404).json({ error: "QR Code not found" });
  }
});

app.listen(3000, async () => {
  console.log(LOGPRE, "http://localhost:3000/qrcode");
  qrcode = (await runBot()) || "";
});

启动项目

node app.js

最终目录结构

在这里插入图片描述
效果:
在这里插入图片描述
前端访问效果
在这里插入图片描述
前端就是拿到接口返回的url,使用QRCode插件进行生成二维码即可

前端生成二维码

插件地址 https://www.npmjs.com/package/qrcode
vue示例

<template>
  <div>
    <canvas id="canvas"></canvas>
  </div>
</template>

<script>
import QRCode from "qrcode";
import axios from "axios";
export default {
  name: "QRCode",
  mounted() {
    this.getQr();
  },
  methods: {
    getQr() {
      axios("http://localhost:3000/qrcode").then((res) => {
        console.log(res, "res");
        const canvas = document.getElementById("canvas");
        QRCode.toCanvas(canvas, res.data.qrcode, function (error) {
          if (error) console.error(error);
          console.log("success!");
        });
      });
    },
  },
};
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1182634.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【GO】项目import第三方的依赖包

目录 一、导入第三方包 1.执行命令 2.查看go环境变量参数 3.查看go.mod文件的变化情况 二、程序里如何import 1. import依赖包 2. 程序编写 本次学习go如果依赖第三方的包&#xff0c;并根据第三方的包提供的接口进行编程&#xff0c;这里需要使用go get命令。下面将go…

【IP固定】地平线开发板如何实现重启IP地址不变

文章目录 1 背景2 临时解决方案3 真正解决方案 1 背景 重新刷了地平线工具链OE包中BSP20230417的系统镜像&#xff0c;结果只能串口连接&#xff0c;无法实现网口连接&#xff0c;串口连接后&#xff0c;发现eth0和eth1的IP竟然是一样的&#xff0c;如下图所示 还挺少见的。 …

基于单片机的多层电梯控制仿真系统

**单片机设计介绍&#xff0c; 基于单片机的多层电梯控制仿真系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的多层电梯控制仿真系统是一个复杂的系统&#xff0c;它需要结合单片机技术、控制理论、电子技术以及人…

C# 继承,抽象,接口,泛型约束,扩展方法

文章目录 前言模拟需求场景模拟重复性高的需求初始类结构继承优化抽象类 需求1&#xff1a;打印CreateTime方法1&#xff1a;使用重载方法2&#xff1a;基类函数方法3&#xff1a;泛型约束方法3.1&#xff1a;普通泛型方法方法3.2&#xff1a;高级泛型约束&#xff0c;扩展方法…

Linux cat命令

连接文件并打印输出到标准输出设备。cat 命令可以用来显示文本文件的内容&#xff08;类似于 DOS 下的 type 命令&#xff09;&#xff0c;也可以把几个文件内容附加到另一个文件中&#xff0c;即连接合并文件。 关于此命令&#xff0c;有人认为写 cat 命令的人是因为喜欢猫&am…

canal 同时监听两个数据库实例

我有两个数据库实例 test和test2 复制一个example 并修改为example2 修改canal.properties文件 找到 destinations 修改为监听example和example2实例 把canal.destinations example 修改为 canal.destinations example,example2 在example目录中修改实例配置文件 instance…

java版小程序商城免费搭建-直播商城平台规划及常见的营销模式有哪些?电商源码/小程序/三级分销

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

centos7安装docker容器

卸载老版本&#xff1a; $ sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine/var/lib/docker/路径下存在镜像、数据卷、容器等&#xff0c;在卸载的时候是不会自动删除…

JAVA字符串格式化——String.format()的使用

引言 String类的format()方法用于创建格式化的字符串以及连接多个字符串对象。熟悉C语言应该记得C语言的sprintf()方法&#xff0c;两者有类似之处。format()方法有两种重载形式。 重载 // 使用当前本地区域对象&#xff08;Locale.getDefault()&#xff09;&#xff0c;制定…

软件测试面试题之测试基础,想轻松应对面试,看完这篇就够了

软件测试的流程是什么&#xff1f;&#xff08;测试流程&#xff09; &#xff08;1&#xff09;需求调查&#xff1a;全面了解系统概况、应用领域、软件开发周期、软件开发环境、开发组织、时间安排、功能需求、性能需求、质量需求及测试要求等。根据系统概况进行项目所需的人…

【计算机网络】物理层知识

目录 1、物理层的基本概念 2、数据通信的基础知识 2.1、数据通信系统模型 2.2、信道的几个基本概念 3、物理层下面的传输媒体 4、信道复用技术 1、物理层的基本概念 物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的 传输媒…

数据标注工具【LabelImg】安装使用 用VOC制作自己的数据集

labelImg的安装 ⭐️LabelImg简介⭐️LabelImg的安装⭐️labelImg标注数据集⭐️利用VOC制作自己的数据集 ⭐️LabelImg简介 Labelimg是一款开源的数据标注工具&#xff0c;标签可用于分类和目标检测&#xff0c;它是用python写的&#xff0c;并使用Qt作为其图形界面&#xf…

漏刻有时百度地图API实战开发(2)文本标签显示和隐藏的切换开关

项目说明 在百度地图开发的过程中&#xff0c;如果遇见大数据量POI标注展示或在最佳视野展示时&#xff0c;没有文本标签&#xff0c;会不清楚具体标注的代表的意义&#xff1b;如果同时显示大量的文本标签&#xff0c;又会导致界面杂乱且无法清晰查看&#xff0c;因此&#x…

Leetcode-160 相交链表

双指针&#xff1a;遍历两次 a重bb重a /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ public class Solution {public ListNode getInter…

Vue3:一页多题答案提示及循环radio和checkbox混合使用

一页多题&#xff0c;类型包括单选&#xff08;单选、判断&#xff09;和多选&#xff0c;radio和checkbox混合使用&#xff0c;答案检验数据匹配&#xff0c;正确答案格式化&#xff0c;答案提交数据格式化&#xff0c;数据提交。 效果&#xff1a; 数据获取&#xff1a; 数据…

伊朗黑客对以色列科技和教育领域发起破坏性网络攻击

导语 近期&#xff0c;以色列的高等教育和科技领域遭受了一系列破坏性的网络攻击。这些攻击始于2023年1月&#xff0c;旨在部署以前未记录的数据清除恶意软件。在最近的攻击中&#xff0c;攻击者试图窃取个人身份信息和知识产权等敏感数据。本文将介绍这些攻击的具体细节&#…

实施电子采购的6个有效步骤

耗时又费力&#xff0c;手动采购之苦相信大家都受够了&#xff0c;现在越来越多的企业正在实施电子采购策略。根据CIPS的《2022年采购与供应数字化报告》&#xff0c;多达95%的企业在采购与供应商管理中采用了技术。 但采用技术并不能保证立竿见影的效果。企业需要制定好电子采…

word统计全部字符数。

问题描述&#xff1a;在投稿SCI论文时&#xff0c;有时会要求提交一个highlight文档&#xff0c;要求不超过85个字符。 具体如下&#xff1a;maximum 85 characters per bullet point including spaces 这里的字符不单单包括字母和汉字&#xff0c;还包括标点和空格键。那么如…

Linux 安装 Nginx 并将其配置为系统服务(详细图文)

目录 前言安装 Nginx安装依赖项下载Nginx解压Nginx编译和安装防火墙设置启动Nginx 配置 Nginx 为系统服务配置 Nginx 服务文件启动 Nginx 服务设置开机自启动检查 Nginx 状态停止 Nginx 服务重启 Nginx 服务 卸载 Nginx结语 前言 Nginx是一款卓越的高性能Web服务器&#xff0c…

【Unity细节】Json序列化时出现:An item with the same key has already been added. Key:

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…