1. 程式人生 > >JAVA版微信機器人(非公眾號)【L】

JAVA版微信機器人(非公眾號)【L】

JAVA微信機器人(一)

我這是強調,我做的是個人聊天微信機器人,非公眾號。就是你和你朋友聊天時用的微信。以下是微信機器人和我朋友的聊天記錄。

首先,微信並沒有給我們提供任何介面,或者功能去開發微信機器人。那麼我們唯一的渠道就是用WEB微信,模擬瀏覽器,獲取聊天記錄,然後智慧回覆。當然說到智慧我們可以用網上一些智回覆外掛。比如圖靈。圖靈的開發不難,因為別人提供好了介面給你用。但是微信就不一樣了。我們先分析WEB微信的登入—聊天的過程。

1、WEB微信網址https://wx.qq.com/

大家訪問時這個網址給我們的就是一個二維碼。二維碼裡面無非就是一個網址。手機掃描後,用微信瀏覽器訪問這個網址,並傳送相應的資料。

好吧。我們先分析二維碼生成流程。

以下先用轉載,暫時沒時間自己寫。

昨天是週末,在家閒得無聊,於是去weiphone.com逛了一圈,偶然發現有人發了一帖叫《微信 for Mac》,這勾起了我的好奇心,國內做Mac開發的人確實很少,對於那些能夠獨自開發一些Mac第三方工具的開發者我都表示很敬畏,於是點進去看了一個究竟,如果你們好奇也可以點進去看個明白,我最終得出的結論就是:坑爹呢這是!直接用一個WebView去載入了wx.qq.com這個網頁也敢自稱是微信For Mac?對於這種欺騙使用者的行為我十分不屑,同時也讓我在思考在微信不提供API的環境下開發一款原生的微信Mac版本是否可行,最有可能的就是去分析微信Web版本的通訊過程,然後在程式中模擬這個流程,在我苦苦研究了一個下午之後,終於摸透了這個過程,並用程式實現了大部分功能,下面就詳細解說一下微信Web版的流程:

1.微信伺服器返回一個會話ID

微信Web版本不使用使用者名稱和密碼登入,而是採用二維碼登入,所以伺服器需要首先分配一個唯一的會話ID,用來標識當前的一次登入,通過請求地址:

伺服器會返回如下的字串:

window.QRLogin.code = 200; window.QRLogin.uuid = “DeA6idundY9VKn”;

而這個DeA6idundY9VKn字串就是微信伺服器返回給我們的ID。

2.通過會話ID獲得二維碼

既然微信Web版本是通過二維碼進行登入,如何獲得這個隨機的二維碼呢?答案就是利用剛才獲得的ID去請求伺服器生成的二維碼,通過上面的ID我們組合得到以下的URL地址:

該請求返回的便是我們需要的二維碼,此時需要使用者在微信的手機版本中掃描這個二維碼(我就搞不明白微信官方是如何想的,登入Web版本竟然還需要手機微信去配合登入,難道沒有考慮我被迫選擇Web微信就是因為手機不在身邊這樣的情形麼?)

3.輪詢手機端是否已經掃描二維碼並確認在Web端登入

當獲得二維碼之後,就需要使用者去手機端去掃描二維碼,並獲得使用者的授權,此時我們並不知道使用者何時完成這個操作,所以我們只有輪詢,而輪詢的地址就是:

如果伺服器返回:

window.code=201;

則說明此時使用者在手機端已經完成掃描,但還沒有點選確認;

如果伺服器返回:

window.redirect_uri=一個URL地址

則說明此時使用者已經在手機端完成了授權過程,儲存下這個URL地址下一步驟中使用。

4.訪問登入地址,獲得uin和sid

通過訪問上一步驟中獲得的URL地址,可以在伺服器返回的Cookies中獲得到wxuin和wxsid這兩個值,這兩值在後續的通訊過程中都要使用到這兩個值,並且Cookies中也需要包括這兩項。

5.初使化微信資訊

前面的步驟算是完成了這個複雜的登入過程,如果我們需要使用微信就需要獲得當前使用者的資訊、好友列表等,還有一個關鍵的就是同步資訊(後續與伺服器輪詢中需要使用同步資訊),通過訪問以下的連結:

