Wechaty|NodeJS基於wechaty-puppet-hostie協議手擼一個企業級微信...

2021-01-09 番茄學前端

需求背景

目前所在企業是一家創新型汽車後市場網際網路科技有限公司,拓展汽車後市場B2C、B2B和O2O業務。

前期打造連結線下6家自營大型汽車專業維修中心,12家自營汽車配件、汽保設備銷售中心,3000餘家加盟汽修、汽配企業及10餘萬個人客戶的服務平臺;

由於客戶維繫和供應商諮詢等都是基於微信群聊的。每天要在成百上千的群聊中提供服務。需要實時在群內產品報價,車架號識別,圖片識別,關鍵字反饋。數據推送等等功能。這樣的需求情況下,人力成本是巨大的。

而我作為一名前端開發工程師,平時也喜歡寫技術博客和交朋友,為此我也創建了微信技術交流群和微信公眾號,一般我都會在文章下面貼出公眾號和我的個人二維碼,給有興趣的小夥伴們添加微信然後我再拉他們進群這些,但是不停的同意微信好友驗證,再發送群邀請真的是太痛苦了,相信很多做公眾號的小夥伴都和我一樣,作為一名開發,這種重複勞動是絕對不能忍受的基於這種情況和公司業務情況,調研發現了並了解到了wechaty,發現其提供的功能能夠覆蓋到企業和個人微信,並且能夠自己定製化開發符合自己需求的功能。

介紹

Wechaty 是什麼

微信個人號功能非常強大和靈活,是一個非常適合用來做ChatBot的載體。它可以靈活不受限制的發送語音簡訊、視頻、圖片和文字,支持多人群聊。但是使用微信個人微信號作為ChatBot,需要通過非官方的第三方庫接入微信。因為截至2018年底,微信尚無任何官方的ChatBot API發布。

Wechaty 是一個開源的的對話機器人 SDK,支持 個人號 微信。它是一個使用Typescript 構建的Node.js 應用。支持多種微信接入方案,包括網頁,ipad,ios,windows, android 等。同時支持Linux, Windows, Darwin(OSX/Mac) 和 Docker 多個平臺。

在GitHub上可以找到很多支持微信個人號接入的第三方類庫,其中大多都是基於Web Wechat的API來實現的,如基於Python的WeixinBot,基於Node.js的Wechaty等。少數支持非Web協議的庫,大多是商業私有閉源的,Wechaty是少有的開源項目支持非Web協議的類庫。

只需要6行代碼,你就可以 通過個人號 搭建一個 微信機器人功能 ,用來自動管理微信消息。

import { Wechaty } from'wechaty'Wechaty.instance().on('scan', qrcode => console.log('掃碼登錄:' + qrcode)).on('login', user => console.log('登錄成功:' + user)).on('message', message => console.log('收到消息:' + message)).on('friendship', friendship => console.log('收到好友請求:' + friendship)).on('room-invite', invitation => console.log('收到入群邀請:' + invitation)).start()更多功能包括

消息處理:關鍵詞回復群管理:自動入群,拉人,踢人自動處理好友請求智能對話:通過簡單配置,即可加入智能對話系統,完成指定任務... 請自行開腦洞所有你能想到的交互模式。在微信上都有實現的可能。每日定時拉取天氣預報。

每天給你心愛的人發送早安和晚安信息。

什麼成語接龍啦。快問快答等等功能

當然wechaty的功能服務並不是免費的

200/月的費用,如果你是個人開發可能會斟酌一二。但是你可以通過社區申請一個長達15天的免費token來嘗試使用和開發一個小型機器人,從而決定你是否需要購買使用。

關於申請的地址我放在了這裡Wechaty Token 申請及使用文檔和常見問題

基於wechaty-puppet-hostie開發企業級微信機器人

目錄結構

├── config│ └── index.js // 配置文件├── package.json├── service│ ├── bot-service│ │ ├── error-service.js│ │ ├── friendship-service.js│ │ ├── heartbeat-service.js│ │ ├── login-service│ │ │ ├── function-service.js│ │ │ └── index.js│ │ ├── logout-service.js│ │ ├── message-service│ │ │ ├── function-service.js│ │ │ └── index.js│ │ ├── ready-service│ │ │ ├── function-service.js│ │ │ └── index.js│ │ ├── room-invite-service.js│ │ ├── room-join-service.js│ │ ├── room-leave-service.js│ │ ├── room-topic-service.js│ │ └── scan-service│ │ └── index.js│ ├── common-service│ │ ├── chatbot-service.js│ │ ├── ding-service.js│ │ └── oss-service.js│ └── redis-service│ └── index.js├── src│ └── main.js // 入口├── store│ └── index.js // 全局存儲對象├── utils│ ├── oss.js // 阿里雲oss認證│ └── redis.js // redis認證登錄└── yarn.locksrc/main.js

