2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWS Lambdaを使ってAndroid JetpackのリリースをRSSで受け取れるようにした

Last updated at Posted at 2019-08-10

AndroidのJetPack便利だし種類も更新も多いのでよくチェックしているのですが、毎日チェックするのは手間なので、RSS形式にして更新を受け取れたらいいなと思い作りました。

画像はRSSをSlackで読ませてみたものです。
rss.png

中身がHTMLだと判定してくれるRSSの場合は各ライブラリのリンクにジャンプもできます。
rss2.png

似たものとして、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」
をライブラリとして使いました。

package.json
{
  "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でラップしたライブラリも導入しました。

コード

index.js
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で返す方が多いそうです。知らなかった。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?