我也來打造一個個人閱讀追蹤系統

coding01發表於2017-10-08

國慶放假期間,偶然發現這篇文章《Serverless實戰:打造個人閱讀追蹤系統》insights.thoughtworks.cn/serverless-…,太吸引我了。

進入網際網路時代,知識的獲取成本變得前所未有的低廉,但是無論再好的知識,若是沒有對個人產生價值的話,那也只不過是一種資訊噪音而已

隨著時間的推移,稍後閱讀的 Instapaper 裡面的文章將會變得越來越多,就像我們在程式碼中所註釋的 TODO:可能就變成了Never Do,「稍後閱讀」也是一樣地被廣為詬病:Read it Later = Read Never。其實我發現文章堆積的一個永恆痛點就是沒有有效的方式追蹤自己的閱讀需求與能力,其核心原因在於閱讀的速度趕不上新增的速度。從而沒辦法視覺化的評估閱讀進度、合理安排閱讀計劃,也就沒辦法給予自己適當的獎勵,長此以往必然將失去閱讀的動力。

稍後閱讀中永遠讀不完的痛點:缺乏追蹤

摘自:《Serverless實戰:打造個人閱讀追蹤系統》

在國慶「探親」和「歸隱山林」的我,利用「深夜」時間邊學寫實踐,再結合自己的一些工具和使用場景,整理成這篇文章——「高大上的個人閱讀追蹤系統」

目標也很簡單:

跟蹤自己的閱讀習慣和統計閱讀量,別讓「稍後閱讀」變成了「再也不讀」了

流程

Architecture - Content Management -1-
Architecture - Content Management -1-

主要過程:根據文章閱讀來源,利用 Workflow 將文章儲存到 Instapaper 中,同時觸發 IFTTT Applet 在 Github 和 ZenHub 中建立 Issue 來跟蹤閱讀情況;然後再有空的時候閱讀 Instapaper 中的文章,並對文章進行歸檔,同時觸發 IFTTT,Close Issue,並利用 ZenHub的圖表功能,並對閱讀進行彙總和後續分析。將「閱讀」這件事形成「閉環」。

工具

1. Instapaper
2. ZenHub
3. IFTTT
4. LeanCloud
5. GitHub
6. Workflow
7. 印象筆記
8. Feedly Rss 閱讀器

閱讀文章來源

主要有「Feedly Rss 閱讀器」、「微信」和「網站」三種來源。

通過 Feedly 方式很簡單的,只要將喜歡文章分享到 Instapaper 上即可。

當從微信公眾號或者網站看到好文章時,也只需「複製」文章的連結,下滑手機,使用 Workflow 神器 —— 「Clipboard to Instapaper」,儲存到 Instapaper 上

WechatIMG120
WechatIMG120

Workflow 下載連結:
workflow.is/workflows/7…

IFTTT 橋接器

IFTTT 的理念就像它的名字 If This Then That ,可以將兩個不同的服務關聯到一起,最典型的例子就是「如果明天下雨,那麼就提醒我帶雨傘」,在這個設定下,IFTTT 會定時查詢第二天的天氣預報,如果天氣預報返回的結果是第二天下雨,那麼 IFTTT 就會給我設定的手機號傳送簡訊,通知我第二天出門別忘了帶傘。

瞭解 IFTTT 可參考:sspai.com/post/39258

IFTTT:新增Instapaper文章後自動建立GitHub Issue

得益於IFTTT非常豐富的第三方服務,IFTTT可以直接建立Instapaper與GitHub Issue相整合的Applet:If new item saved, then create a new issue – IFTTT,就可以在當Instapaper新增文章的時候,自動在GitHub所指定的倉庫Issues · JimmyLv/reading 中建立一個新的Issue並新增相應的標題、連結以及描述等相關資訊。

當從 Workflow 或者 Feedly 閱讀器建立一條 Instarpaper 記錄時,可以觸發 IFTTT 的 Then 條件時,建立 GitHub 一條 Issue。

首先需要建立一個 Repository,來存放 Issues,如本文建立的:github.com/fanly/readi…

有了 Repository,就可以直接利用 IFTTT 建立自己的 Applet。

當接收到 IFTTT 的 notification 時,並且在 Activity 可以看到每次執行 Applet 的狀態:

在 GitHub 中就能看到了建立了對應的 Issue:

