AndroidのJetPack便利だし種類も更新も多いのでよくチェックしているのですが、毎日チェックするのは手間なので、RSS形式にして更新を受け取れたらいいなと思い作りました。
中身がHTMLだと判定してくれるRSSの場合は各ライブラリのリンクにジャンプもできます。
似たものとして、GitHub Releaseを使った更新通知を受け取る方法もあります。
GitHubのNotificationsで、androidxライブラリのリリースの通知を受け取れるリポジトリを作りました | stsnブログ
環境
- AWS Lambda
- Node.js 10
今回は https://developer.android.com/jetpack/androidx/releases のページをパースしてRSSのXMLに変換するアプリをAWS Lambda上に作成しました。
HTTPリクエストが簡単できる「request」、
HTMLのパーサーに「node-html-parser」、
XMLの作成に「rss」
をライブラリとして使いました。
{
"private": true,
"name": "androidx",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"node-html-parser": "^1.1.16",
"request": "^2.88.0",
"request-promise": "^4.2.4",
"rss": "^1.2.2"
}
}
AWS LambdaのNode.jsバージョンがあがり、async-awaitが使えるようになったので、requestをpromiseでラップしたライブラリも導入しました。
コード
const parser = require('node-html-parser');
const request = require('request-promise');
const RSS = require('rss');
exports.handler = async (event) => {
const htmlString = await request.get('https://developer.android.com/jetpack/androidx/versions/all-channel');
const releaseNotes = parseItems(htmlString);
const response = {
statusCode: 200,
headers: { 'Content-Type': 'text/html' }, // RSS形式として返すためContent-Typeを明示的に指定
body: createRSS(releaseNotes),
};
return response;
};
function parseItems(html) {
const root = parser.parse(html);
const releases = root.querySelector('.devsite-article-body');
const headers = releases.querySelectorAll('h3');
const lists = releases.querySelectorAll('ul');
return headers.map((header, index) => {
const updateDate = header.text;
// 更新内容をHTMLのリスト形式に変換
const updates = lists[index].querySelectorAll('a');
const packages = updates.map((package) => {
// リンクは相対パスなので絶対パスに変換
const link = `https://developer.android.com${package.attributes.href}`;
const packageName = package.text;
return `<li><a href="${link}">${packageName}</a></li>`;
});
return {
title: updateDate,
date: updateDate,
packages: `<ul>${packages.join('\n')}</ul>` // Slackは装飾系タグが無視されるので改行で整える
};
});
}
function createRSS(items) {
let feed = new RSS({
title: 'Recent Release Notes',
feed_url: 'hoge',
site_url: 'https://developer.android.com/jetpack/androidx/versions/all-channel',
});
items.forEach((item) => {
feed.item({
title: item.title,
date: item.date,
description: item.packages,
// August 7, 2019 -> august_7_2019 に変換
url: `https://developer.android.com/jetpack/androidx/versions/all-channel#${item.title.toLowerCase().replace(/,? /g, '_')}`
});
});
return feed.xml();
}
基本的にはHTMLをパースしRSSのXML形式に落とし込んでいく作業です。
特に難しいことはしていませんが、HTMLのパース作業は地味に重労働です。
こうしてできあがるRSSはこのような形になりました。
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title><![CDATA[Recent Release Notes]]></title>
<description><![CDATA[Recent Release Notes]]></description>
<link>https://developer.android.com/jetpack/androidx/versions/all-channel</link>
<generator>RSS for Node</generator>
<lastBuildDate>Sat, 10 Aug 2019 13:35:09 GMT</lastBuildDate>
<atom:link href="hoge" rel="self" type="application/rss+xml" />
<item>
<title><![CDATA[August 7, 2019]]></title>
<description><![CDATA[<ul><li><a href="https://developer.android.com/jetpack/androidx/versions/all-channel/activity#1.1.0-alpha02">Activity 1.1.0-alpha02</a></li>
<li><a href="https://developer.android.com/jetpack/androidx/versions/all-channel/ads#1.0.0-alpha01">Ads 1.0.0-alpha01</a></li>
<li><a href="https://developer.android.com/jetpack/androidx/versions/all-channel/autofill#1.0.0-alpha02">Autofill 1.0.0-alpha02</a></li>
<!-- 長いので省略 -->
</ul>]]></description>
<link>https://developer.android.com/jetpack/androidx/versions/all-channel#august_7_2019</link>
<guid isPermaLink="true">https://developer.android.com/jetpack/androidx/versions/all-channel#august_7_2019</guid>
<pubDate>Wed, 07 Aug 2019 00:00:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[July 30, 2019]]></title>
<description><![CDATA[<ul><li><a href="https://developer.android.com/jetpack/androidx/versions/all-channel/work#2.2.0-rc01">WorkManager 2.2.0-rc01</a></li></ul>]]></description>
<link>https://developer.android.com/jetpack/androidx/versions/all-channel#july_30_2019</link>
<guid isPermaLink="true">https://developer.android.com/jetpack/androidx/versions/all-channel#july_30_2019</guid>
<pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>
全部の更新を取っているので近々直近10件とかに絞ろうかと思います。
あとRSSってContent-Typeがapplication/xmlかと思いきやtext/htmlで返す方が多いそうです。知らなかった。