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

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

進入互聯網時代,知識的獲取成本變得史無前例的低廉,可是不管再好的知識,如果沒有對我的產生價值的話,那也只不過是一種信息噪音而已git

隨着時間的推移,稍後閱讀的 Instapaper 裏面的文章將會變得愈來愈多,就像咱們在代碼中所註釋的 TODO:可能就變成了Never Do,「稍後閱讀」也是同樣地被廣爲詬病:Read it Later = Read Never。其實我發現文章堆積的一個永恆痛點就是沒有有效的方式追蹤本身的閱讀需求與能力,其核心緣由在於閱讀的速度趕不上添加的速度。從而沒辦法可視化的評估閱讀進度、合理安排閱讀計劃,也就沒辦法給予本身適當的獎勵,久而久之必然將失去閱讀的動力。程序員

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

摘自:《Serverless實戰:打造我的閱讀追蹤系統》web

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

目標也很簡單:後端

跟蹤本身的閱讀習慣和統計閱讀量,別讓「稍後閱讀」變成了「不再讀」了api

流程

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

Workflow 下載連接:
https://workflow.is/workflows/7d54eabb797647c4b92960b8318d8c2b

IFTTT 橋接器

IFTTT 的理念就像它的名字 If This Then That ,能夠將兩個不一樣的服務關聯到一塊兒,最典型的例子就是「若是明天下雨,那麼就提醒我帶雨傘」,在這個設定下,IFTTT 會定時查詢次日的天氣預報,若是天氣預報返回的結果是次日下雨,那麼 IFTTT 就會給我設置的手機號發送短信,通知我次日出門別忘了帶傘。

瞭解 IFTTT 可參考:https://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,如本文建立的:https://github.com/fanly/reading/issues

有了 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 查看:https://developer.github.com/...

有了接口,咱們還須要權限 (access token),在 https://github.com/settings/t... 中建立 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 的知識能夠參考:

https://www.qcloud.com/community/article/782918

正如《Serverless實戰:打造我的閱讀追蹤系統》做者使用的是 Webtask,國內如騰訊雲、阿里雲,國外的 Oracle 開源的 Fn project (https://github.com/fnproject/fn/blob/master/docs/serverless.md) 等,這幾天我都嘗試了一遍,但我的以爲國外的訪問多多少少一點「跨國」不穩定,國內的兩家都須要去配置一些「東西」,我試了試仍是決定暫時不用,各位看官能夠嘗試的。

最後我仍是使用 LeanCloud (https://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

相關文章
相關標籤/搜索