欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

微信小程序在線客服自動(dòng)回復(fù)功能(基于node)

 更新時(shí)間:2019年07月03日 17:16:40   作者:熊貓貓超人  
這篇文章主要介紹了微信小程序在線客服自動(dòng)回復(fù)功能(基于node),由于小程序嵌套webview時(shí)需要校驗(yàn)域名,因此跳轉(zhuǎn)到第三方應(yīng)用市場(chǎng)和Appstroe無法實(shí)現(xiàn)導(dǎo)流。那怎么辦呢,需要的朋友可以參考下

前言

我們知道H5頁(yè)面經(jīng)常需要將用戶導(dǎo)流到APP,通過下載安裝包或者跳轉(zhuǎn)至應(yīng)用寶市場(chǎng)/Appstore等方式進(jìn)行導(dǎo)流。但是由于小程序嵌套webview時(shí)需要校驗(yàn)域名,因此跳轉(zhuǎn)到第三方應(yīng)用市場(chǎng)和Appstroe無法實(shí)現(xiàn)導(dǎo)流。那怎么辦呢?

只能說道高一尺魔高一丈,看看微博小程序是怎么導(dǎo)流的:

曲線救國(guó)的方式,利用小程序的在線功能可以打開H5的方式,去進(jìn)行下載引導(dǎo)。

于是,就引出了這次文檔的主題,小程序在線客服自動(dòng)回復(fù)功能。

閱讀本文檔之前,最好已經(jīng)了解過小程序客服信息官方的相關(guān)文檔:

這次開發(fā)做在線客服功能也踩了不少坑,網(wǎng)上也查閱不少資料,但大部分的后臺(tái)都是基于php或者python,java開發(fā),node.js開發(fā)的較少,因此將這次開發(fā)的流程記錄一下,供大家參考,避免大家踩坑??赡軙?huì)有一些錯(cuò)誤地方歡迎指正交流。

另外,我們用的node框架是基于koa自行封裝的,在一些細(xì)節(jié)實(shí)現(xiàn)上和其他框架會(huì)有區(qū)別,不必糾結(jié)。

需求描述

小程序中點(diǎn)按鈕跳轉(zhuǎn)在線客服界面,根據(jù)關(guān)鍵詞自動(dòng)回復(fù)

客服回復(fù)判斷條件,支持cms配置key,及 respond

respond 支持配置以下類型,及回復(fù)內(nèi)容:

type 內(nèi)容
text text=文本回復(fù)內(nèi)容
link title=標(biāo)題 description=描述 url=跳轉(zhuǎn)鏈接 thumb_url=圖片地址
image imageurl=圖片地址

  • 配置后用戶需要精準(zhǔn)匹配回復(fù)條件才可收到自動(dòng)回復(fù)
  • 可支持配置多個(gè)key,及對(duì)應(yīng)respond
  • 除了配置的key以外的回復(fù),可配置默認(rèn)的自動(dòng)回復(fù)

開發(fā)流程

寫個(gè)跳轉(zhuǎn)客服的按鈕吧

index.wxml

<button open-type="contact">轉(zhuǎn)在線客服</button>

后臺(tái)配置

登錄小程序后臺(tái)后,在「開發(fā)」-「開發(fā)設(shè)置」-「消息推送」中,管理員掃碼啟用消息服務(wù),填寫服務(wù)器地址(URL)、令牌(Token) 和 消息加密密鑰(EncodingAESKey)等信息。

1.URL服務(wù)器地址

URL: 開發(fā)者用來接收微信消息和事件的接口 URL。開發(fā)者所填寫的URL 必須以 http:// 或 https:// 開頭,分別支持 80 端口和 443 端口。

務(wù)必要記住,服務(wù)器地址必須是線上地址,因?yàn)樾枰⑿欧?wù)器去訪問。localhost,IP,內(nèi)網(wǎng)地址都不行的。

不然會(huì)提示 '解析失敗,請(qǐng)檢查信息是否填寫正確'。