const { Wechaty } = require('wechaty') // 機器人木偶const { onScan } = require("../service/bot-service/scan-service") // 當機器人需要掃碼登陸的時候會觸發這個事件。const { onLogin } = require("../service/bot-service/login-service") // 當機器人成功登陸後,會觸發事件,並會在事件中傳遞當前登陸機器人的信息const { onLogout } = require("../service/bot-service/logout-service") // 當機器人檢測到登出的時候,會觸發事件,並會在事件中傳遞機器人的信息。const { onReady } = require("../service/bot-service/ready-service") // 當所有數據加載完成後,會觸發這個事件。在wechaty-puppet-padchat 中,它意味著已經加載完成Contact 和Room 的信息。const { onMessage } = require("../service/bot-service/message-service") // 當機器人收到消息的時候會觸發這個事件。const { onRoomInvite } = require("../service/bot-service/room-invite-service") // 當收到群邀請的時候,會觸發這個事件。const { onRoomTopic } = require("../service/bot-service/room-topic-service") // 當有人修改群名稱的時候會觸發這個事件。const { onRoomJoin } = require("../service/bot-service/room-join-service") // 當有人進入微信群的時候會觸發這個事件。機器人主動進入某個微信群,那個樣會觸發這個事件。const { onRoomleave } = require("../service/bot-service/room-leave-service") // 當機器人把群裡某個用戶移出群聊的時候會觸發這個時間。用戶主動退群是無法檢測到的。const { onFriendship } = require("../service/bot-service/friendship-service") // 當有人給機器人發好友請求的時候會觸發這個事件。const { onHeartbeat } = require('../service/bot-service/heartbeat-service') // 獲取機器人的心跳。const { onError } = require('../service/bot-service/error-service') // 當機器人內部出錯的時候會觸發error 事件。const { wechatyToken } = require('../config/index') // 機器人tokenconst { globalData } = require('../store/index') // 全局對象globalData.bot = new Wechaty({puppet: 'wechaty-puppet-hostie',puppetOptions: {token: wechatyToken }});globalData.bot .on('scan', onScan) .on('login', onLogin) .on('logout', onLogout) .on('ready', onReady) .on('message', onMessage) .on('room-invite', onRoomInvite) .on('room-topic', onRoomTopic) .on('room-join', onRoomJoin) .on('room-leave', onRoomleave) .on('friendship', onFriendship) .on('heartbeat', onHeartbeat) .on('error', onError) .start()具體功能實現及代碼

