🐷

Kindle で購入した書籍をブクログへ自動で登録する

commits7 min read

はじめに

いきなりですが、みなさん読書の管理ってどうしていますか?

僕は、ブクログ というサービスを使っています。以前は、読書メーター を使っていたんですが、ブクログは本に記載されている ISBN コードや Amazon の Asin コードから本を登録できる[1]ということを知って最近移行しました。

どうせなら自動化したいなと思い、調べてみると同じことを考えた先人たちがいらっしゃったので参考にしつつやってみたのでその紹介です。

作ったもの

Kindle で書籍を購入した際に Amazon からメールが届きます。そのメールから使いたい Asin コードだけを抜き出して、購入した書籍をブクログに登録します。その際、処理のログをスプレッドシートへ登録します。

これらの処理は、TypeScript で書いていて GitHub でバージョン管理しています。デプロイは GitHub Actions を使って Google Apps Script へ自動で行い、Google Apps Script のトリガーで定期実行させます。


作ったものの全体像

ソースコードはこちら。

https://github.com/ysmtegsr/kindle-booklog-sync

作成手順

以下ポイントだけピックアップします。

ローカルで Google Apps Script を使う

Amazon のメールアドレスとして登録しているのが Gmail なので、それを扱いやすい Google Apps Script ( 以下 GAS ) が選択肢として挙げられました。

また、Clasp を使うとローカルで GAS を扱うことができます。そのメリットとしては下記です。

  • コードを Git 管理しやすくなる点
  • Clasp は TypeScript をサポートしている点
  • 好きなエディタを使うことができる点

まずは、Clasp CLI を導入します。

# Clasp をインストール
$ npm install -g @google/clasp

# 確認
$ clasp --version

# ブラウザが開くので、Google アカウントで認証します
$ clasp login

プロジェクトの作成

Clasp CLI を使って作成します。--type オプションを使って同時にプロジェクトに紐づくスプレッドシートも作成します。

$ clasp create --title "kindle-booklog-sync" \
    --type sheets \
    --rootDir ./src

ローカルに appsscript.json を含む雛形が作成されていれば OK です。

GAS で Gmail を検索する

実際に ./src ディレクトリに TS ファイルを作成して書いていく訳ですが Google のサービスを扱う場合は GAS を使うと下記のように非常に簡単です。

interface GmailApp {
  search(query: string): GmailThread[]
}

const searchEmail = (): GmailThread[] => {
  return GmailApp.search("from:digital-no-reply@amazon.co.jp in:inbox")
}

ちなみに、メールの検索には実際に Kindle で書籍を購入した際に送られてくるメールアドレスを from に指定しています。

ブクログに Asin コードを使って書籍を登録する

ログインの処理などは必要ですが、今回は省略して実際に登録する処理だけピックアップしました。

UrlFetchApp.fetch() というメソッドを使って HTTP のリクエストを送ることができます。返り値は HTTPResponse で、ステータスコードやレスポンスボディを取得できます。

const uploadBook = (cookies: string[], asinList: string[]): HTTPResponse => {
  const url = 'https://booklog.jp/input'

  return UrlFetchApp.fetch(url, {
    method: 'post',
    headers: {
      Cookie: cookies.join(';'),
      Referer: url
    },
    payload: {
      isbns: asinList.join("\n"),
      category_id: 0, // なし
      status: 4 // 積読
    }
  })
}

category_id はブクログ上でのカテゴリです。事前に作っておいてその ID を指定しても良いですし、僕みたいに 0 とすると 未選択 になります。

status は読書状況です。0: 未設定, 1: 読みたい, 2: いま読んでる, 3: 読み終わった となっているので、とりあえず購入したばかりなので 4: 積読 にしました。

結果をスプレッドシートに書き込む

上記の HTTP レスポンスをスプレッドシートに残しておきます。
getSheetByName() メソッドの引数にはシートの名前を指定しました。事前に好きな名前にしておきましょう。

const log = (asin: string, text: string): void => {
  SpreadsheetApp.getActiveSpreadsheet()
    .getSheetByName('log') // シート名を記載
    .appendRow([new Date(), asin, text])
}

実行した後で見てみるとちゃんとデータが残っていました。

ブクログを確認