那么問題來了,不同的公司都有一套上線流程,總不能為了調(diào)試URL是否可用要上到線上去測(cè)試,成本太大,也不方便。
這就要引出內(nèi)網(wǎng)穿透了,簡(jiǎn)單來說就是配置一個(gè)線上域名,但是這個(gè)域名可以穿透到你配置的本地開發(fā)地址上,這樣可以方便你去調(diào)試看日志。

推薦一個(gè)可以實(shí)現(xiàn)內(nèi)網(wǎng)穿透的工具。(非廣告)

NATAPP 具體不詳細(xì)介紹,免得廣告嫌疑。

簡(jiǎn)單說,NATAPP有免費(fèi)和付費(fèi)兩種模式,免費(fèi)的是域名不定時(shí)更換,對(duì)于微信的推送消息配置一個(gè)月只有3次更改機(jī)會(huì)來說,有點(diǎn)奢侈。不定什么時(shí)候配置的域名就不能訪問,得重新配置。而付費(fèi)的則是固定域名,映射的內(nèi)網(wǎng)地址也可以隨時(shí)更改。樓主從免費(fèi)切到付費(fèi)模式,一個(gè)月的VIP使用大概十幾塊錢吧。

2.Token

Token自己隨便寫就行了,但是要記住它,因?yàn)槟阍诮涌谥幸玫摹?/p>

3.EncodingAESKey

隨機(jī)生成即可。

4.加密方式和數(shù)據(jù)格式

根據(jù)自己喜歡選擇,樓主選擇的安全模式和JSON格式。
不同的模式和數(shù)據(jù)格式,在開發(fā)上會(huì)有不同,自己衡量。
既然這些配置都清楚,那開始碼代碼。

驗(yàn)證消息的確來自微信服務(wù)器

配置提交前,需要把驗(yàn)證消息來自微信服務(wù)器的接口寫好。

server.js

  /*
   * https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html
   * 驗(yàn)證消息的確來自微信服務(wù)器
   * 開發(fā)者通過檢驗(yàn) signature 對(duì)請(qǐng)求進(jìn)行校驗(yàn)(下面有校驗(yàn)方式)。
   * 若確認(rèn)此次 GET 請(qǐng)求來自微信服務(wù)器,請(qǐng)?jiān)瓨臃祷?echostr 參數(shù)內(nèi)容,
   * 則接入生效,成為開發(fā)者成功,否則接入失敗。加密/校驗(yàn)流程如下:
   * 將token、timestamp、nonce三個(gè)參數(shù)進(jìn)行字典序排序
   * 將三個(gè)參數(shù)字符串拼接成一個(gè)字符串進(jìn)行sha1加密
   * 開發(fā)者獲得加密后的字符串可與signature對(duì)比,標(biāo)識(shí)該請(qǐng)求來源于微信
   */
   const crypto = require('crypto');
   async wxCallbackAction(){
    const ctx = this.ctx;
    const method = ctx.method;
    //微信服務(wù)器簽名驗(yàn)證,確認(rèn)請(qǐng)求來自微信
    if(method === 'GET') {
      // 1.獲取微信服務(wù)器Get請(qǐng)求的參數(shù) signature、timestamp、nonce、echostr
      const {
        signature,
        timestamp,
        nonce,
        echostr
      } = ctx.query;
      
      // 2.將token、timestamp、nonce三個(gè)參數(shù)進(jìn)行字典序排序
      let array = ['yourToken', timestamp, nonce];
      array.sort();
      
      // 3.將三個(gè)參數(shù)字符串拼接成一個(gè)字符串進(jìn)行sha1加密
      const tempStr = array.join('');
      const hashCode = crypto.createHash('sha1'); //創(chuàng)建加密類型
      const resultCode = hashCode.update(tempStr, 'utf8').digest('hex');
      
      // 4.開發(fā)者獲得加密后的字符串可與signature對(duì)比,標(biāo)識(shí)該請(qǐng)求來源于微信
      if (resultCode === signature) {
        console.log('驗(yàn)證成功,消息是從微信服務(wù)器轉(zhuǎn)發(fā)過來');
        return this.json(echostr);
      }else {
        console.log('驗(yàn)證失?。。?!');
        return this.json({
          status: -1,
          message: "驗(yàn)證失敗"
        });
      }
      
    }
   }