掃碼登錄通過node啟動後,觸發onScan事件,將登錄二維碼列印在控制臺,掃碼登錄const QrcodeTerminal = require('qrcode-terminal');const { ScanStatus } = require('wechaty-puppet')/** * @method onScan 當機器人需要掃碼登陸的時候會觸發這個事件。 建議你安裝 qrcode-terminal(運行 npm install qrcode-terminal) 這個包,這樣你可以在命令行中直接看到二維碼。 * @param {*}qrcode * @param {*}status */const onScan = async (qrcode, status) => {try {if (status === ScanStatus.Waiting) {console.log(`========================二維碼狀態:${status}========================\n\n`) QrcodeTerminal.generate(qrcode, {small: true }) } } catch (error) {console.log('onScan', error) }}module.exports = { onScan }登錄成功掃碼登錄成功後會觸發onLogin事件,通過事件接收到登錄信息和機器人信息,通過釘釘接口將登錄通知發送至釘釘群內const { notificationLoginInformation } = require('../../common-service/ding-service')const { updateBotInfo } = require('./function-service')const { globalData } = require('../../../store/index')/** * @method onLogin 當機器人成功登陸後,會觸發事件,並會在事件中傳遞當前登陸機器人的信息 * @param {*}botInfo */const onLogin = async botInfo => {try {console.log('======================== onLogin ========================\n\n')console.log(`機器人信息:${JSON.stringify(botInfo)}\n\n`)console.log(` // \\ // \\ // ##DDDDDDDDDDDDDDDDDDDDDD## ## DDDDDDDDDDDDDDDDDDDD ## ## DDDDDDDDDDDDDDDDDDDD ## ## hh hh ## ## ## ## ## ## ## ## ## ### ## #### ## ## hh // \\ hh ## ## ## ## ## ## ## ## ## ## hh // \\ hh ## ## ## ## ## ## ## ## ## ## hh hh ## ## ## ## ## ## ## ## ## ## ## hh wwww hh ## ## ## ## ## ## ## ## #### ## hh hh ## ## ## ## ## ## ## ## ## ## ## ### ## ## ### ## MMMMMMMMMMMMMMMMMMMM ## ##MMMMMMMMMMMMMMMMMMMMMM## 微信機器人名為: [${botInfo.payload.name}] 已經掃碼登錄成功了。\n\n `)// 全局存儲機器人信息 globalData.botPayload = botInfo.payload// 更新機器人列表 updateBotInfo()// 機器人登錄登出提醒/通知釘釘接口 notificationLoginInformation(true) } catch (error) {console.log(`onLogin: ${error}`) }}module.exports = { onLogin }— 機器人異常退出當node服務異常終端,或者手機上退出機器人登錄後會觸發onLogout事件,同樣釘釘群內通知信息,並且銷毀程序中運行的一些定時器等

const { notificationLoginInformation } = require('../common-service//ding-service')const { globalData } = require('../../store/index')/** * @method onLogout 當機器人檢測到登出的時候,會觸發事件,並會在事件中傳遞機器人的信息。 * @param {*}botInfo */const onLogout = async botInfo => {try {console.log('======================== onLogout ========================')console.log(`當bot檢測到註銷時,將與當前登錄用戶的聯繫人發出註銷。`)// 全局存儲機器人信息 globalData.botPayload = botInfo.payloadconst { updateRoomInfoTimer, chatbotSayQueueTimer } = globalData// 機器人退出清空定時器if (updateRoomInfoTimer) { clearTimeout(updateRoomInfoTimer) }if (chatbotSayQueueTimer) { clearInterval(chatbotSayQueueTimer) }// 機器人登錄登出提醒/通知釘釘接口 notificationLoginInformation(false) } catch (error) {console.log(`error in onLogout:${error}`) }}module.exports = { onLogout }消息接收處理當微信接收到新的消息時候會觸發onMessage事件,通過事件內對消息的判斷,群內消息還是私聊消息等做出不同的邏輯處理。從而實現業務需求。部分代碼如下const dayjs = require('dayjs');const { say } = require('../../common-service/chatbot-service')const { isCanSay, roomIdentifyVin, rooImageIdentifyVin, contactIdentifyVin, contactImageIdentifyVin, messageProcessing, storageRoomMessage, storageContactMessage,} = require('./function-service')const { roomMessageFeedback, contactMessageFeedback} = require('../../common-service/ding-service')const { globalData } = require('../../../store/index');const { Message } = require('wechaty');/** * @method onMessage 當機器人收到消息的時候會觸發這個事件。 * @param {*} message */const onMessage = async message => {try {console.log('======================== onMessage ========================\n\n')// 機器人信息const { botPayload } = globalData// 獲取發送消息的聯繫人const contact = message.from()// 獲取消息所在的微信群,如果這條消息不在微信群中,會返回null const room = message.room()// 查看這條消息是否為機器人發送的const isSelf = message.self()// 處理消息內容const text = await messageProcessing(message)// console.log(`======================== 處理消息後內容為:${text} ========================\n\n`)// 消息為空不處理if (!text) return// 消息詳情const messagePayload = message.payload// console.log(`======================== 消息詳情:${JSON.stringify(messagePayload)} ========================\n\n`)// 消息聯繫人詳情const contactPayload = contact.payload// console.log(`======================== 消息聯繫人詳情:${JSON.stringify(contactPayload)} ========================\n\n`)// 群消息if (room) {console.log(`======================== 群聊消息 ========================\n\n`)// 房間詳情const roomPayload = room.payload// console.log(`======================== 房間詳情:${JSON.stringify(roomPayload)} ========================\n\n`)console.log(`消息時間:${dayjs(messagePayload.timestamp).format('YYYY-MM-DD HH:mm:ss')}\n\n群聊名稱:${roomPayload.topic}\n\n微信名稱:${contactPayload.name}\n\n微信類型:${contactPayload.type}\n\n備註暱稱:${contactPayload.alias}\n\n消息內容:${text}\n\n消息類型:${messagePayload.type}\n\n`);// 存儲消息內容const storeMessage = {category: 'room', // 消息類別,room為群聊消息, contact為個人消息。isSelf: isSelf, // 是否自己發送的消息messageId: messagePayload.id, // 消息idmessageToId: messagePayload.toId, // 消息接收者idmessageType: messagePayload.type, // 消息類型messageText: text, // 消息內容messageTimestamp: messagePayload.timestamp, // 消息時間戳messageTime: dayjs(messagePayload.timestamp).format('YYYY-MM-DD HH:mm:ss'), // 消息時間roomOwnerId: roomPayload.ownerId, // 群聊群主idroomTopic: roomPayload.topic, // 群聊名稱roomId: roomPayload.id, // 群聊名稱contactId: contactPayload.id, // 消息發送者idcontactName: contactPayload.name, // 消息發送者暱稱contactType: contactPayload.type, // 消息發送者類型contactAvatar: contactPayload.avatar, // 消息發送者頭像contactAlias: contactPayload.alias, // 消息發送者備註 }// redis中存儲群聊消息內容 storageRoomMessage(roomPayload, storeMessage)// 消息是機器人自己發的 或者 消息發送到wechaty接收間隔大於半個小時if (isSelf || message.age() > 1800) return// 判斷當前機器人在群中是否可以說話 if (!await isCanSay(roomPayload)) return// 有人@我const isMentionSelf = await message.mentionSelf()// 如果有人@我if (isMentionSelf) {console.log('this message were mentioned me! [You were mentioned] tip ([有人@我]的提示)')// 獲取消息內容,拿到整個消息文本,去掉 @名字const sendText = text.replace("@" + botPayload.name, "")if (sendText == '圖片') {// 通過不同的關鍵字進行業務處理邏輯 say({messageType: 6,sender: room,messageInfo: {fromUrl: 'https://wework.qpic.cn/wwhead/nMl9ssowtibVGyrmvBiaibzDqfABIxIv4ic0FyQRxgQVpUIaqlePxrDW8L2qPB3WnHSZclgQ1D1QhDs/0' } })return }if (sendText == '分享') {// 通過不同的關鍵字進行業務處理邏輯 say({messageType: 14,sender: room,messageInfo: {urlLink: {description: 'WeChat Bot SDK for Individual Account, Powered by TypeScript, Docker, and Love',thumbnailUrl: 'https://avatars0.githubusercontent.com/u/25162437?s=200&v=4',title: 'Welcome to Wechaty',url: 'https://github.com/wechaty/wechaty', } } })return }if (sendText == '名片') { say({messageType: 3,sender: room,messageInfo: {contactCard: 'contactId' } })return }if (sendText == '小程序') { say({messageType: 9,sender: room,messageInfo: {miniProgram: {appid: 'xxxxx',title: '我正在使用Authing認證身份,你也來試試吧',pagePath: 'pages/home/home.html',description: '身份管家',thumbUrl: '30590201000452305002010002041092541302033d0af802040b30feb602045df0c2c5042b777875706c6f61645f31373533353339353230344063686174726f6f6d3131355f313537363035393538390204010400030201000400',thumbKey: '42f8609e62817ae45cf7d8fefb532e83', } } })return }// 群裡正常說話 } else {// 重構機器人功能測試群if (roomPayload.id !== 'R:10696051737544800') {console.log(`======================== 不是測試群消息,不處理群內業務邏輯 ========================\n\n`)return }if (/^[a-zA-Z0-9]{17}$/.test(text)) {// 群聊識別vin反饋 roomIdentifyVin(room, contact, message, text)return }// 群內消息以[弱]開頭的信息為反饋信息。反饋給後臺if (text.indexOf('[弱]') === 0) {// 群消息反饋 roomMessageFeedback(room, contact, text)return }// 如果是圖片,則圖片解析vinif (messagePayload.type === Message.Type.Image) {// 群聊圖片識別vin反饋 rooImageIdentifyVin(room, contact, message) } }// 私聊消息 } else {console.log(`======================== 私聊消息 ========================\n\n`)console.log(`消息時間:${dayjs(messagePayload.timestamp).format('YYYY-MM-DD HH:mm:ss')}\n\n微信名稱:${contactPayload.name}\n\n微信類型:${contactPayload.type}\n\n備註暱稱:${contactPayload.alias}\n\n消息內容:${text}\n\n消息類型:${messagePayload.type}\n\n`);// 存儲消息內容const storeMessage = {category: 'contact', // 消息類別,room為群聊消息, contact為個人消息。isSelf: isSelf, // 是否自己發送的消息messageId: messagePayload.id, // 消息idmessageToId: messagePayload.toId, // 消息接收者idmessageType: messagePayload.type, // 消息類型messageText: text, // 消息內容messageTimestamp: messagePayload.timestamp, // 消息時間戳messageTime: dayjs(messagePayload.timestamp).format('YYYY-MM-DD HH:mm:ss'), // 消息時間contactId: contactPayload.id, // 消息發送者idcontactName: contactPayload.name, // 消息發送者暱稱contactType: contactPayload.type, // 消息發送者類型contactAvatar: contactPayload.avatar, // 消息發送者頭像contactAlias: contactPayload.alias, // 消息發送者備註 }// redis中存儲私聊消息內容 storageContactMessage(contact, storeMessage)// 消息是機器人自己發的 或者 消息發送到wechaty接收間隔大於半個小時if (isSelf || message.age() > 1800) returnif (/^[a-zA-Z0-9]{17}$/.test(text)) {// 私聊識別vin反饋 contactIdentifyVin(contact, message, text)return }// 私聊消息以[弱]開頭的信息為反饋信息。反饋給後臺if (text.indexOf('[弱]') === 0) {// 私聊消息反饋 contactMessageFeedback(contact, text)return }// 如果是圖片,則圖片解析vinif (messagePayload.type === Message.Type.Image) {// 私聊圖片解析vin反饋 contactImageIdentifyVin(contact, message) } } } catch (error) {console.log(`onMessage:${error}`) }}module.exports = { onMessage }至於其他的一些生命周期以及鉤子函數。大家可以參考文檔做出屬於自己的應用機器人

封裝say方法

因為say()方法會在多處調用,並且要根據不同的消息類型發送的內容做出不同的數據處理。大家以後也會遇到,因此這裡將我封裝的一個say方法展示給大家用於參考

const { MiniProgram, UrlLink, FileBox } = require('wechaty')const dayjs = require('dayjs');const { DelayQueueExector } = require('rx-queue');const { redisHexists, redisHset, redisHget, redisSet, redisLpush } = require('../redis-service/index')const { globalData } = require('../../store/index')const delay = new DelayQueueExector(10000);/** * @method say 機器人發送消息 * @param {*} messageType 消息類型 7文本,1文件,6圖片,3個人名片,14卡片連結 9小程序 * @param {*} sender 來源 房間 || 個人 實例對象 * @param {*} messageInfo 內容 *//** * messageInfo 數據結構 * tetx: string 文本消息必傳 * fileUrl: string 文件消息必傳 * imageUr: string 圖片消息必傳 * cardId: string 個人名片消息必傳 * linkInfo: object 卡片消息必傳 * description: string 描述 * thumbnailUrl: string 縮略圖地址 * title: string 標題 * url: string 跳轉地址 */asyncfunctionsay({ messageType, sender, messageInfo }) {// console.log(messageType);// console.log(sender);// console.log(messageInfo);try {returnnewPromise(async (resolve, reject) => {// 機器人信息const { bot } = globalData// 枚舉消息類型const MessageType = {text: 7, // 文本fromFile: 1, // 文件fromUrl: 6, // 圖片contactCard: 3, // 個人名片urlLink: 14, // 卡片連結miniProgram: 9, // 小程序 }// 內容不存在if (!messageInfo) {return }// 要發送的消息內容let contentswitch (messageType) {// 文本 7case MessageType.text: content = messageInfo.textbreak;// 文件 1case MessageType.fromFile: content = FileBox.fromFile(messageInfo.fromFile)break;// 圖片 6case MessageType.fromUrl: content = FileBox.fromUrl(messageInfo.fromUrl)break;// 個人名片 3case MessageType.contactCard: content = await bot.Contact.load('1688853777824721')break;// 連結 14case MessageType.urlLink: content = new UrlLink({description: 'WeChat Bot SDK for Individual Account, Powered by TypeScript, Docker, and Love',thumbnailUrl: 'https://avatars0.githubusercontent.com/u/25162437?s=200&v=4',title: 'Welcome to Wechaty',url: 'https://github.com/wechaty/wechaty', })break;// 小程序 9case MessageType.miniProgram: content = new MiniProgram({appid: 'wx60090841b63b6250',title: '我正在使用Authing認證身份,你也來試試吧',pagePath: 'pages/home/home.html',description: '身份管家',thumbUrl: '30590201000452305002010002041092541302033d0af802040b30feb602045df0c2c5042b777875706c6f61645f31373533353339353230344063686174726f6f6d3131355f313537363035393538390204010400030201000400',thumbKey: '42f8609e62817ae45cf7d8fefb532e83', });break;default:break; } delay.execute(async () => { sender.say(content) .then(value => {console.log(`======================== 機器人回復 ========================\n\n`) resolve() }) .catch(reason => {console.log(`======================== 機器人發送消息失敗 ========================\n\n`, reason) }) }) }) } catch (error) {console.log('error in say', error); }}module.exports = { say}對了,對於onMessage事件中消息格式的判斷我也做了一層封裝,這裡給大家提供參考。

/** * @method messageProcessing 處理消息內容 * @param {*}message */asyncfunctionmessageProcessing(message) {try {returnnewPromise(async (resolve, reject) => {console.log(`======================== messageProcessing ========================\n\n`)// 消息詳情const messagePayload = message.payload// 獲取消息的文本內容。 let text = message.text()/** * Unknown: 0, Attachment: 1, Audio: 2, Contact: 3, ChatHistory: 4, Emoticon: 5, Image: 6, Text: 7, Location: 8, MiniProgram: 9, GroupNote: 10, Transfer: 11, RedEnvelope: 12, Recalled: 13, Url: 14, Video: 15 */// 消息類型switch (messagePayload.type) {// 附件 0case Message.Type.Unknown:console.log(`======================== 消息類型為未知消息:${messagePayload.type} ========================\n\n`) text = '[你收到一個未知消息,請在手機上查看]'break;// 附件 1case Message.Type.Attachment:console.log(`======================== 消息類型為附件:${messagePayload.type} ========================\n\n`)// 暫時不知道怎麼處理 text = '[你收到一個附件,請在手機上查看]'break;// 音頻 2case Message.Type.Audio:console.log(`======================== 消息類型為音頻:${messagePayload.type} ========================\n\n`) text = '[你收到一條語音消息,請在手機上查看]'break;// 個人名片 3case Message.Type.Contact:console.log(`======================== 消息類型為個人名片:${messagePayload.type} ========================\n\n`) text = '[你收到一張個人名片,請在手機上查看]'break;// 聊天記錄 4case Message.Type.ChatHistory:console.log(`======================== 消息類型為聊天記錄:${messagePayload.type} ========================\n\n`) text = '[你收到聊天記錄,請在手機上查看]'break;// 表情符號 5case Message.Type.Emoticon:console.log(`======================== 消息類型為表情符號:${messagePayload.type} ========================\n\n`) text = '[你收到表情符號,請在手機上查看]'// 暫時不知道怎麼處理break;// 圖片 6case Message.Type.Image:console.log(`======================== 消息類型為圖片:${messagePayload.type} ========================\n\n`)// 上傳圖片至阿里雲獲取圖片地址 text = await addImageOss(message)break;// 文本 7case Message.Type.Text:console.log(`======================== 消息類型為文本:${messagePayload.type} ========================\n\n`)// 去空格換行 text = text.replace(/\s+/g, '')break;// 位置 8case Message.Type.Location:console.log(`======================== 消息類型為位置:${messagePayload.type} ========================\n\n`) text = '[你收到一條圖片消息,請在手機上查看]'break;// 小程序 9case Message.Type.MiniProgram:console.log(`======================== 消息類型為小程序:${messagePayload.type} ========================\n\n`) text = '[你收到一個小程序消息,請在手機上查看]'break;// GroupNote 10case Message.Type.GroupNote:console.log(`======================== 消息類型為GroupNote:${messagePayload.type} ========================\n\n`) text = '[你收到一個GroupNote,請在手機上查看]'break;// Transfer 11case Message.Type.Transfer:console.log(`======================== 消息類型為Transfer:${messagePayload.type} ========================\n\n`) text = '[你收到一個Transfer,請在手機上查看]'break;// 紅包 12case Message.Type.RedEnvelope:console.log(`======================== 消息類型為紅包:${messagePayload.type} ========================\n\n`) text = '[你收到一個紅包,請在手機上查看]'break;// Recalled 13case Message.Type.Recalled:console.log(`======================== 消息類型為Recalled:${messagePayload.type} ========================\n\n`) text = '[你收到一個Recalled,請在手機上查看]'break;// 連結地址 14case Message.Type.Url:console.log(`======================== 消息類型為連結地址:${messagePayload.type} ========================\n\n`)// 暫時不知道怎麼處理 text = '[你收到一條連結消息,請在手機上查看]'break;// 視頻 15case Message.Type.Video:console.log(`======================== 消息類型為視頻:${messagePayload.type} ========================\n\n`) text = '[你收到一個視頻消息,請在手機上查看]'break;default: text = ''break; } resolve(text) }) } catch (error) {console.log('error in messageProcessing', error); }}為什麼這樣做一層封裝處理,是因為我們的業務需求要將聊天內容進行redis和mysql數據存儲。方便以後數據訂正和查詢使用。

實現的功能

基於wechaty我們實現的功能有那些呢?

— 根據關鍵詞,輸入車輛 VIN 對應反饋出車型配件信息,並且將公司所在群區域的店鋪配件庫存信息反饋出來

根據圖片識別車輛 VIN,然後識別 VIN 對應反饋出車型配件信息,並且將公司所在群區域的店鋪配件庫存信息反饋出來,圖片解釋接口採用的百度的接口關鍵詞指令綁定群信息。根據不同指令進行群配置。redis 存儲機器人信息。將群信息存儲並同步在 redis 和 mysql 中。後臺配置對應群是否開啟某些功能等等。每月月初定時發送每個群的採購信息。銷量信息等等。機器人登錄調用釘釘接口,在釘釘群內發布機器人登錄或者退出的提醒信息群邀請自動通過,入群以後做出相應數據存儲邏輯判斷功能設置等好友申請自動通過,關鍵字申請自動邀請入不同的群,功能覆蓋等等等等功能。以上功能支持還在不斷開發和摸索中。但已經滿足目前我們的業務需求。最後

你如果想用我這些東西,拉下代碼config.js裡換下token和一些配置信息就可以,當然我在不停更新,功能會越來越多,所以倉庫中代碼和文中會有些不一樣,使用時簡單看下代碼,都寫了詳細注釋,也很簡單,但是因為代碼中很多地方涉及到來企業敏感信息。我只好重新寫來一份最小可執行的demo,大家僅供參考。

看完幫個忙

如果你覺得這篇內容對你挺有啟發,我想邀請你幫我個小忙:

點讚,讓更多的人也能看到這篇內容(收藏不點讚,都是耍流氓 -_-)關注公眾號「番茄學前端」,我會定時更新和發布前端相關信息和項目案例經驗供你參考。加個好友, 雖然幫不上你大忙,但是一些業務問題大家可以探討交流。

相關焦點

  • 實用口語:「擼貓」的英語是什麼
    「貓」的英語單詞大家肯定都知道,但你知道如何用英語來說「擼貓」嗎?   每天擼貓Pat : (喜愛,友善地)輕拍Stroke : 特指輕撫動物的毛皮   2. Are you a Dog person or Cat Person?   你是養狗一族還是養貓一族?鏟屎官可以用Cat person表達哦   3.
  • 盤古團隊證實:微信小遊戲「跳一跳」改分漏洞仍在,這是最新攻略 |...
    ,微信小遊戲「跳一跳」改分漏洞仍在,此前流傳的「微信已補漏洞」是指已經修補了微信小遊戲「跳一跳」的原始碼下載漏洞。(2)獲取session idhttps配置完畢後,打開微信的跳一跳小程序,就可以看到抓包歷史有一個帶有session id的請求:https://mp.weixin.qq.com/wxagame/wxagame_init
  • 深信服發布企業級分布式存儲aStor-EDS
    在大會主論壇上,深信服副總裁陳彥彬隆重發布了深信服2019年春季重量級新品:企業級分布式存儲 aStor-EDS,標誌著深信服在雲計算領域又添一款強勢產品。  作為深信服雲計算業務版圖新成員,深信服企業級分布式存儲 aStor-EDS 集合 AI 人工智慧技術,通過分布式架構構建雲存儲服務資源。本次共發布兩個型號的分布式存儲一體機,以及可部署在 X86 伺服器上的 EDS 軟體。
  • 我的世界:假如遊戲裡的樹木不能用手擼了,我們該如何發展下去
    其中有一個還能讓史蒂夫失去徒手砍樹的技能!1.無法擼掉的樹在我的世界的玩家群體之間有這麼一句話「要想富,先擼樹」,其實這句話描述的就是玩家們進遊戲之後要做的第一件事情,往往就是去手擼一棵樹,然後開始製作工具臺和各種各樣的工具,然而最近我在網絡上看到了一個玩家的實況卻讓我懵了圈,不知道他玩的是什麼mod,居然無法直接用手去打掉木塊,如果你執意去打它的話你的屏幕會不停晃動,就好像是受傷時候一樣的效果。
  • 鯊魚寶寶手偶毛絨玩具
    Baby shark 鯊魚手偶產品尺寸:24cm*15cm*12cm產品重量:125g產品包裝:opp袋產品顏色:黃色 粉色 藍色產品功能:手偶互動(無音樂默認版)增值功能:手偶互動(帶音樂單曲baby shark)定製功能:手偶互動(三首歌曲可切換英文版)電池規格:LR44*3 可更換電池外箱尺寸:57*57*63cm外箱重量:100pcs 14kg(無音樂)16KG(有音樂
  • 微信企業號:「連接」的革命
    自媒體人士陽淼認為,微信企業號的推出將給整個基於網際網路的企業級軟體行業帶來巨大的契機,因為包括SAAS、PAAS甚至IAAS等在內的平臺服務產品,可以通過與微信實現連接的方式,利用微信簡潔、高效和便利的產品平臺為企業及員工提供更加便利的服務。而一些質疑者則認為微信企業號將給行業帶來一定的衝擊,將使得一些CRM、SAAS等企業軟體失去生存的機會。
  • LOL無限火力站擼型英雄排名 站擼哪個英雄最強
    一個比較冷門的英雄,但是不妨礙她的無情站擼,無限火力原地罰站王之一。  很笨比的一個英雄,但是站擼值Max,沒什麼特別怕的英雄,就怕不和你打的英雄,容易被無限火力幾大毒瘤盧錫安,ez,vn等手長靈活的射手秀爛。為數不多可以靈活一點的方式就是狼頭、疾跑、藍色兩大移速天賦、正義榮耀、貓咪等等移速來提升靈活度。
  • 主人擼鳥擼得正爽,隔壁貓咪一個動作讓主人笑噴
    主人擼鳥擼得正爽,隔壁貓咪一個動作讓主人笑噴現在很多人都會養一隻貓在家裡解悶。而最近有一個網友家就發生了一件趣事。網友家裡養了一隻很可愛的狸花貓,可是網友害怕貓咪一隻貓在家太孤獨。於是網友又買來一隻小鸚鵡來給貓咪作伴。
  • 高階享受,物超所值的體驗——擼擼杯SUCKE吸吮飛機杯體驗分享
    今天帶來的是一款相對比較高階的產品——擼擼杯SUCKE吸吮飛機杯,價格是399元。到了這個價位,與常規飛機杯一個很重要的差別就是自動。橫向比較,自動功能的飛機杯價格都在400+往上了,日本的產品價格更貴。當然,對於很多人來說,這個價位會略超一點心理預期,那麼,所謂自動化的體驗究竟如何呢,能不能做到物超所值呢?
  • 美國男付150美元去擼黑豹,一進籠就被扯掉頭皮撕裂耳朵!
    微信的規則進行了調整 希望大家看完故事多點「在看」,喜歡的話也點個分享和贊 這樣事兒君的推送才能繼續出現在你的訂閱列表裡
  • 揭秘企業號:微信商業化的下一站
    微信企業號、服務號、訂閱號的區別微信開放平臺業務部企業應用中心負責人孫劍暉說:「企業號基於一個成熟的生態構建企業自己的生態,這個生態的基礎是企業的用戶會不會在企業級市場形成寡頭壟斷?一家企業號上的第三方服務公司暢移信息負責人胡瑛對極客公園說:做企業號的 30 多個人團隊其實是張小龍養的一支「夢想團隊」,因為基於企業號帶來的真實商業場景是有著巨大的潛力和價值,不單單提升了微信活躍度,也讓微信有了商業價值。胡瑛所說的「商業價值」可以理解為豐富的生態競爭。
  • 紫光發布企業級SSD P8260硬碟:3D快閃記憶體、主控、內存均為紫光產品……
    強大硬體與高效固件之間的完美結合,可以為用戶提供多樣化、差異化的高性能、高可靠性、高穩定性特質的企業級SSD。該產品在安全存儲領域具有較強競爭力,能夠針對不同用戶的需求提供理想的定製化選擇。目前,紫光P8260提供1TB/2TB兩種容量,以及AIC和U.2兩種產品形態接口。
  • 基於網絡協議仿真軟體的實驗教學系統的建設
    某高校計算機學院基於網絡協議仿真軟體的實驗教學系統實驗室配置為:一個主控中心平臺,30個數據採集器,30套仿真編輯器軟體和30套協議分析器軟體;與60臺PC機組成30個實驗單元組,可滿足60個學生同時實驗。
  • 老怪談幣:ERC-721協議
    我們經常會在業內媒體報導上看到「ERC-20」這個詞,它是以太坊上的一種標準協議。除了ERC-20,以太坊上的另一個較多數人聽說過的協議是ERC-721。 雖然聽說過,但很多人依然不了解這兩個協議究竟是什麼,應該如何區分。今天,小編就給大家梳理一下。
  • 微信語音回復「收到」後又反悔,這個協議還有效嗎
    收到對方發來的一份房屋租賃協議,微信語音進行了回復,後來覺得不踏實想解除協議並索要租金,這樣的想法法院會支持嗎?10月9日,濟南市中級人民法院發布了這樣一起「微信協議」案。但雙方並未籤訂紙質租賃協議,而是通過微信達成的協議。5月24日,酒店的法定代表人王某通過微信向劉某發送了房屋租賃協議,劉某當天未予回應。次日,王某又向劉某發送了該房屋租賃協議,對方隨即向王某發送微信語音消息稱:「王總王總,我看到了,好,謝謝你,你說定金多少,等一會兒我打給你。」事後,劉某通過微信向王某轉帳支付了5千元租賃押金。
  • 不止王源愛擼貓,看到易烊千璽和王俊凱的擼貓表情,粉絲不淡定了
    4月24日,王源突然更博曬出一張新自拍,配文「擼貓」,自拍中,王源穿著黑色衛衣,還把衛衣反過來穿,不愧是剛哥,用衛衣帽子小心翼翼兜著貓咪,可愛的貓咪被揣在懷裡,這一幕讓粉絲紛紛吃醋,感慨王源對貓咪也太好了吧!不少粉絲表示想做掛在源哥領口的貓咪!
  • 我在非洲擼豹子 全家誰最勇敢?
    暑假,我和爸爸媽媽來到了非洲納米比亞一個養豹子的莊園,與獵豹進行了一次親密接觸。這個莊園面積大概有三四平方公裡。精緻的叢林小屋,一個個點綴在碧綠的草地上。乾草特製的屋頂,茶綠色的牆壁和隨處可見的動物造型,使大家感受到非洲大陸特有的淳樸和熱情。莊園的招待大廳更是豪華氣派。一進門,高高的圓錐形屋頂由幾根擎天柱頂著。
  • 「萌貓」來襲,如何才能快樂擼貓?
    俗話說「擼貓一時爽,一直擼貓一直爽……」擼貓這種事不少人都上癮喵星人這毛茸茸的身體特質簡直讓人慾罷不能!那麼如何做到健康擼貓、快樂擼貓呢?這就要從萌貓體內、體外寄生蟲的故事說起了!1. 體內寄生蟲主要分為原蟲和蠕蟲兩大類。
  • 【讀書筆記】1.2、基於HTTP協議的RPC
    1.2.1HTTP協議棧HTTP協議Hypertext Transfer Protocol的縮寫(超文本傳輸協議)屬於應用層協議,構建在TCP與IP協議之上,處於TCP/IP體系架構頂端無需處理丟包補發、握手及數據的分段和重新組裝等細節