Atom

思考、故事和创意

TTRSS

TTRSS 的一个图片插件

有些网站页面中在 HTML 刚刚加载完成时,其中的 img 图片标签并没有 src 属性,而是把图片链接放在一个自定义的属性里,比如这样,放在 data-url 里:

<img data-url="http://example.com/aaa.jpg">

然后由 js 来对 img 标签里的属性进行修改,把 data-url 的内容,放到 src 里,以此来实现图片的懒加载。当通过 RSS 订阅,在 RSS 阅读器里看这种文章内容时,因为 js 的缺失,图片标签里就一直都不会有 src 属性,也就看不到图片了。好在 TTRSS 支持插件扩展,可以比较方便地解决这个小问题。

在 TTRSS 官方有个非常简略的页面 Making plugins 介绍如何写它的插件。大意是提供了从抓取数据、到数据库、到页面展示之间几个可以 HOOK 的点,在这些位置,对库中的内容进行处理。看完这点介绍,仍然一头雾水,不过还有一些插件的例子可以学习和模仿。

在 TTRSS 的主目录下 plugins 目录,创建一个新的目录,新目录名就是要写的会显示在 TTRSS 后台的插件名,在这个目录里新建 init.php 文件作为入口,根据不同的 HOOK 点以及要做的不同功能,在里面实现几个指定的函数,就算完成了。

最终照虎话猫写了一个这样的 TTRSS 插件:

// ttrss/plugins/af_imgdataurl/init.php

<?php
class Af_ImgDataUrl extends Plugin {
    private $host;

    function about() {
        return array(1.0,
                "Convert data-url in img tag to src",
                "noodles");
    }

    function init($host) {
        $this->host = $host;
        $host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
    }

    function hook_article_filter($article) {
        $charset_hack = '<head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>';

        $doc = new DOMDocument();
        $doc->loadHTML($charset_hack . $article["content"]);
                $found = false;
        if ($doc) {
            $xpath = new DOMXpath($doc);
            $images = $xpath->query('(//img[@data-url])');
            foreach ($images as $img) {
                $src = $img->getAttribute("data-url");
                $img->removeAttribute("data-url");
                $img->setAttribute("src", $src);
                $found = true;
            }
            $images = $xpath->query('(//img[@data-original])');
            foreach ($images as $img) {
                $src = $img->getAttribute("data-original");
                $img->removeAttribute("data-original");
                $img->setAttribute("src", $src);
                $found = true;
            }
            if ($found) {
                $doc->removeChild($doc->firstChild);
                $article["content"] = $doc->saveHTML();
            }
        }

        return $article;
    }

    function api_version() {
        return 2;
    }
}
?>

主要逻辑在 hook_article_filter 函数里:解析 HTML,获取图片 url,设置 src 属性,生成新的 HTML 返回出去。

然后去 TTRSS 后台偏好设置里给这个插件打勾选中启用就可以了。

选中,启用插件

Tiny Tiny RSS 用上 Privoxy 代理

上周搭建 TTRSS 时,给配置了代理是针对所有请求的,这周抽时间做了些改变,针对国内的站,就不再绕远路了。

不宜细说,这里做个备忘。

1. 功能强大的 privoxy

对比之前用的 polipo,privoxy 功能强大太多,这里主要用到它针对不同地址进行不同处理的功能,默认直接转发不走代理,特殊域名走代理。

privoxy 配置

# /etc/privoxy/config

listen-address  127.0.0.1:8118
forward / .
actionsfile /etc/privoxy/gfw.action
actionsfile /etc/privoxy/extra.action

gfw.action 样例

# /etc/privoxy/gfw.action

{+forward-override{forward-socks5 127.0.0.1:12345 .}}
.google.com
.facebook.com
.twitter.com
.youtube.com

2. 使用 gfwlist2privoxy 把 gfwlist.txt 转换成适用于 privoxy 的 actionfile

使用 gfwlist 转换的 privoxy actionfile,免去自己收集、配置的麻烦。

gfwlist2privoxy -i gfwlist.txt -f gfw.action -p 127.0.0.1:12345 -t socks5

3. crontab 脚本,定期更新、转换 gfwlist

#crontab

26 03 * * * bash /home/noodles/update_gfwlist.sh > /var/log/update_gfwlist.log 2>&1

脚本内容

#!/bin/bash

# update_gfwlist.sh

set -o errexit

WGET="/usr/bin/wget"
GFWLIST_URL="https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"
GFWLIST_FILE="/tmp/gfwlist.txt"

# PROXYCHAINS4_CONFIG="/home/noodles/.proxychains/proxychains.conf"
# PROXYCHAINS4="/usr/bin/proxychains4 -f $PROXYCHAINS4_CONFIG -q"

GFWLIST2PRIVOXY="/usr/local/bin/gfwlist2privoxy"

PRIVOXY_GFW_ACTION_TEMP="/tmp/gfw.action.new"
PRIVOXY_GFW_ACTION="/etc/privoxy/gfw.action"

