微信公眾號開發訊息推送以及圖文推送
阿新 • • 發佈:2018-12-18
今天給大家分享的關注公眾號自動推送圖文訊息,以及做一個超牛逼的機器人。
先看看效果。
發錯圖了。。。這是我昨天開發的一款機器人chu了會罵人啥都不會了。
我今天將它詞庫進行了更新和升級,接入了http://www.itpk.cn/ 機器人第三詞庫
先給你截圖:
機器人的配置:
詞庫資訊。可以自定義詞庫資訊
來看看進一步效果
是不是乖巧多了哈哈哈。
想不想把這個乖巧機器人帶走。。。。
來吧 給你們秀一波操作。看好了xiongder們別眨眼我要開始變形了。。。
不好意思忘了一件灰常重要的事情,忘了給你們看官方API文件了
第一步登入微信公眾平臺 現在開發-基本配置然後伺服器配置。如下圖
解釋含義:
伺服器地址(URL):伺服器接收訊息的的地址也就自己後臺處理邏輯的地方
Toke:需要配置到程式碼中。可以理解為密碼
訊息加解密金鑰(EncodingAESKey) 就是防止別人擷取你的訊息,可以選擇加密 我用的明文模式
來吧走個流程圖吧。哈哈哈哈。。
直接上程式碼。我程式碼上的註釋很清晰。我就不多解釋了。有什麼不明白隨時聯絡我。。
/** * 微信訊息推送的驗證。 * * @param request * @param response */ @RequestMapping(value = "sendMsg", method = RequestMethod.GET, produces = "text/html;charset=UTF-8") public void sendMsgget(HttpServletRequest request, HttpServletResponse response) { // 微信加密簽名 String signature = request.getParameter("signature"); // 時間戳 String timestamp = request.getParameter("timestamp"); // 隨機數 String nonce = request.getParameter("nonce"); // 隨機字串 String echostr = request.getParameter("echostr"); PrintWriter out = null; try { out = response.getWriter(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 通過檢驗signature對請求進行校驗,若校驗成功則原樣返回echostr,表示接入成功,否則接入失敗 if (SignUtil.checkSignature(signature, timestamp, nonce)) { out.print(echostr); } out.close(); out = null; } package cn.cnbuilder.utils; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; public class SignUtil { // 與介面配置資訊中的Token要一致 需要登入微信公眾號 private static String token = "xxxxxxxxxxx"; /** * 驗證簽名 * * @param signature * @param timestamp * @param nonce * @return */ public static boolean checkSignature(String signature, String timestamp, String nonce) { String[] arr = new String[] { token, timestamp, nonce }; // 將token、timestamp、nonce三個引數進行字典序排序 Arrays.sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 將三個引數字串拼接成一個字串進行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } content = null; // 將sha1加密後的字串可與signature對比,標識該請求來源於微信 return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false; } /** * 將位元組陣列轉換為十六進位制字串 * * @param byteArray * @return */ static String byteToStr(byte[] byteArray) { String strDigest = ""; for (int i = 0; i < byteArray.length; i++) { strDigest += byteToHexStr(byteArray[i]); } return strDigest; } /** * 將位元組轉換為十六進位制字串 * * @param mByte * @return */ private static String byteToHexStr(byte mByte) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] tempArr = new char[2]; tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; tempArr[1] = Digit[mByte & 0X0F]; String s = new String(tempArr); return s; } }
驗籤驗完了之後,我們開始接受訊息然後處理邏輯。
/** * 處理業務邏輯 * * @param request * @param response */ @RequestMapping(value = "sendMsg", method = RequestMethod.POST, produces = "text/html;charset=UTF-8") public void sendMsgPost(HttpServletRequest request, HttpServletResponse response) { // 呼叫核心業務類接收訊息、處理訊息 try { String respMessage = processRequest(request); System.err.println(respMessage); // 我們這裡處理的是utf-8 微信要的是ios8859-1這是坑啊。。。。。 byte[] uMessage = respMessage.getBytes("UTF-8");// 編碼:字串變成位元組陣列 輸入 引數(編碼表) String iMessage = new String(uMessage, "ISO8859-1");// 解碼:位元組陣列變成字串,String引數(陣列,編碼表) 輸出 if (respMessage != null) { // 響應訊息 PrintWriter out = response.getWriter(); out.print(iMessage); out.close(); } } catch ( Exception e) { // 也許大概不會走到這裡除非異常了。。。。。。 } } public String processRequest(HttpServletRequest request) { String respMessage = null; try { // xml請求解析 Map<String, String> requestMap = MessageUtil.parseXml(request); // 傳送方帳號(open_id) String fromUserName = requestMap.get("FromUserName"); // 公眾帳號 String toUserName = requestMap.get("ToUserName"); // 訊息型別 String msgType = requestMap.get("MsgType"); TextMessage textMessage = new TextMessage(); textMessage.setToUserName(fromUserName); textMessage.setFromUserName(toUserName); textMessage.setCreateTime(new Date().getTime()); textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); // 文字訊息 if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { // 接收使用者傳送的文字訊息內容 String content = requestMap.get("Content"); System.err.println(content); // 建立圖文訊息 NewsMessage newsMessage = new NewsMessage(); newsMessage.setToUserName(fromUserName); newsMessage.setFromUserName(toUserName); newsMessage.setCreateTime(new Date().getTime()); newsMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_NEWS); List<Article> articleList = new ArrayList<Article>(); // 單圖文訊息 if ("yifan".equals(content)) { Article article = new Article(); article.setTitle("歡迎關注KingYiFan's Blog"); article.setDescription("點選進入詳情"); article.setPicUrl( "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1521808337402&di=515fdc032be051f5085c3f9c03af5646&imgtype=0&src=http%3A%2F%2Fwww.vvfeng.com%2Fdata%2Fupload%2Fueditor%2F20170327%2F58d8c88855d9f.jpg"); article.setUrl("http://cnbuilder.cn/"); articleList.add(article); // 設定圖文訊息個數 newsMessage.setArticleCount(articleList.size()); // 設定圖文訊息包含的圖文集合 newsMessage.setArticles(articleList); // 將圖文訊息物件轉換成xml字串 respMessage = MessageUtil.newsMessageToXml(newsMessage); } else { // 機器人api String jiqiren = HttpClientUtils.sendGetUtF8("http://i.itpk.cn/api.php", "limit=2&api_key=xxxxx&api_secret=xxxx&type=json&question=" + content); textMessage.setContent(jiqiren); respMessage = MessageUtil.textMessageToXml(textMessage); } // 事件處理開始 } else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) { NewsMessage newsMessage = new NewsMessage(); newsMessage.setToUserName(fromUserName); newsMessage.setFromUserName(toUserName); newsMessage.setCreateTime(new Date().getTime()); newsMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_NEWS); // 事件型別 String eventType = requestMap.get("Event"); List<Article> articleList = new ArrayList<Article>(); if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) { Article article = new Article(); article.setTitle("歡迎關注KingYiFan's Blog"); article.setDescription("點選進入詳情"); article.setPicUrl( "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1521808337402&di=515fdc032be051f5085c3f9c03af5646&imgtype=0&src=http%3A%2F%2Fwww.vvfeng.com%2Fdata%2Fupload%2Fueditor%2F20170327%2F58d8c88855d9f.jpg"); article.setUrl("http://cnbuilder.cn/"); articleList.add(article); // 設定圖文訊息個數 newsMessage.setArticleCount(articleList.size()); // 設定圖文訊息包含的圖文集合 newsMessage.setArticles(articleList); // 將圖文訊息物件轉換成xml字串 respMessage = MessageUtil.newsMessageToXml(newsMessage); try { // 儲存使用者資訊 wxMsgService.savaWxInfo(fromUserName); } catch (Exception e) { return respMessage; } } } else if (msgType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) { // 取消關注,使用者接受不到我們傳送的訊息了,可以在這裡記錄使用者取消關注的日誌資訊 } } catch (Exception e) { e.printStackTrace(); } return respMessage; } package cn.cnbuilder.utils; import java.io.InputStream; import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; import cn.cnbuilder.entity.wx.sendMsg.Article; import cn.cnbuilder.entity.wx.sendMsg.MusicMessage; import cn.cnbuilder.entity.wx.sendMsg.NewsMessage; import cn.cnbuilder.entity.wx.sendMsg.TextMessage; public class MessageUtil { /** * 返回訊息型別:文字 */ public static final String RESP_MESSAGE_TYPE_TEXT = "text"; /** * 返回訊息型別:音樂 */ public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; /** * 返回訊息型別:圖文 */ public static final String RESP_MESSAGE_TYPE_NEWS = "news"; /** * 請求訊息型別:文字 */ public static final String REQ_MESSAGE_TYPE_TEXT = "text"; /** * 請求訊息型別:圖片 */ public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; /** * 請求訊息型別:連結 */ public static final String REQ_MESSAGE_TYPE_LINK = "link"; /** * 請求訊息型別:地理位置 */ public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; /** * 請求訊息型別:音訊 */ public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; /** * 請求訊息型別:推送 */ public static final String REQ_MESSAGE_TYPE_EVENT = "event"; /** * 事件型別:subscribe(訂閱) */ public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; /** * 事件型別:unsubscribe(取消訂閱) */ public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; /** * 事件型別:CLICK(自定義選單點選事件) */ public static final String EVENT_TYPE_CLICK = "CLICK"; /** * 解析微信發來的請求(XML) * * @param request * @return * @throws Exception */ @SuppressWarnings("unchecked") public static Map<String, String> parseXml(HttpServletRequest request) throws Exception { // 將解析結果儲存在HashMap中 Map<String, String> map = new HashMap<String, String>(); // 從request中取得輸入流 InputStream inputStream = request.getInputStream(); // 讀取輸入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子節點 List<Element> elementList = root.elements(); // 遍歷所有子節點 for (Element e : elementList) map.put(e.getName(), e.getText()); // 釋放資源 inputStream.close(); inputStream = null; return map; } /** * 文字訊息物件轉換成xml * * @param textMessage 文字訊息物件 * @return xml */ public static String textMessageToXml(TextMessage textMessage) { xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } /** * 音樂訊息物件轉換成xml * * @param musicMessage 音樂訊息物件 * @return xml */ public static String musicMessageToXml(MusicMessage musicMessage) { xstream.alias("xml", musicMessage.getClass()); return xstream.toXML(musicMessage); } /** * 圖文訊息物件轉換成xml * * @param newsMessage 圖文訊息物件 * @return xml */ public static String newsMessageToXml(NewsMessage newsMessage) { xstream.alias("xml", newsMessage.getClass()); xstream.alias("item", new Article().getClass()); return xstream.toXML(newsMessage); } /** * 擴充套件xstream,使其支援CDATA塊 * * @date 2013-05-19 */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 對所有xml節點的轉換都增加CDATA標記 boolean cdata = true; @SuppressWarnings("unchecked") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); } package cn.cnbuilder.entity.wx.sendMsg; public class TextMessage extends BaseMessage { // 訊息內容 private String Content; public String getContent() { return Content; } public void setContent(String content) { Content = content; } } package cn.cnbuilder.entity.wx.sendMsg; import java.util.List; public class NewsMessage extends BaseMessage{ // 圖文訊息個數,限制為10條以內 private int ArticleCount; // 多條圖文訊息資訊,預設第一個item為大圖 private List<Article> Articles; public int getArticleCount() { return ArticleCount; } public void setArticleCount(int articleCount) { ArticleCount = articleCount; } public List<Article> getArticles() { return Articles; } public void setArticles(List<Article> articles) { Articles = articles; } } package cn.cnbuilder.entity.wx.sendMsg; public class Article { // 圖文訊息名稱 private String Title; // 圖文訊息描述 private String Description; // 圖片連結,支援JPG、PNG格式,較好的效果為大圖640*320,小圖80*80,限制圖片連結的域名需要與開發者填寫的基本資料中的Url一致 private String PicUrl; // 點選圖文訊息跳轉連結 private String Url; public String getTitle() { return Title; } public void setTitle(String title) { Title = title; } public String getDescription() { return null == Description ? "" : Description; } public void setDescription(String description) { Description = description; } public String getPicUrl() { return null == PicUrl ? "" : PicUrl; } public void setPicUrl(String picUrl) { PicUrl = picUrl; } public String getUrl() { return null == Url ? "" : Url; } public void setUrl(String url) { Url = url; } } package cn.cnbuilder.entity.wx.sendMsg; public class BaseMessage { // 開發者微訊號 private String ToUserName; // 傳送方帳號(一個OpenID) private String FromUserName; // 訊息建立時間 (整型) private long CreateTime; // 訊息型別(text/image/location/link) private String MsgType; // 訊息id,64位整型 private long MsgId; public String getToUserName() { return ToUserName; } public void setToUserName(String toUserName) { ToUserName = toUserName; } public String getFromUserName() { return FromUserName; } public void setFromUserName(String fromUserName) { FromUserName = fromUserName; } public long getCreateTime() { return CreateTime; } public void setCreateTime(long createTime) { CreateTime = createTime; } public String getMsgType() { return MsgType; } public void setMsgType(String msgType) { MsgType = msgType; } public long getMsgId() { return MsgId; } public void setMsgId(long msgId) { MsgId = msgId; } } package cn.cnbuilder.utils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import net.sf.json.JSONObject; public class HttpClientUtils { /** * 向指定URL傳送GET方法的請求 * * @param url 傳送請求的URL * @param param 請求引數,請求引數應該是 name1=value1&name2=value2 的形式。 * @return URL 所代表遠端資源的響應結果 */ public static String sendGet(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString); // 開啟和URL之間的連線 URLConnection connection = realUrl.openConnection(); // 設定通用的請求屬性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); // 建立實際的連線 connection.connect(); // 獲取所有響應頭欄位 Map<String, List<String>> map = connection.getHeaderFields(); // 遍歷所有的響應頭欄位 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定義 BufferedReader輸入流來讀取URL的響應 in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "gbk")); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("傳送GET請求出現異常!" + e); e.printStackTrace(); } // 使用finally塊來關閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定URL傳送GET方法的請求 * * @param url 傳送請求的URL * @param param 請求引數,請求引數應該是 name1=value1&name2=value2 的形式。 * @return URL 所代表遠端資源的響應結果 */ public static String sendGetUtF8(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString); // 開啟和URL之間的連線 URLConnection connection = realUrl.openConnection(); // 設定通用的請求屬性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); // 建立實際的連線 connection.connect(); // 獲取所有響應頭欄位 Map<String, List<String>> map = connection.getHeaderFields(); // 遍歷所有的響應頭欄位 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定義 BufferedReader輸入流來讀取URL的響應 in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("傳送GET請求出現異常!" + e); e.printStackTrace(); } // 使用finally塊來關閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 傳送POST方法的請求 * * @param url 傳送請求的 URL * @param param 請求引數,請求引數應該是 name1=value1&name2=value2 的形式。 * @return 所代表遠端資源的響應結果 */ public static String sendPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 開啟和URL之間的連線 URLConnection conn = realUrl.openConnection(); // 設定通用的請求屬性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 傳送POST請求必須設定如下兩行 conn.setDoOutput(true); conn.setDoInput(true); // 獲取URLConnection物件對應的輸出流 out = new PrintWriter(conn.getOutputStream()); // 傳送請求引數 out.print(param); // flush輸出流的緩衝 out.flush(); // 定義BufferedReader輸入流來讀取URL的響應 in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("傳送 POST 請求出現異常!" + e); e.printStackTrace(); } // 使用finally塊來關閉輸出流、輸入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } public static String doPostForJson(String url, String jsonParams) { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(url); RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(180 * 1000) .setConnectionRequestTimeout(180 * 1000).setSocketTimeout(180 * 1000).setRedirectsEnabled(true).build(); httpPost.setConfig(requestConfig); httpPost.setHeader("Content-Type", "application/json"); // try { httpPost.setEntity(new StringEntity(jsonParams, ContentType.create("application/json", "utf-8"))); System.out.println("request parameters" + EntityUtils.toString(httpPost.getEntity())); HttpResponse response = httpClient.execute(httpPost); String str = EntityUtils.toString(response.getEntity()); /** 把json字串轉換成json物件 **/ JSONObject fromObject = JSONObject.fromObject(str); return fromObject.toString(); } catch (Exception e) { e.printStackTrace(); return "KingYiFan溫馨提示你:post請求出異常了" + e.getMessage().toString(); } finally { if (null != httpClient) { try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } } } } package cn.cnbuilder.entity.wx.sendMsg; public class Music { // 音樂名稱 private String Title; // 音樂描述 private String Description; // 音樂連結 private String MusicUrl; // 高質量音樂連結,WIFI環境優先使用該連結播放音樂 private String HQMusicUrl; public String getTitle() { return Title; } public void setTitle(String title) { Title = title; } public String getDescription() { return Description; } public void setDescription(String description) { Description = description; } public String getMusicU package cn.cnbuilder.entity.wx.sendMsg; public class MusicMessage { // 音樂 private Music Music; public Music getMusic() { return Music; } public void setMusic(Music music) { Music = music; } } rl() { return MusicUrl; } public void setMusicUrl(String musicUrl) { MusicUrl = musicUrl; } public String getHQMusicUrl() { return HQMusicUrl; } public void setHQMusicUrl(String musicUrl) { HQMusicUrl = musicUrl; } } package cn.cnbuilder.entity.wx.sendMsg; public class MusicMessage { // 音樂 private Music Music; public Music getMusic() { return Music; } public void setMusic(Music music) { Music = music; } }
這就是微信公眾平臺訊息回覆的教程,哪裡不懂可以私信我哦!
鼓勵作者寫出更好的技術文件,就請我喝一瓶哇哈哈哈哈哈哈哈。。
微信:
支付寶:
感謝一路支援我的人。。。。。
Love me and hold me
QQ:69673804(16年老號)
EMAIL:[email protected]
友鏈交換
如果有興趣和本部落格交換友鏈的話,請按照下面的格式在評論區進行評論,我會盡快新增上你的連結。
網站名稱:KingYiFan’S Blog
網站地址:http://blog.cnbuilder.cn
網站描述:年少是你未醒的夢話,風華是燃燼的彼岸花。
網站Logo/頭像:頭像地址