微信公衆號開發之自動消息回覆和自定義菜單

 目錄

(一)微信公衆號開發之VS遠程調試
(二)微信公衆號開發之基礎梳理
(三)微信公衆號開發之自動消息回覆和自定義菜單
(四)微信公衆號開發之網頁受權獲取用戶基本信息
(五)微信公衆號開發之網頁中及時獲取當前用戶Openid及注意事項
(六)微信公衆號開發之掃碼支付
(七)微信公衆號開發之公衆號支付
(八)微信公衆號開發之現金紅包
(九)微信公衆號開發之回覆圖文消息(被動)

前言

上一篇咱們大體講解了下微信公衆號開發的基本原理和流程概述。本章主要是對文本消息回覆和自定義菜單作一個記錄和分解html

消息回覆

處理請求,並響應

1)關注

也可參考官網文檔:https://mp.weixin.qq.com/wikigit

當微信用戶關注公衆帳號時,能夠給其適當的提示。能夠是歡迎詞,能夠是幫助提示。示例代碼以下:github

 class EventHandler : IHandler
    {
        /// <summary>
        /// 請求的xml
        /// </summary>
        private string RequestXml { get; set; }
        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="requestXml"></param>
        public EventHandler(string requestXml)
        {
            this.RequestXml = requestXml;
        }
        /// <summary>
        /// 處理請求
        /// </summary>
        /// <returns></returns>
        public string HandleRequest()
        {
            string response = string.Empty;
            EventMessage em = EventMessage.LoadFromXml(RequestXml);
            if (em.Event.Equals("subscribe",StringComparison.OrdinalIgnoreCase))
            {
                //回覆歡迎消息
                TextMessage tm = new TextMessage();
                tm.ToUserName = em.FromUserName;
                tm.FromUserName = em.ToUserName;
                tm.CreateTime = Common.GetNowTime();
                tm.Content = "歡迎您關注咱們,我是服務小二,有事您開口~\n\n";
                response = tm.GenerateContent();
            }

            return response;
        }
    }

官方給出的介紹是這樣的json

關注/取消關注事件

用戶在關注與取消關注公衆號時,微信會把這個事件推送到開發者填寫的URL。方便開發者給用戶下發歡迎消息或者作賬號的解綁。api

微信服務器在五秒內收不到響應會斷掉鏈接,而且從新發起請求,總共重試三次。數組

關於重試的消息排重,推薦使用FromUserName + CreateTime 排重。緩存

假如服務器沒法保證在五秒內處理並回復,能夠直接回復空串,微信服務器不會對此做任何處理,而且不會發起重試。服務器

推送XML數據包示例:微信

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>

參數說明:app

參數 描述
ToUserName 開發者微信號
FromUserName 發送方賬號(一個OpenID)
CreateTime 消息建立時間 (整型)
MsgType 消息類型,event
Event 事件類型,subscribe(訂閱)、unsubscribe(取消訂閱)

也可以使用微信在線接口調試工具測試是否正常:取消/關注事件接口在線調試

2)問候