ブクログの方も確認してみると登録ができていました。良さげですね。

GitHub Actions でデプロイ

今回は git tag をトリガーに実行されるスクリプトを作成しました。長くなってしまったのでコードの折りたたんでおきます。

env セクションで使用している環境変数は事前に GitHub 上に登録しておく必要があります。
リポジトリ TOP > Settings > Secrets で登録画面に行くことができます。

.github/workflows/deploy.yml
name: Deployment

on:
  push:
    tags:
      - "v*"

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ['14.16.x']

    env:
      CLASP_ACCESS_TOKEN: ${{ secrets.CLASP_ACCESS_TOKEN }}
      CLASP_CLIENT_ID: ${{ secrets.CLASP_CLIENT_ID }}
      CLASP_CLIENT_SECRET: ${{ secrets.CLASP_CLIENT_SECRET }}
      CLASP_EXPIRY_DATE: ${{ secrets.CLASP_EXPIRY_DATE }}
      CLASP_ID_TOKEN: ${{ secrets.CLASP_ID_TOKEN }}
      CLASP_REFRESH_TOKEN: ${{ secrets.CLASP_REFRESH_TOKEN }}
      CLASP_SCRIPT_ID: ${{ secrets.CLASP_SCRIPT_ID }}

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup node
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'yarn'

      - name: Set env
        id: yarn-cache-dir-path
        run: echo "::set-output name=dir::$(yarn cache dir)"

      - name: Cache node modules
        uses: actions/cache@v2
        id: yarn-cache
        with:
          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: ${{ runner.os }}-yarn-

      - name: Create authentication file
        run: |
          echo $(cat <<-EOS
          {
            "token": {
              "access_token": "${CLASP_ACCESS_TOKEN}",
              "scope": "https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/script.webapp.deploy openid https://www.googleapis.com/auth/script.projects https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/service.management https://www.googleapis.com/auth/logging.read https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/script.deployments https://www.googleapis.com/auth/drive.metadata.readonly",
              "token_type": "Bearer",
              "id_token": "${CLASP_ID_TOKEN}",
              "expiry_date": ${CLASP_EXPIRY_DATE},
              "refresh_token": "${CLASP_REFRESH_TOKEN}"
            },
            "oauth2ClientSettings": {
              "clientId": "${CLASP_CLIENT_ID}",
              "clientSecret": "${CLASP_CLIENT_SECRET}",
              "redirectUri": "http://localhost"
            },
            "isLocalCreds": false
          }
          EOS
          ) > ~/.clasprc.json
      - name: Create clasp application file
        run: |
          echo $(cat <<-EOS
          {
            "scriptId": "${CLASP_SCRIPT_ID}",
            "rootDir": "./src",
            "fileExtension": "ts"
          }
          EOS
          ) > ./.clasp.json
      - name: Get version
        id: version
        run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}

      - name: Deploy to GAS
        run: npx @google/clasp push --force

      - name: Add version
        run: npx @google/clasp version ${{ steps.version.outputs.VERSION }}

GitHub Actions が正常に完了したら clasp open と実行して完了しているか確認します。

トリガーを設定

GAS へのデプロイができたところで定期実行させるためのトリガーを設定します。左側のメニューの中からトリガーを選択肢、「トリガーを追加」で設定できます。僕は毎日実行にしておきました。

まとめ

これまで GAS はブラウザのエディタで書くことが多かったので、いつも使っているエディタを使えるのはエディタの機能をそのまま使えて効率がいいし、ストレスなく実装できました。また、なんといっても TypeScript で型定義ができる点は非常にいいですね。

また、GitHub 上で管理できる点はかなり大きいと感じました。(今回の内容はプライベートですが)チームで開発する際には必須になってくるのと、GitHub Actions を使えるというのが大きなメリットです。

読んでくださった方で、自分はこんな読書管理しているよというものがあればぜひ教えてください!

参考にさせていただいたサイト

https://tateren.hateblo.jp/entry/2016/10/03/025425
https://rela1470.hatenablog.jp/entry/2018/02/05/205140
https://panda-program.com/posts/clasp-typescript
https://dev.classmethod.jp/articles/typescript-clasp-github-actions/
脚注
  1. ブクログ - web本棚サービス ↩︎