訪問該連結需要使用POST,並且在Body中帶上以下的JSON資訊:

12 {"BaseRequest":{"Uin":"2545437902","Sid":"QfLp+Z+FePzvOFoG","Skey":"","DeviceID":"e1615250492"}}

這個JSON串中Uin和Sid分別是上面步驟中獲得的那兩個Cookie值,DeviceID是一個本地生成的隨機字串(分析了官方的總是e+一串數字,所以我們也保持這樣的格式)。

伺服器就會返回一個很長的JSON串,這其中包括:BaseResponse中的值用來表示請求狀態碼,ContactList主要用來表示聯絡人(此列表不全,只包括了類似通訊錄助手、檔案助手、微信團隊和一些公眾帳號等,後面會通過另一介面去獲得更全面的資訊),SyncKey是使用者與伺服器同步的資訊,User就是當前登入使用者自己的資訊。

6.獲得所有的好友列表

在上一步驟中已經獲得了部分好友和公眾帳號,如果需要獲得完整的好友資訊,就需要訪問以下的連結:

訪問該連結同樣需要POST方式,但Body為空JSON:{},伺服器對身份的判定是通過Cookies,所以需要保持之前訪問的Cookies不被修改(在Objective-C中會自動儲存相關的Cookies,無需程式特殊處理),在返回的JSON串中,MemberList中就包含了所有的好友資訊。

7.保持與伺服器的資訊同步

與伺服器保持同步需要在客戶端做輪詢,該輪詢的URL如下:

其中的引數r和_都是time,sid,uin,deviceid與上面步驟的值相對應,此處的synkey是上步步驟獲得的同步鍵值,但需要按一定的規則組合成以下的字串:

1_124125|2_452346345|3_65476547|1000_5643635

就是將鍵和值用_隔開,不同的鍵值對用|隔開,但記得|需要URL編碼成%7C,通過訪問上面的地址,會返回如下的字串:

window.synccheck={retcode:”0”,selector:”0”}

如果retcode中的值不為0,則說明與伺服器的通訊有問題了,但具體問題我就無法預測了,selector中的值表示客戶端需要作出的處理,目前已經知道當為6的時候表示有訊息來了,就需要去訪問另一個介面獲得新的訊息。

8.獲得別人發來的訊息

當一個步驟中知道有新訊息時,就需要去獲取訊息內容,通過訪問以下的連結:

上面連結中的引數sid對應上面步驟中的值,r為時間,訪問連結需要使用POST方式,Body中包括JSON串,該JSON串格式如下:

123 {"BaseRequest" : {"Uin":2545437902,"Sid":"QfLp+Z+FePzvOFoG"},"SyncKey" : {"Count":4,"List":[{"Key":1,"Val":620310295},{"Key":2,"Val":620310303},{"Key":3,"Val":620310285},{"Key":1000,"Val":1377479086}]},"rr" :1377482079876};

以下的資訊中BaseRequest中包括的Uin與Sid與上面步驟中的值對應,SyncKey也是上面步驟中獲得的同步鍵值對,rr為時間,訪問成功之後伺服器會返回一個JSON串,其中AddMsgList中是一個數組,包含了所有新訊息。

9.向用戶傳送訊息

1234567891011121314151617 { "BaseRequest":{ "DeviceID" : "e441551176", "Sid" : "S8wNi91Zry3024eg", "Skey" : "F820928BBA5D8ECA23448F076D2E8A915E1349E9FB4F4332", "Uin" : "2545437902" }, "Msg" : { "ClientMsgId" : 1377504862158, "Content" : "hello", "FromUserName" : "wxid_2rrz8g8ezuox22", "LocalID" : 1377504862158, "ToUserName" : "wxid_j4nu420ojhsr21", "Type" : 1 }, "rr" = 1377504864463}

其中BaseRequest都是授權相關的值,與上面的步驟中的值對應,Msg是對訊息的描述,包括了傳送人與接收人,訊息內容,訊息的型別(1為文字),ClientMsgId和LocalID由本地生成。rr可用當前的時間。
在返回JSON結果中BaseResponse描述了傳送情況,Ret為0表示傳送成功。