簡單的交流問候,好比你好、幫助等等,跟咱們使用微信聊天同樣,不過迴應是由咱們的程序響應。具體功能,能夠根據本身的須要進行添加。
微信原本就是溝通的平臺。這個案例,能夠用於在線服務機器人,相似於淘寶的客服機器人,但是咱們這個是微信版的。
其實,很簡單,獲取請求消息,根據關鍵字來匹配迴應。固然這裏可能要作的工做不少,如何支持智能匹配,如何支持模糊匹配等。

 /// <summary>
    /// 文本信息處理類
    /// </summary>
    public class TextHandler : IHandler
    {
        /// <summary>
        /// 請求的XML
        /// </summary>
        private string RequestXml { get; set; }
        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="requestXml">請求的xml</param>
        public TextHandler(string requestXml)
        {
            this.RequestXml = requestXml;
        }
        /// <summary>
        /// 處理請求
        /// </summary>
        /// <returns></returns>
        public string HandleRequest()
        {
            string response = string.Empty;
            TextMessage tm = TextMessage.LoadFromXml(RequestXml);
            string content = tm.Content.Trim();
            if (string.IsNullOrEmpty(content))
            {
                response = "您什麼都沒輸入,無法幫您啊,%>_<%。";
            }
            else
            {
                if (content.StartsWith("tq", StringComparison.OrdinalIgnoreCase))
                {
                    string cityName = content.Substring(2).Trim();
                    response = WeatherHelper.GetWeather(cityName);
                }
                else
                {
                    response = HandleOther(content);
                }
            }
            tm.Content = response;
            //進行發送者、接收者轉換
            string temp = tm.ToUserName;
            tm.ToUserName = tm.FromUserName;
            tm.FromUserName = temp;
            response = tm.GenerateContent();
            return response;
        }
        /// <summary>
        /// 處理其餘消息
        /// </summary>
        /// <param name="tm"></param>
        /// <returns></returns>
        private string HandleOther(string requestContent)
        {
            string response = string.Empty;
            if (requestContent.Contains("你好") || requestContent.Contains("您好"))
            {
                response = "您也好~";
            }
            else if (requestContent.Contains(""))
            {
                response = "我不傻!哼~ ";
            }
            else if (requestContent.Contains("shit") || requestContent.Contains("小王八蛋"))
            {
                response = "哼,你說髒話! ";
            }
            else if (requestContent.Contains("是誰"))
            {
                response = "我是大哥大,有什麼能幫您的嗎?~";
            }
            else if (requestContent.Contains("再見"))
            {
                response = "再見!";
            }
            else if (requestContent.Contains("bye"))
            {
                response = "Bye!";
            }
            else if (requestContent.Contains("謝謝"))
            {
                response = "不客氣!嘿嘿";
            }
            else if (requestContent == "h" || requestContent == "H" || requestContent.Contains("幫助"))
            {
                response = @"有事找警察";
            }
            else
            {
                response = "您說的,惋惜,我沒明白啊,試試其餘關鍵字吧。";
            }

            return response;
        }
    }

實現效果如圖所示:

自定義菜單

 首先呢,咱們要了解下微信公衆號開發裏面菜單有哪些類型呢?

自定義菜單接口可實現多種類型按鈕,以下:

請注意,3到8的全部事件,僅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用戶,舊版本微信用戶點擊後將沒有迴應,開發者也不能正常接收到事件推送。9和10,是專門給第三方平臺旗下未微信認證(具體而言,是資質認證未經過)的訂閱號準備的事件類型,它們是沒有事件推送的,能力相對受限,其餘類型的公衆號沒必要使用。

接口調用請求說明

http請求方式:POST(請使用https協議) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

click和view的請求示例

 {
     "button":[
     {    
          "type":"click",
          "name":"今日歌曲",
          "key":"V1001_TODAY_MUSIC"
      },
      {
           "name":"菜單",
           "sub_button":[
           {    
               "type":"view",
               "name":"搜索",
               "url":"http://www.soso.com/"
            },
            {
               "type":"view",
               "name":"視頻",
               "url":"http://v.qq.com/"
            },
            {
               "type":"click",
               "name":"贊一下咱們",
               "key":"V1001_GOOD"
            }]
       }]
 }

參數說明

參數 是否必須 說明
button 一級菜單數組,個數應爲1~3個
sub_button 二級菜單數組,個數應爲1~5個
type 菜單的響應動做類型
name 菜單標題,不超過16個字節,子菜單不超過60個字節
key click等點擊類型必須 菜單KEY值,用於消息接口推送,不超過128字節
url view類型必須 網頁連接,用戶點擊菜單可打開連接,不超過1024字節
media_id media_id類型和view_limited類型必須 調用新增永久素材接口返回的合法media_id

 

固然也可使用在線調試接口調試菜單是否設置正確:我要在線調試菜單接口

詳細步驟

一、首先獲取access_token 

access_token是公衆號的全局惟一票據,公衆號調用各接口時都需使用access_token。正常狀況下access_token有效期爲7200秒,重複獲取將致使上次獲取的access_token失效。

公衆號可使用AppID和AppSecret調用本接口來獲取access_token。AppID和AppSecret可在開發模式中得到(須要已經成爲開發者,且賬號沒有異常狀態)。注意調用全部微信接口時均需使用https協議。

接口調用請求說明

http請求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

參數說明

參數 是否必須 說明
grant_type 獲取access_token填寫client_credential
appid 第三方用戶惟一憑證
secret 第三方用戶惟一憑證密鑰,既appsecret

返回說明

正常狀況下,微信會返回下述JSON數據包給公衆號:

{"access_token":"ACCESS_TOKEN","expires_in":7200}
參數 說明
access_token 獲取到的憑證
expires_in 憑證有效時間,單位:秒