驗(yàn)證接口開發(fā)完畢,后臺(tái)配置可以去點(diǎn)提交了。配置成功會(huì)提示如下:

接收消息和推送消息

當(dāng)用戶在客服會(huì)話發(fā)送消息、或由某些特定的用戶操作引發(fā)事件推送時(shí),微信服務(wù)器會(huì)將消息或事件的數(shù)據(jù)包發(fā)送到開發(fā)者填寫的 URL。開發(fā)者收到請(qǐng)求后可以使用 發(fā)送客服消息 接口進(jìn)行異步回復(fù)。

本文以接收文本消息為例開發(fā):

server.js

  const WXDecryptContact = require('./WXDecryptContact');
  async wxCallbackAction(){
    const ctx = this.ctx;
    const method = ctx.method;
    //接收信息時(shí) 為POST請(qǐng)求;(完整代碼自行與上面驗(yàn)證時(shí)的合并即可)
    if(method === 'POST'){
      const { Encrypt } = ctx.request.body;
      //配置時(shí)選的安全模式 因此需要解密
      if(!Encrypt){
        return this.json('success');
      }
      const decryptData = WXDecryptContact(Encrypt);
      await this._handleWxMsg(decryptData);
      return this.json('success');
    }else{
      return this.json('success');
    }
  }
  
  //處理微信回調(diào)消息的總?cè)肟?(只處理了文本類型,其他類型自行添加)
  async _handleWxMsg(msgJson){
    if(!msgJson){
      return this.json('success');
    }

    const { MsgType } = msgJson;
    if(MsgType === 'text'){
      await this._sendTextMessage(msgJson);
    }
  }
  
  //微信文本信息關(guān)鍵字自動(dòng)回復(fù)
  async _sendTextMessage(msgJson){
    //獲取CMS客服關(guān)鍵詞回復(fù)配置
    const result = await this.callService('cms.getDataByName', 'wxApplet.contact');
    
    let keyWordObj = result.data || {};
  
    //默認(rèn)回復(fù)default
    let options = keyWordObj.default;
    for(let key in keyWordObj){
      //查看是否命中配置的關(guān)鍵詞
      if(msgJson.Content === key){
        //CMS配置項(xiàng)
        options = keyWordObj[key];
        }
      }
    }
    
    //獲取access_token
    const accessToken = await this._getAccessToken();
    
    /*
    * 先判斷配置回復(fù)的消息類型是不是image類型
    * 如果是 則需要先通過 新增素材接口 上傳圖片文件獲得 media_id
    */
    
    let media_id = '';
    if(options.type === 'image'){
      //獲取圖片地址(相對(duì)路徑)
      let url = options.url;
      const file = fs.createReadStream(url);
      
      //調(diào)用微信 uploadTempMedia接口 具體實(shí)現(xiàn)見 service.js
      const mediaResult = await this.callService('wxApplet.uploadTempMedia',
        {
          access_token: accessToken,
          type: 'image'
        },
        {
          media: file
        }
      );
      
      if(mediaResult.status === 0){
        media_id = mediaResult.data.media_id;
      }else {
        //如果圖片id獲取失敗 則按默認(rèn)處理
        options = keyWordObj.default;
      }
    }
    
    //回復(fù)信息給用戶
    const sendMsgResult = await this.callService('wxApplet.sendMessageToCustomer',
      {
        access_token: accessToken,
        touser: msgJson.FromUserName,
        msgtype: options.type || 'text',
        text: {
          content: options.description || '',
        },
        link: options.type === "link" ? 
          {
            title: options.title,
            description: options.description,
            url: options.url,
            thumb_url: options.thumb_url
          }
          :
          {},
        image: {
          media_id
        }
      }
    );    
  }