整個操作流程很方便 。如:在微信公眾號看到一篇好文章,先複製文章連結,接著調起 Workflow,將文章儲存到 Instapaper 中,最後 IFTTT 會實時監控 Instapaper,發現有新的 item created,就會觸發 Then 語句,將文章的 title 和 des,以及連結儲存到 Github 的 Issue 中。

如果說只是需要將想看的文章放到 Instapaper。我想到這就可以結束了。根據這篇《Serverless實戰:打造個人閱讀追蹤系統》文章:

但僅僅只是新增一個 Issue 還不夠,這時候還需要將這個 Issue 加入到指定的 Milestone 以便利用 ZenHub 的圖表功能。

這裡就需要利用 GitHub 的 Webhooks 和需要我們編寫程式碼,來接收 Webhooks 請求後,建立 Issue 的 Milestone 值和建立 ZenHub。

GitHub

GitHub,作為開發人員,對它再熟悉不過了,如何很好使用 Github 是程式設計師的必修課。Webhooks 的使用可以作為很多第三方的模仿物件。每個 Webhook 都可以定製化,在什麼情況下觸發 Webhook,這點和 IFTTT 的 IF 很像,只是 then 交給了我們自己接收處理。

這裡建立 ZenHub 時,會選擇和哪個 Repository 繫結,同理,也是在 Webhooks 建立一條 Webhook 來接收 Repository 的變化。

GitHub 的 api 文件也是我們學習的地方。如需要「更新Issue 的 milestone」:

更多的 GitHub API 檢視:developer.github.com/v3/

有了介面,我們還需要許可權 (access token),在 github.com/settings/to… 中建立 personal access token:

有了 Webhook,GitHub API,和 access token,萬事俱備只欠東風了。

Serverless 初探和 LeanCloud 選擇

Serverless 架構,或者稱為無伺服器架構,是最近幾年新出來的一種架構風格。對於 Serverless 來說,只是使用者不用更多的去關注和考慮伺服器的相關內容和配置了,甚至也不需要再去考慮伺服器的規格大小、儲存空間、頻寬、自動擴縮容問題等等;同時,也不需要再對伺服器進行運維了,無需不斷的打系統補丁、應用補丁、無需進行資料備份、軟體配置、環境更新等工作了。

但是沒有伺服器,如何來將程式、應用執行起來呢?這裡要介紹的是 Serverless 包含的兩個概念:函式即服務,Function as a Service FaaS,後端即服務,Backend as a Service BaaS。

總之一句話:「只關注實現的功能,剩下的交給 Serverless 服務商處理了。

更多 Serverless 的知識可以參考:

www.qcloud.com/community/a…

正如《Serverless實戰:打造個人閱讀追蹤系統》作者使用的是 Webtask,國內如騰訊雲、阿里雲,國外的 Oracle 開源的 Fn project (github.com/fnproject/f…) 等,這幾天我都嘗試了一遍,但個人覺得國外的訪問多多少少一點「跨國」不穩定,國內的兩家都需要去配置一些「東西」,我試了試還是決定暫時不用,各位看官可以嘗試的。

最後我還是使用 LeanCloud (leancloud.cn) Nodejs 雲引擎 —— 更多的是一個容器,而且完全可以充當 Serverles 來操作,具體的開發可以參考官網,而且本人對 LeanCloud 情有獨鍾。

注:在之前的文章中,介紹如何開發公眾號自動回覆功能時,使用的也是 LeanCloud。

搭建公眾號自動回覆功能

讓我們開始寫接收 Webhook 的程式碼吧:

