ADVENT CALENDAR 2019

ツイートで自動更新する静的サイトを作った話

By negset

これは AmusementCreators 2019 アドベントカレンダー その 1 の 21 日目の記事です。 その 2 もあります。


以前、以下のようなサイトを作成しました。

これは、とある VTuber プロダクションの配信スケジュールを表示するサイトです。

運営が毎日スケジュールをツイートするので、それを基に自動で更新しています。

この記事では、上記サイトの技術的な仕組みを解説します。

動作例

これが ↓

こうなって ↓

source = "http://twitter.com/dotLIVEyoutuber/status/1200731195937443840"
update = "2019-11-30T20:00:00"
date = "2019-12-01"

[[timeline]]
time = "21:00"
member = [ "神楽すず" ]

[[timeline]]
time = "22:00"
member = [ "金剛いろは" ]

[[timeline]]
time = "23:00"
member = [ "メリーミルク" ]

こうなる ↓

参考

以下の記事を参考にしました。

How to automate your Github Pages blog - for free - with IFTTT and Glitch

概要

おおまかに次のような流れでツイートがサイトに反映されます。

  1. IFTTT1 がツイートを監視し、新規ツイートがあれば Glitch2 に POST する。
  2. Node.js が受け取ったツイートをもとに toml を作成し、GitHub のリポジトリにコミットする。
  3. Netlify3 が GitHub リポジトリを監視し、更新があれば Hugo4 でビルドし、生成されたサイトをホスティングする。

ちなみに、ここで使用するサービスは全て無料で利用できるので、運用費は 0 円です。

各種詳細設定

Glitch

Glitch で Express を使ってプレーンテキストを受け取るアプリを作成します。

以下のような感じのコードになります。

const express = require('express');
const bodyParser = require('body-parser');
const toml = require('@iarna/toml');
const Octokit = require('@octokit/rest');
const app = express();

app.use(bodyParser.text());
app.post('/', (req, res) => {
  if (req.query.token !== process.env.WEBHOOK_TOKEN) {
    // invalid token
    return res.sendStatus(400);
  }

  // "|||" を改行で置換する。
  const body = req.body.replace(/\|\|\|/g, '\n');
  // toml としてパースする。
  const parsed = toml.parse(body);
  
  //
  // 必要に応じて toml の編集を行う。
  //

  const path = `data/${parsed.date}.toml`;
  const content = Buffer.from(toml.stringify(parsed)).toString('base64');
  const msg = parsed.date;

  commitFile(path, content, msg).then(_ => {
    return res.sendStatus(200);
  }).catch(err => {
    console.log(err);
    return res.sendStatus(500);
  });
});

ファイルをコミットする処理は別関数に実装しました。

async function commitFile(path, content, msg) {
  const octokit = new Octokit({
    auth: `token ${process.env.GH_TOKEN}`
  });

  const params = {
    owner: process.env.GH_USER,
    repo: process.env.GH_REPO,
    path: path,
    message: msg,
    content: content
  };

  // ファイルが既に存在する場合は更新する。
  await octokit.repos.getContents(params)
    .then(result => {
      params.sha = result.data.sha;
    });

  return octokit.repos.createOrUpdateFile(params)
}

また、GitHub と Webhook のトークンを .env ファイルに書いておきます。

(GitHub のトークンはあらかじめ取得しておき、Webhook のトークンは自分で 適当 に決めます。)

GH_USER=...
GH_REPO=...
GH_TOKEN=...
WEBHOOK_TOKEN=...

IFTTT

IFTTT で新規アプレットを作成します。

THIS に「Twitter: New tweet by a specific user.」を選択、
THAT に「Webhooks: Make a web request.」を選択します。

Webhook は以下のように設定しておきます。

  • URL:
    https://PROJECT-NAME.glitch.me/?token=WEBHOOK-TOKEN
  • Method:
    POST
  • Content Type:
    text/plain
  • Body:
    source = "{{LinkToTweet}}"|||update = "{{CreatedAt}}"|||text = '''{{Text}}'''

URL は作成した Glitch のプロジェクト名と Webhook トークンに応じて書き換えてください。

Body は toml 形式ですが、改行が使えないので代わりに ||| を使っています。これは Node.js 側で受け取った際に改行に置換します。

Hugo

Data Templates を使って、toml からいい感じの html を生成するコードを書きます。

Netlify

デプロイ時に自動で Hugo のビルドが走るように設定しておきます。

結び

かなり端折って書いたので分かりにくい点もあるかと思いますが、参考元の記事を読めば解決すると思います。

どっとライブはいいぞ 🌸🐈


  1. さまざまな Web サービス同士を連携することができる Web サービス。 [return]
  2. Node.js のアプリを作成できる Web サービス。 [return]
  3. 静的サイトホスティングサービス。 [return]
  4. 静的サイトジェネレータ。 [return]

SHARE THIS POST