我也来打造一个我的阅读追踪系统

国庆放假期间,偶然发现这篇文章《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

相关文章
相关标签/搜索