server.route({
        method: 'POST',
        path: '/*****',
        handler: function (request, reply) {
            const GITHUB_ACCESS_TOKEN = '******************'
            const ZENHUB_ACCESS_TOKEN = '******************'
            const REPO_ID = '*******'
            // const { GITHUB_ACCESS_TOKEN, ZENHUB_ACCESS_TOKEN } = req.webtaskContext.secrets
            const { action, issue } = request.payload
            const { url, title, html_url, number, body } = issue

            console.info(`[BEGIN] issue updated with action: ${action}`)

            if (action === 'opened') {
                // 儲存資料到 lean

                let read = new Read();
                // const book = request.payload;
                read.set('title', title);
                read.set('number', number);
                read.set('url', url);
                read.set('body', pub.reconvert(body));
                read.save().then(function (blog) {
                    // 成功儲存之後,執行其他邏輯.
                    console.log('成功儲存:New object created with objectId: ' + read.id);
                    // reply(blog);
                }, function (error) {
                    // 失敗之後執行其他邏輯
                    console.log('Failed to create new object, with error message: ' + error.message);
                    // return reply(Boom.wrap(error, 'error'));
                });

                fetch(`${url}?access_token=${GITHUB_ACCESS_TOKEN}`, {
                    method: 'PATCH',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ milestone: 1}),
                }).then(
                    () => console.info(`[END] set milestone successful! ${html_url}`),
                    (e) => reply(e)
                )
            } else if (action === 'milestoned') {
                fetch(`https://api.zenhub.io/p1/repositories/${REPO_ID}/issues/${number}/estimate?access_token=${ZENHUB_ACCESS_TOKEN}`, {
                    method: 'PUT',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ estimate: 1 }),
                }).then(
                    () => console.info(`[END] Set estimate successful! ${html_url}`),
                    (e) => console.error(`[END] Failed to set estimate! ${html_url}`, e)
                )
            }

            reply({ message: 'issue updated!' })
        }
    });複製程式碼

相信大家都能看得懂該功能,當接收到 Issue 的 opened事件時,先將 Issue主要欄位儲存到 LeanCloud中,便於後續統計和 search 操作,然後再請求 GitHub 和 ZenHub 的介面做對應的處理。

到目前為止我們算完成了「稍後閱讀」文章的第一步了。但這不是我們的目標,我們的目標是:當在 Instapaper 閱讀完一篇文章後,對文章進行歸檔,然後更新 GitHub Issues 來 close 對應的 Issue,這樣才會利用到 ZenHub 的統計檢視功能。

所以需要再建立一個 IFTTT Applet 來完成對應的操作,但IFTTT 沒有對 close issue 的 Then 操作,需要利用到 IFTTT 的 Webhook 功能,即將 close issue 的方法由我們自己來寫,這時候我們又需要利用「Serverless」了。

close issue

Issue close 函式:

server.route({
        method: 'GET',
        path: '/******',
        handler: function (request, reply) {
            const GITHUB_ACCESS_TOKEN = '********'

            const words = request.query.title
            // console.log('get title' + title)

            // const words = pub.ascii(title);

            console.log('get words ' + words)

            const titleQuery = new AV.Query(Read)
            titleQuery.contains('title', words);

            const bodyQuery = new AV.Query(Read)
            bodyQuery.contains('body', words);

            const wordsQuery = AV.Query.or(titleQuery, bodyQuery);

            wordsQuery.descending('createdAt');
            wordsQuery.limit(1);
            wordsQuery.find().then(function (results) {
                console.log('get results' + JSON.stringify(results))
                if (results.length === 1) {
                    let url = results[0].get('url')
                    console.log(url)

                    fetch(`${url}?access_token=${GITHUB_ACCESS_TOKEN}`, {
                        method: 'PATCH',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ state: 'closed' }),
                    }).then(() => {
                        console.info(`[END] issue closed successful! ${url}`)
                    })
                        .catch(err => console.error(err))
                }
                reply({ message: 'issue closed!' })
            }, function (error) {
                reply({ message: 'error' })
            });
        }
    });複製程式碼

函式即功能,功能即服務

總結

有了這一整套「稍後閱讀」流程,和閱讀後歸檔更新 GitHub 和 ZenHub Issue 狀態來追蹤閱讀。閱讀這件事就形成閉環了,我們就可以知道了每天甚至每週一共讀了多少文章,有多少文章「死在 Instapaper」中,每篇文章的閱讀處理時間大概多長時間等等。

至於其他統計,和統計之後,如何來提升我們的閱讀和如何對文章進行分類等操作,有待於繼續完善。

最後附上一個對於喜歡的文章我們如何進行彙總呢,這裡我還是使用 IFTTT 的 Applet,當你對某一篇文章喜歡時,只要觸碰「❤️」按鈕,即可將文章彙總到印象筆記中,看圖不說話:

最後在印象筆記中:

這個也將結合到我之前的文章:《我是這麼製作「coding01 日報」的》我是這麼製作「coding01 日報」的 中,不斷完善我製作日報的流程,並系統化。

「完」


coding01 期待您繼續關注

qrcode
qrcode


也很感謝您能看到這了

qrcode
qrcode

相關文章