service.js

const request = require('request');
/*
* 獲取CMS客服關(guān)鍵詞回復(fù)配置
* 這個(gè)接口只是為了回去CMS配置的字段回復(fù)關(guān)鍵字配置 返回的data數(shù)據(jù)結(jié)構(gòu)如下
*/
async contact(){
  return {
    data: {
      "1": {
        "type": "link",
        "title": "點(diǎn)擊下載[****]APP",
        "description": "注冊(cè)領(lǐng)取領(lǐng)***元注冊(cè)紅包禮",
        "url": "https://m.renrendai.com/mo/***.html",
        "thumb_url": "https://m.we.com/***/test.png"
       },
       "2": {
        "url": "http://m.renrendai.com/cms/****/test.jpg",
        "type": "image"
       },
       "3": {
        "url": "/cms/***/test02.png",
        "type": "image"
       },
       "default": {
        "type": "text",
        "description": "再見"
       }
    }
  }
}

/*
 * 把媒體文件上傳到微信服務(wù)器。目前僅支持圖片。用于發(fā)送客服消息或被動(dòng)回復(fù)用戶消息。
 * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/customer-message/customerServiceMessage.uploadTempMedia.html
 */
 
 async uploadTempMedia(data,formData){
  const url = `https://api.weixin.qq.com/cgi-bin/media/upload?access_token=${data.access_token}&type=${data.type}`;
  return new Promise((resolve, reject) => {
    request.post({url, formData: formData}, (err, response, body) => {
      try{
        const out = JSON.parse(body);
        let result = {
          data: out,
          status: 0,
          message: "ok"
        }
        
        return resolve(result);
      
      }catch(err){
        return reject({
          status: -1,
          message: err.message
        });
      }
    });
  }
 }
 
 /*
 * 發(fā)送客服消息給用戶
 * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/customer-message/customerServiceMessage.send.html
 */
 
 async sendMessageToCustomer(data){
  const url = `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${data.access_token}`;
  return new Promise((resolve, reject) => {
    request.post({url, data}, (err, response, body) => {
      ...
    });
  }
 }

WXDecryptContact.js

消息加密解密文檔

const crypto = require('crypto'); // 加密模塊

const decodePKCS7 = function (buff) {
  let pad = buff[buff.length - 1];
  if (pad < 1 || pad > 32) {
    pad = 0;
  }
  return buff.slice(0, buff.length - pad);
};

// 微信轉(zhuǎn)發(fā)客服消息解密
const decryptContact = (key, iv, crypted) => {
  const aesCipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
  aesCipher.setAutoPadding(false);
  let decipheredBuff = Buffer.concat([aesCipher.update(crypted, 'base64'), aesCipher.final()]);
  decipheredBuff = decodePKCS7(decipheredBuff);
  const lenNetOrderCorpid = decipheredBuff.slice(16);
  const msgLen = lenNetOrderCorpid.slice(0, 4).readUInt32BE(0);
  const result = lenNetOrderCorpid.slice(4, msgLen + 4).toString();
  return result;
};

// 解密微信返回給配置的消息服務(wù)器的信息
const decryptWXContact = (wechatData) => {
  if(!wechatData){
    wechatData = '';
  }
  //EncodingAESKey 為后臺(tái)配置時(shí)隨機(jī)生成的
  const key = Buffer.from(EncodingAESKey + '=', 'base64');
  const iv = key.slice(0, 16);
  const result = decryptContact(key, iv, wechatData);
  const decryptedResult = JSON.parse(result);
  console.log(decryptedResult);
  return decryptedResult;
};
module.exports = decryptWXContact;

呼~ 代碼終于碼完,來看看效果:

總結(jié)

開發(fā)并不是一帆風(fēng)順的,也遇到了一些值得留意的坑,強(qiáng)調(diào)一下:

  • 后臺(tái)配置URL地址一定外網(wǎng)可訪問(可以通過內(nèi)網(wǎng)穿透解決)
  • 文件上傳接口uploadTempMedia media參數(shù)要用 FormData數(shù)據(jù)格式 (用node的request庫(kù)很容易實(shí)現(xiàn)。urllib這個(gè)庫(kù)有坑有坑 都是淚T_T)
  • 切記接收消息不論成功失敗都要返回success,不然即使成功接收返回消息,日志沒有報(bào)錯(cuò)的情況下,還是出現(xiàn)IOS提示該小程序提供的服務(wù)出現(xiàn)故障 請(qǐng)稍后再試。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • node.js中的fs.createWriteStream方法使用說明

    node.js中的fs.createWriteStream方法使用說明

    這篇文章主要介紹了node.js中的fs.createWriteStream方法使用說明,本文介紹了fs.createWriteStream方法說明、語法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • Node.js與PHP、Python的字符處理性能對(duì)比

    Node.js與PHP、Python的字符處理性能對(duì)比

    因?yàn)楹罄m(xù)考慮實(shí)現(xiàn) Fl 引擎的Node.js版本,所以對(duì)比了下Node.js和PHP的字符處理性能。發(fā)現(xiàn)Node.js真是甩了PHP幾條街啊,再測(cè)試了下Python,比PHP還慢。
    2014-07-07
  • 詳解Nodejs get獲取遠(yuǎn)程服務(wù)器接口數(shù)據(jù)

    詳解Nodejs get獲取遠(yuǎn)程服務(wù)器接口數(shù)據(jù)

    這篇文章主要介紹了Nodejs get獲取遠(yuǎn)程服務(wù)器接口數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • nodejs中模塊定義實(shí)例詳解

    nodejs中模塊定義實(shí)例詳解

    這篇文章主要介紹了nodejs中模塊定義方法,結(jié)合實(shí)例形式分析了nodejs模塊的原理、常見模塊及相應(yīng)的定義方法,需要的朋友可以參考下
    2017-03-03
  • nodejs部署到騰訊云的實(shí)現(xiàn)方法

    nodejs部署到騰訊云的實(shí)現(xiàn)方法

    本文主要介紹了nodejs部署到騰訊云的實(shí)現(xiàn)方法,文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 深入淺出了解Node.js Streams

    深入淺出了解Node.js Streams

    這篇文章講了了解流的用途,為什么它們?nèi)绱酥匾约叭绾问褂盟鼈?。下面我們來一起學(xué)習(xí)吧
    2019-05-05
  • Node.js的文件權(quán)限及讀寫flag詳解

    Node.js的文件權(quán)限及讀寫flag詳解

    Node.js對(duì)文件的讀寫還是相當(dāng)靈活的,因?yàn)樽约豪鲜怯洸蛔∥募蚰夸洐?quán)限的數(shù)值表達(dá)和字符表達(dá)。所以整理出這篇文章,方便以后查閱,下面來一起看看吧。
    2016-10-10
  • 利用Node.js對(duì)文件進(jìn)行重命名

    利用Node.js對(duì)文件進(jìn)行重命名

    最近工作中遇到了對(duì)文件進(jìn)行重命名的需求,后通過node.js實(shí)現(xiàn)了,所以下面這篇文章主要給大家介紹了利用Node.js對(duì)文件進(jìn)行重命名的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。
    2017-03-03
  • Node.js中的流(Stream)介紹

    Node.js中的流(Stream)介紹

    這篇文章主要介紹了Node.js中的流(Stream)介紹,本文講解了什么是流、pipe方法、流的分類、Readable流狀態(tài)的切換等內(nèi)容,需要的朋友可以參考下
    2015-03-03
  • 在Node.js應(yīng)用中使用Redis的方法簡(jiǎn)介

    在Node.js應(yīng)用中使用Redis的方法簡(jiǎn)介

    這篇文章主要介紹了在Node.js應(yīng)用中使用Redis的方法,最簡(jiǎn)單的數(shù)據(jù)讀寫操作相關(guān),需要的朋友可以參考下
    2015-06-06

最新評(píng)論