function get_gfw_action {
    printf "download %s ...... " "$GFWLIST_URL"
    # $PROXYCHAINS4 $WGET -q -O $GFWLIST_FILE $GFWLIST_URL
    $WGET -q -O $GFWLIST_FILE $GFWLIST_URL
    echo "ok"
    printf "convert %s to %s ... " "$GFWLIST_FILE" "$PRIVOXY_GFW_ACTION_TEMP"
    $GFWLIST2PRIVOXY -i $GFWLIST_FILE -f $PRIVOXY_GFW_ACTION_TEMP -p 127.0.0.1:12345 -t socks5
    echo "ok"
}

function replace_gfw_action {
    local old_md5=""
    local new_md5=""
    if [[ -s $PRIVOXY_GFW_ACTION ]]; then
        old_md5=$(sed 1,2d "$PRIVOXY_GFW_ACTION" | md5sum | awk '{print $1}')
    fi
    new_md5=$(sed 1,2d "$PRIVOXY_GFW_ACTION_TEMP" | md5sum | awk '{print $1}')
    echo "old md5sum: [$old_md5]"
    echo "new md5sum: [$new_md5]"
    if [[ "x$old_md5" = "x$new_md5" ]]; then
        echo "no need to update"
    else
        echo "mv $PRIVOXY_GFW_ACTION_TEMP to $PRIVOXY_GFW_ACTION"
        mv $PRIVOXY_GFW_ACTION_TEMP $PRIVOXY_GFW_ACTION
        echo "reload privoxy"
        /usr/sbin/service privoxy force-reload
    fi
    rm -f $GFWLIST_FILE
}

function main {
    get_gfw_action
    if [[ -s $PRIVOXY_GFW_ACTION_TEMP ]]; then
        replace_gfw_action
        echo "Done."
    else
        echo "$PRIVOXY_GFW_ACTION_TEMP is empty."
        echo "Error."
    fi
}

main

4. 命令行工具的代理

上面脚本里,下载 gfwlist.txt 时可能要用到。

proxychains4

rofl0r/proxychains-ng

配置文件 ~/.proxychains/proxychains.conf

strict_chain
proxy_dns
remote_dns_subnet 224
tcp_read_time_out 15000
tcp_connect_time_out 8000
[ProxyList]
socks5  127.0.0.1 12345   # ss 代理地址

Tiny Tiny RSS

Google Reader 死后的一段时间,我试用了好几个 RSS 阅读器,期望能够代替它,最终是 Inoreader 胜出。一直到上周,我把 Inoreader 换成了自建的 Tiny Tiny RSS:一是因为 Inoreader 的速度不太理想,二是 Inoreader 免费版的各种限制和广告。

Tiny Tiny RSS

Tiny Tiny RSS 的安装没有什么坑,主要参考了这几篇文章

  1. 官方的安装文档
  2. DigitalOcean 社区的教程 How To Install Tiny Tiny RSS with Nginx for Debian 7 on a VPS

官方安装文档较为简略。DigitalOcean 社区的那篇教程很详细,仅这一篇足矣。

另外一件必须要做的事情是定期更新订阅源,照着官方文档做即可,推荐的做法是第一种,”Update daemon”,以后台服务的形式更新。第二种做法是利用 crontab,第三种方法用于单用户模式,在 Web 后台手动更新,不推荐这么做。

nginx 配置

这里可能会有个坑,按照上述 DigitalOcean 社区教程配置完 nginx 后,打开页面总是状态 200 的空白内容,之后在 stackoverflow 上找到了答案,把

include /etc/nginx/fastcgi_params;

替换为

include /etc/nginx/fastcgi.conf;

两个文件的区别就是后者多了这一行

fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

完整的相关配置如下

location / {
	index           index.php;
}

location ~ \.php$ {
	try_files $uri = 404;
	fastcgi_pass unix:/run/php/php7.0-fpm.sock;
	fastcgi_index index.php;
	#include /etc/nginx/fastcgi_params;   # with next line
	#fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
	# or
	include /etc/nginx/fastcgi.conf;
}
location /cache {
	deny all;
}

location = /config.php {
	deny all;
}

至于为什么有 fastcgi_paramsfastcgi.conf 两个文件,可以到这里查看下它们之间有怎样的历史纠葛。

科学上网

我把 Tiny Tiny RSS 放在了腾讯云国内云主机上,把以前订阅的源搬过来之后,发现了一个尴尬的问题:有些被“认证”的订阅源,在国内是无法访问到的,需要科学上网。好在 Tiny Tiny RSS 留了一条路。给主目录下 config.php 文件加上如下一行:

`define('_CURL_HTTP_PROXY', '127.0.0.1:8080');

后面的 127.0.0.1:8080 是我在云主机上用 ss + polipo (socks5 转 http) 搭的代理。这样 Tiny Tiny RSS 就可以拉取、更新被“认证”的源了。

主题

给 Tiny Tiny RSS 找了一个看得过去的主题,在后台配置管理处却没有它的踪影。后来在另外一个主题的 FAQ 中发现了缘由。

因为主题版本号和 Tiny Tiny RSS 版本号对不上,有两种办法可以看到版本号

  1. Tiny Tiny RSS 目录里 include/version.php 中写着 define('VERSION_STATIC', '17.4');
  2. 在 Tiny Tiny RSS 的 Web 后台最底部可以看到版本号

主题的 css 文件里写着 /* supports-version:17.1 */,一目了然,非常 dirty 得手动把 17.1 改成 17.4 了(当前该主题在 github 上已更新支持 17.4),问题被粗暴解决,倒没发现什么毛病。