錯誤時微信會返回錯誤碼等信息,JSON數據包示例以下(該示例爲AppID無效錯誤):

{"errcode":40013,"errmsg":"invalid appid"} 

 

建立自定義菜單

自定義菜單可以幫助公衆號豐富界面,讓用戶更好更快地理解公衆號的功能。

目前自定義菜單最多包括3個一級菜單,每一個一級菜單最多包含5個二級菜單。一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以「...」代替。請注意,建立自定義菜單後,因爲微信客戶端緩存,須要24小時微信客戶端纔會展示出來。建議測試時能夠嘗試取消關注公衆帳號後再次關注,則能夠看到建立後的效果。

三、查詢菜單

使用接口建立自定義菜單後,開發者還可以使用接口查詢自定義菜單的結構。

請求說明

http請求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN

返回說明

對應建立接口,正確的Json返回結果:
{"menu":{"button":[{"type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC","sub_button":[]},{"type":"click","name":"歌手簡介","key":"V1001_TODAY_SINGER","sub_button":[]},{"name":"菜單","sub_button":[{"type":"view","name":"搜索","url":"http://www.soso.com/","sub_button":[]},{"type":"view","name":"視頻","url":"http://v.qq.com/","sub_button":[]},{"type":"click","name":"贊一下咱們","key":"V1001_GOOD","sub_button":[]}]}]}}

四、刪除菜單

使用接口建立自定義菜單後,開發者還可以使用接口刪除當前使用的自定義菜單。

請求說明

http請求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN

返回說明

對應建立接口,正確的Json返回結果:
{"errcode":0,"errmsg":"ok"}

五、事件處理

用戶點擊自定義菜單後,若是菜單按鈕設置爲click類型,則微信會把這次點擊事件推送給開發者,注意view類型(跳轉到URL)的菜單點擊不會上報。

推送XML數據包示例:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[CLICK]]></Event>
<EventKey><![CDATA[EVENTKEY]]></EventKey>
</xml>

參數說明:

 

 

參數 描述
ToUserName 開發者微信號
FromUserName 發送方賬號(一個OpenID)
CreateTime 消息建立時間 (整型)
MsgType 消息類型,event
Event 事件類型,CLICK
EventKey 事件KEY值,與自定義菜單接口中KEY值對應

實例講解

咱們將會在上一篇的基礎上,添加自定義菜單的功能

一、獲取access_token

首先須要獲得AppId和AppSecret

當你成爲開發者後,天然可以在,開發者模式,即可看到這兩個值,能夠重置。

而後調用按照二.1中所示,進行操做。

注意:access_token有過時時間,若是過時,須要從新獲取。

代碼以下:

  private static DateTime GetAccessToken_Time;
        /// <summary>
        /// 過時時間爲7200秒
        /// </summary>
        private static int Expires_Period = 7200;
        /// <summary>
        /// 
        /// </summary>
        private static string mAccessToken;
        /// <summary>
        /// 
        /// </summary>
        public static string AccessToken
        {
            get
            {
                //若是爲空,或者過時,須要從新獲取
                if (string.IsNullOrEmpty(mAccessToken) || HasExpired())
                {
                    //獲取
                    mAccessToken = GetAccessToken(AppID, AppSecret);
                }

                return mAccessToken;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="appSecret"></param>
        /// <returns></returns>
        private static string GetAccessToken(string appId, string appSecret)
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, appSecret);
            string result = HttpUtility.GetData(url);

            XDocument doc = XmlUtility.ParseJson(result, "root");
            XElement root = doc.Root;
            if (root != null)
            {
                XElement access_token = root.Element("access_token");
                if (access_token != null)
                {
                    GetAccessToken_Time = DateTime.Now;
                    if (root.Element("expires_in")!=null)
                    {
                        Expires_Period = int.Parse(root.Element("expires_in").Value);
                    }
                    return access_token.Value;
                }
                else
                {
                    GetAccessToken_Time = DateTime.MinValue;
                }
            }

            return null;
        }
        /// <summary>
        /// 判斷Access_token是否過時
        /// </summary>
        /// <returns>bool</returns>
        private static bool HasExpired()
        {
            if (GetAccessToken_Time != null)
            {
                //過時時間,容許有必定的偏差,一分鐘。獲取時間消耗
                if (DateTime.Now > GetAccessToken_Time.AddSeconds(Expires_Period).AddSeconds(-60))
                {
                    return true;
                }
            }
            return false;
        }

