如果圖片失效:見(jiàn)【教程&工具】微信同步文章到Bear
在我日常工作中,我會(huì)將各種互聯(lián)網(wǎng)以及生活中產(chǎn)出的信息匯總到Bear,再通過(guò)Bear的云同步使我各個(gè)終端的信息保持一致。
以前在使用有道云筆記的時(shí)候,有個(gè)功能我很喜歡,就是當(dāng)看到一篇想收藏的文章的話(huà),就可以直接右上角發(fā)送到有道云筆記,如下圖:
順便一提:熊掌記是一款優(yōu)雅、靈活的寫(xiě)作筆記應(yīng)用。
回到正題,我現(xiàn)在面臨的需求是能不能在看到喜歡的文章的時(shí)候,也通過(guò)類(lèi)似于右上角分享一下就可以直接將文章同步到我各個(gè)終端上的Bear,最終成果如下:
解決方案
要實(shí)現(xiàn)上述的需求,我大概思考了如下的解決方案:
- 準(zhǔn)備一個(gè)微信號(hào)(這里直接稱(chēng)作小號(hào))專(zhuān)門(mén)接收待收藏到Bear的文章
- 編寫(xiě)一個(gè)服務(wù)監(jiān)控小號(hào)的消息,比如收到推文類(lèi)型的消息就進(jìn)行內(nèi)容提取
- 監(jiān)控服務(wù)將提取后的內(nèi)容發(fā)送到Bear(這里要求服務(wù)運(yùn)行在Mac OS上)
所以在繼續(xù)之前,你需要有以下條件:
- 基本的Python基礎(chǔ)知識(shí)(寫(xiě)小腳本Python真的很方便)
- 一臺(tái)裝有Bear的Mac OS
方案調(diào)研
上面的解決方案看起來(lái)還是挺好實(shí)現(xiàn),第一步不用多說(shuō),這年頭誰(shuí)沒(méi)個(gè)小號(hào),第二點(diǎn)的話(huà),我印象中Python是有個(gè)第三方庫(kù)可以直接監(jiān)聽(tīng)微信對(duì)應(yīng)賬號(hào)的消息。
因?yàn)檫@些第三方庫(kù)都是基于Web版的微信,所以在使用之前我想驗(yàn)證下此方案是否可行,剛準(zhǔn)備登錄網(wǎng)頁(yè)版微信,就直接提示:
<error><ret>1203</ret><message>To protect your account, logging in to WeChat via the web has been suspended. Use WeChat for Windows or WeChat for Mac to log in on a computer. Download WeChat for Windows or Mac at http://wechat.com.</message></error>
[圖片上傳失敗...(image-1157a-1571723347291)]
果不其然現(xiàn)在微信準(zhǔn)備加強(qiáng)Web版本的限制了,心里涼涼的,第二步還沒(méi)開(kāi)始就已經(jīng)被宣判死刑。
只能換個(gè)思路了,怎么辦。其實(shí)這一步走不通我還是能接受的,因?yàn)槲乙恢庇X(jué)得依賴(lài)Web版總有一天會(huì)掛掉,畢竟多了個(gè)依賴(lài)總是會(huì)增加復(fù)雜度。
能不能依靠客戶(hù)端?
我們知道,微信數(shù)據(jù)是有同步功能的,開(kāi)發(fā)過(guò)客戶(hù)端的都知道,這就意味著微信的數(shù)據(jù)必然保存一份在客戶(hù)端本地系統(tǒng)上。
所以對(duì)于第二點(diǎn)的解決思路就轉(zhuǎn)換成了如何獲取微信保存在客戶(hù)端本地的數(shù)據(jù),找到某個(gè)軟件的數(shù)據(jù)文件夾自然是很簡(jiǎn)單的事情,比如微信客戶(hù)端的數(shù)據(jù)就存放在:
# howie6879是我的用戶(hù)名,請(qǐng)自行替換
/Users/howie6879/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9
具體有如下目錄:
├── 988eebd1078a0d794bff2b6f5c8d5176
├── Avatar
├── CGI
├── CrashReport
├── KeyValue
├── MMResourceMgr
├── checkVersionFile
├── d41d8cd93400b204e9800998ecf8427e
├── f965739b566114f907dc394322e1e826
├── topinfo.data
├── upgradeHistoryFile
├── whatsNewVersionFile
└── wx.dat
8 directories, 5 files
不知道上面那三個(gè)32位的字符串大家看起來(lái)熟悉不熟悉:
一想到32,就是md5加密,我第一反應(yīng)就是對(duì)于每個(gè)登錄賬號(hào)的id加密值,我們先不管,直接進(jìn)去看更深一層的文件夾:
├── Account
├── Avatar
├── Contact
├── Favorites
├── FileStateSync
├── FunctionMsg
├── Group
├── Message
├── RevokeMsg
├── Session
├── Stickers
├── Sync
├── complexSearch
├── mmexpt
└── newabtest
15 directories, 0 files
Message出來(lái)了,這是不是我們想要的呢?再往下看里面的目錄:
├── MessageTemp
├── fts
├── msg_0.db
├── msg_0.db-backup
├── msg_0.db-shm
├── msg_0.db-wal
├── msg_1.db
如果你登錄過(guò)該臺(tái)電腦并同步過(guò)信息,那么不出意外會(huì)有挺多*.db后綴的文件。大膽地猜測(cè)一下,這是不是我們想要的聊天數(shù)據(jù)存放路徑呢?
不要管太多,先看看總不會(huì)錯(cuò),一般本地存儲(chǔ)的數(shù)據(jù)庫(kù),咱們程序員第一反應(yīng)應(yīng)該就是SQLite,要不要試試?
sqlite3 Message/msg_0.db
sqlite> .schema
Error: file is not a database
sqlite>
??提示不是數(shù)據(jù)庫(kù),此時(shí)陷入了瓶頸,怎么就不是數(shù)據(jù)庫(kù)了呢。反思一下,是不是打開(kāi)的姿勢(shì)不對(duì)。
會(huì)不會(huì)是加密了?依照這個(gè)思路,我了解到有一款基于SQLite的擴(kuò)展數(shù)據(jù)庫(kù)[SQLCipher](https://github.com/sqlcipher/sqlcipher,SQLCipher是一個(gè)在SQLite基礎(chǔ)之上進(jìn)行擴(kuò)展的開(kāi)源數(shù)據(jù)庫(kù),它主要是在SQLite的基礎(chǔ)之上增加了數(shù)據(jù)加密功能。
實(shí)踐證明,我猜想的是對(duì)的,接下來(lái)主要做的怎么打開(kāi)Message/msg_0.db這個(gè)文件并成功讀取里面的數(shù)據(jù)。
最后我參考到一份有意思的問(wèn)答,我就是參考這個(gè)問(wèn)答對(duì)數(shù)據(jù)庫(kù)進(jìn)行解密,這里我復(fù)述一下:
- 打開(kāi)微信,但是先不登錄
- 打開(kāi)終端,輸入
lldb -p $(pgrep WeChat) - 會(huì)看到進(jìn)入了
lldb,然后輸入br set -n sqlite3_key,按回車(chē) - 在
lldb中,輸入c,按回車(chē) - 打開(kāi)微信并掃碼登錄
- 然后回到
lldb中,輸入memory read --size 1 --format x --count 32 $rsi
此時(shí)就會(huì)得到以下類(lèi)似的輸出:
0x600003888340: 0xd1 0x05 0x29 0x04 0x75 0xc5 0x45 0x05
0x600003888348: 0x92 0x26 0xa1 0x65 0x95 0xe5 0x15 0x3f
0x600003888350: 0xf3 0xc7 0x43 0x85 0x05 0x35 0x45 0x3d
0x600003888358: 0x84 0xc8 0x64 0xe5 0x35 0x65 0x45 0xe2
去掉冒號(hào)前面的那一串,后面是四行八列的數(shù)據(jù),再去除掉0x、空格、\n等,就會(huì)得到一串64位的字符串,舉個(gè)例子:
df012f587cc546000025a56599e81530f9cc49800329423d8ec460e1386549e2
這就是我們進(jìn)入數(shù)據(jù)庫(kù)的鑰匙,接下來(lái),請(qǐng)安裝sqlcipher的相關(guān)軟件,如:
brew install sqlcipher
brew cask install db-browser-for-sqlite
讓我們用db-browser-for-sqlite打開(kāi)db后綴的文件看看有什么不一樣吧:
點(diǎn)擊OK,成功打開(kāi)!

隨便進(jìn)入一個(gè)表:
很顯然,我們成功獲取了本地的聊天記錄,總算將第二步流程打通了,如今我們可以監(jiān)控發(fā)送收藏文章的微信賬戶(hù)的聊天記錄,只要收到此賬號(hào)發(fā)來(lái)的推文消息,此時(shí)監(jiān)控服務(wù)可以立馬反應(yīng)過(guò)來(lái)并解析發(fā)送到Bear。
有個(gè)小問(wèn)題,怎么知道發(fā)推文的賬號(hào)在哪個(gè)庫(kù)哪個(gè)表呢?可以這樣看,在電腦上登錄發(fā)推文的賬號(hào),打開(kāi)文件userinfo.data:
cd /Users/howie6879/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/988eebd1023a0d794bff2b6f5c8d5176/Account
cat userinfo.data
大概輸出如下:
":BHPpx??127417592694754732??wxid_epXXXXXXXfj12? Howie6879?老胡的儲(chǔ)物柜?
這里很明顯我的wxid就是:wxid_epXXXXXXXfj12,那么對(duì)應(yīng)需要監(jiān)控的表名就是:Chat_md5(wxid_epXXXXXXXfj12),形式如同這樣
Chat_f965739xxxx114fxxxxc394322exxxx
隨后實(shí)現(xiàn)在庫(kù)里面找到對(duì)應(yīng)的表即可,我本機(jī)發(fā)現(xiàn)對(duì)應(yīng)賬戶(hù)的表存在于庫(kù) msg_5.db中。
接下來(lái)要做的事情就很簡(jiǎn)單了,就是將提取后的內(nèi)容發(fā)送到Bear,這里可以利用X callback url Scheme documentation,比如你在終端輸入:
open 'bear://x-callback-url/create?title=Test%20Bear&text=Hello%20Bear'
立馬就可以看到Bear自動(dòng)建立了一篇筆記
編碼實(shí)現(xiàn)
終于到了編碼階段,好心酸:
第一步,拿到必須要的常量:
- S_ACCOUNT_ID:微信發(fā)送賬戶(hù)ID,可以在
Account/userinfo.data下查看 - R_ACCOUNT_ID:微信接收賬戶(hù)ID,同上
- RAW_KEY:解密Key,就是上面介紹的64位字符串
- DB_PATH_TEM:定義的是消息DB路徑,比如:"/Users/howie6879/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/{0}/Message/"
定義這四個(gè)常量,接下來(lái)的事情就一帆風(fēng)順了哈,我將項(xiàng)目開(kāi)源在Github,地址見(jiàn)w2b,接下來(lái)我直接說(shuō)說(shuō)怎么用:
git clone https://github.com/howie6879/w2b
cd w2b
# 推薦使用pipenv 你也可以使用自己中意的環(huán)境構(gòu)建方式
pipenv install --python=/Users/howie6879/anaconda3/envs/python36/bin/python3.6 --skip-lock
# 運(yùn)行前需要填好配置文件
pipenv run python w2b/run.py
隨后,會(huì)有日志輸出:
[w2b] pipenv run python w2b/run.py
Loading .env environment variables…
[2019:09:13 09:16:35] INFO w2b 目標(biāo)表 Chat_f965739b676114fxxxxc394322e1e826 存在于庫(kù) msg_5.db
好,代碼跑起來(lái)后,接下來(lái)電腦上登錄你的小號(hào)(也就是接收微信文章的微信號(hào)),然后在手機(jī)上登錄發(fā)送文章的微信號(hào),最終成功就和文章一開(kāi)始的動(dòng)圖一樣了~
搞定收工,有興趣歡迎關(guān)注我的公眾號(hào):