二、設置菜單

菜單需根據須要,按照實際要求進行設定。

這裏咱們只作簡單的演示。

而後還提供了友情連接,這裏提供了view類型的菜單,直接能夠跳轉至URL頁面,爲跳轉作個好的演示。

具體菜單以下:

{

    "button": [
        {
            "name": "測試跳轉", 
            "sub_button": [
                {
                    "type": "view", 
                    "name": "搜索", 
                    "url": "http://www.baidu.com/"
                }, 
                {
                    "type": "view", 
                    "name": "視頻", 
                    "url": "http://v.qq.com/"
                }, 
                {
                    "type": "click", 
                    "name": "贊一下咱們", 
                    "key": "BTN_GOOD"
                }
            ]
        }, 
        {
            "type": "view",
            "name": "設備狀態", 
            "url": "http://vanrui.com/weixin"
        }, 
        {
            "type": "click", 
            "name": "幫助", 
            "key": "BTN_HELP"
        }
    ]
}

三、管理菜單

由於菜單的變動沒有那麼頻繁,所以經過txt文件來設置菜單,並經過管理界面來管理菜單。

主要的管理功能:

1)從文件加載菜單

2)建立菜單。即將菜單通知微信服務端,並更新至微信客戶端

3)查詢菜單。獲取當前系統的菜單。

4)刪除菜單。從微信服務器刪除菜單,也能夠刪除後再建立。 

實現代碼以下:

public class MenuManager
    {
        /// <summary>
        /// 菜單文件路徑
        /// </summary>
        private static readonly string Menu_Data_Path = System.AppDomain.CurrentDomain.BaseDirectory + "/Data/menu.txt";
        /// <summary>
        /// 獲取菜單
        /// </summary>
        public static string GetMenu()
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", Context.AccessToken);

            return HttpUtility.GetData(url);
        }
        /// <summary>
        /// 建立菜單
        /// </summary>
        public static void CreateMenu(string menu)
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}", Context.AccessToken);
            //string menu = FileUtility.Read(Menu_Data_Path);
            HttpUtility.SendHttpRequest(url, menu);
        }
        /// <summary>
        /// 刪除菜單
        /// </summary>
        public static void DeleteMenu()
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", Context.AccessToken);
            HttpUtility.GetData(url);
        }
        /// <summary>
        /// 加載菜單
        /// </summary>
        /// <returns></returns>
        public static string LoadMenu()
        {
            return FileUtility.Read(Menu_Data_Path);
        }
    }

四、基本方法

上面的代碼,其實咱們對一些公共功能作了封裝。如進行get請求、POST提交等操做,讀取文件等。

這裏咱們提供進行get、Post提交的方法案例代碼,若是使用,建議優化。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace WeChat.Utility
{
    /// <summary>
    /// 幫助類
    /// </summary>
    class HttpUtility
    {
        /// <summary>
        /// 發送請求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="data">數據</param>
        public static string SendHttpRequest(string url, string data)
        {
            return SendPostHttpRequest(url, "application/x-www-form-urlencoded", data);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GetData(string url)
        {
            return SendGetHttpRequest(url, "application/x-www-form-urlencoded");
        }

        /// <summary>
        /// 發送請求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="method">方法(post或get)</param>
        /// <param name="method">數據類型</param>
        /// <param name="requestData">數據</param>
        public static string SendPostHttpRequest(string url, string contentType, string requestData)
        {
            WebRequest request = (WebRequest)HttpWebRequest.Create(url);
            request.Method = "POST";
            byte[] postBytes = null;
            request.ContentType = contentType;
            postBytes = Encoding.UTF8.GetBytes(requestData);
            request.ContentLength = postBytes.Length;
            using (Stream outstream = request.GetRequestStream())
            {
                outstream.Write(postBytes, 0, postBytes.Length);
            }
            string result = string.Empty;
            using (WebResponse response = request.GetResponse())
            {
                if (response != null)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
                        {
                            result = reader.ReadToEnd();
                        }
                    }

                }
            }
            return result;
        }

        /// <summary>
        /// 發送請求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="method">方法(post或get)</param>
        /// <param name="method">數據類型</param>
        /// <param name="requestData">數據</param>
        public static string SendGetHttpRequest(string url, string contentType)
        {
            WebRequest request = (WebRequest)HttpWebRequest.Create(url);
            request.Method = "GET";
            request.ContentType = contentType;
            string result = string.Empty;
            using (WebResponse response = request.GetResponse())
            {
                if (response != null)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
                        {
                            result = reader.ReadToEnd();
                        }
                    }
                }
            }
            return result;
        }
    }
}
class XmlUtility
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="json"></param>
        /// <param name="rootName"></param>
        /// <returns></returns>
        public static XDocument ParseJson(string json,string rootName)
        {
            return JsonConvert.DeserializeXNode(json, rootName);
        }
    }

五、事件處理

設置了菜單,這下須要處理事件了。跟咱們以前設計ASPX或者WinForm同樣,都要綁定按鈕的事件。這裏只是經過XML消息將請求傳遞過來。

經過「二、設置菜單"中具體的菜單內容,咱們便已經知道須要進行哪些事件處理了。對於按鈕類型爲view的,無須處理,它會自動跳轉至指定url.

須要處理的點擊事件:

1)贊一下

2)幫助

這個還要沿用上章中的事件處理器EventHandler來擴展處理。

具體的實現代碼吧:

class EventHandler : IHandler
    {
        /// <summary>
        /// 請求的xml
        /// </summary>
        private string RequestXml { get; set; }
        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="requestXml"></param>
        public EventHandler(string requestXml)
        {
            this.RequestXml = requestXml;
        }
        /// <summary>
        /// 處理請求
        /// </summary>
        /// <returns></returns>
        public string HandleRequest()
        {
            string response = string.Empty;
            EventMessage em = EventMessage.LoadFromXml(RequestXml);
            if (em != null)
            {
                switch (em.Event.ToLower())
                {
                    case ("subscribe"):
                        response = SubscribeEventHandler(em);
                        break;
                    case "click":
                        response = ClickEventHandler(em);
                        break;
                }
            }

            return response;
        }
        /// <summary>
        /// 關注
        /// </summary>
        /// <param name="em"></param>
        /// <returns></returns>
        private string SubscribeEventHandler(EventMessage em)
        {
            //回覆歡迎消息
            TextMessage tm = new TextMessage();
            tm.ToUserName = em.FromUserName;
            tm.FromUserName = em.ToUserName;
            tm.CreateTime = Common.GetNowTime();
            tm.Content = "歡迎您關注咱們,我是服務小二,有事您說話~\n\n";
            return tm.GenerateContent();
        }
        /// <summary>
        /// 處理點擊事件
        /// </summary>
        /// <param name="em"></param>
        /// <returns></returns>
        private string ClickEventHandler(EventMessage em)
        {
            string result = string.Empty;
            if (em != null && em.EventKey != null)
            {
                switch (em.EventKey.ToUpper())
                {
                    case "BTN_GOOD":
                        result = btnGoodClick(em);
                        break;
                    case "BTN_HELP":
                        result = btnHelpClick(em);
                        break;
                }
            }

            return result;
        }
        /// <summary>
        /// 贊一下
        /// </summary>
        /// <param name="em"></param>
        /// <returns></returns>
        private string btnGoodClick(EventMessage em)
        {
            //回覆歡迎消息
            TextMessage tm = new TextMessage();
            tm.ToUserName = em.FromUserName;
            tm.FromUserName = em.ToUserName;
            tm.CreateTime = Common.GetNowTime();
            tm.Content = @"謝謝您的支持!";
            return tm.GenerateContent();
        }
        /// <summary>
        /// 幫助
        /// </summary>
        /// <param name="em"></param>
        /// <returns></returns>
        private string btnHelpClick(EventMessage em)
        {
            //回覆歡迎消息
            TextMessage tm = new TextMessage();
            tm.ToUserName = em.FromUserName;
            tm.FromUserName = em.ToUserName;
            tm.CreateTime = Common.GetNowTime();
            tm.Content = @"有事找警察~";
            return tm.GenerateContent();
        }
      

調試效果

圖中點擊的幫助菜單,若是你們仔細觀察,會發現後面斷點進入第二次了,這是由於前面提到的,微信服務器5秒未回覆,就會重複嘗試發3次請求,因此就會有屢次進入斷點的效果。

最終效果

Demo下載

連接:點我下載  密碼:41dt

Git地址:https://github.com/XiaoYong666/-Demo

 

未完待續,持續填坑中。。。。

 

注:文中部份內容截取自:http://www.cnblogs.com/yank/p/3418194.html

相關文章
相關標籤/搜索