アドベントカレンダーくろーるくん

メリー・クリスマス :christmas_tree: ！ [ラクス Advent Calendar](https://qiita.com/advent-calendar/2021/rakus), 25 日目の記事です。

# はじめに

前置きとして何かいろいろ書きたい気持ちがあったのですが、まとまりきらないまま時間切れとなってしまいました。

ただこれだけは言わせてください。今年のラクスアドベントカレンダーは、ラクスのメンバーが好きな技術を語る場、あるいは何か作ってみるきっかけを提供するために作られました。

というわけで私自身もアドベントカレンダー駆動でアプリを一つ作り、期間中社内で運用してみました。その名も「アドベントカレンダーくろーるくん」です。

# アドベントカレンダーくろーるくん

アドベントカレンダーくろーるくんは Qiita アドベントカレンダーの新着記事一覧を Mattermost に投稿するボットです。指定したアドベントカレンダーの RSS を取得して新着記事を抜き出し、メッセージに加工して Mattermost に投稿します。

https://github.com/rodbb/advent-calendar-bot

## Installation

### Windows または Linux

[リリースページ](https://github.com/rodbb/advent-calendar-bot/releases) から使用するプラットフォームに対応するファイルをダウンロードして解凍してください。`advent-calendar-bot` ディレクトリの中に実行ファイルが入っています。

### MacOS

ソースからビルドする必要があります。事前に [Haskell Stack](https://docs.haskellstack.org/en/stable/README/) をインストールしておいてください。

リポジトリを clone し、以下のコマンドを実行してください。

```
stack install
```

ビルドにはけっこう時間がかかりますが、シュトーレンでも食べながら待ちましょう :cake:

なお、多分ビルドできると思いますが、一回も Mac でやったことがないので保証はできません。

## Usage

コマンドラインで動作します。

```
> advent-calendar-bot-exe.exe --help
Command to post Qiita Advent Calendar updates to Mattermost

Usage: advent-calendar-bot-exe.exe WEBHOOK_URL FEED_URL [--summary-api URL]
                                   [--summary-api-key STRING] [--template FILE]
                                   [--cache FILE] [--dry-run]

Available options:
  WEBHOOK_URL              Target Mattermost Incoming Webhook URL
  FEED_URL                 Target Qiita Advent Calendar Feed URL
  --summary-api URL        Generate Summary API URL
  --summary-api-key STRING Generate Summary API Key String
  --template FILE          Message Template File Path
                           (default: "templates/default.mustache.md")
  --cache FILE             Feed Updated Date Cache File Path
                           (default: ".advent-calendar-bot")
  --dry-run                If true, will not post to Mattermost
  -h,--help                Show this help text
```

### Available options

#### `WEBHOOK_URL`

新着通知を投稿する先の Mattermost Incoming Webhook URL を指定します。実際にどのチャンネルにメッセージが投稿されるかは Mattermost 側で設定してください。

#### `FEED_URL`

新着を確認したい Qiita Advent Calendar の RSS Feed URL を指定します。どこで URL を確認できるかは以下の記事の冒頭をご覧ください。

https://qiita.com/amderbar/items/a333c8230a420a5bb9f5

#### `--summary-api`

記事要約を生成する際に使用する API の URL を指定します。指定されなかった場合、要約は生成されません。

ちなみに本来特定の API を利用する想定ではありませんが、現状では朝日新聞社が公開している長文要約生成API [^2] でしか動作を確認していません。ほかの API にも対応できるようにしたいですが、API ごとに異なるインターフェース（必要なヘッダやリクエスト、レスポンスのデータ型など）を抽象化する方法が思いつきません。あるいは要約生成器を内蔵できたらいいのですが…。

[^2]: https://cl.asahi.com/api_data/longsum.html

#### `--summary-api-key`

記事要約の API 利用時に必要な API キーを指定します。

#### `--template`

Mattermost に投稿するメッセージのテンプレートファイルのパスを指定します。

テンプレートファイルは [Mustache 記法](https://qiita.com/sengok/items/1d958348215647a5eaf0) で書かれた Markdown ファイルです。テンプレートで利用可能なデータについては「[メッセージテンプレートに渡されるデータ](#メッセージテンプレートに渡されるデータ)」をご覧ください。

指定されなかった時用のデフォルトテンプレートが存在しますが、テスト用の適当なテンプレートなので自作することをお勧めします。

なお、現状では既知の問題として Windows や Linux 向けのビルド済みバイナリを使用した場合、デフォルトテンプレートが見つからずエラー終了します [^3]。

[^3]: https://github.com/rodbb/advent-calendar-bot/issues/5

#### `--cache`

前回実行時の情報を保持するキャッシュファイルのパスを指定します。

アドベントカレンダーくろーるくんは実行ごとに Feed の最終更新日時をキャッシュファイルに記録しています。キャッシュされた日時と比較することで、記事が新着かどうかを判断しています。

デフォルトでは作業ディレクトリ下の`.advent-calendar-bot`というファイルを使用します。

#### `--dry-run`

このオプションを指定した場合、メッセージの作成までは行いますが Mattermost への投稿は行われず、代わりに生成したメッセージをターミナルに出力します。

動作確認の際に使います。

#### `-h,--help`

このオプションを指定するとほかの引数やオプションはすべて無視し、ヘルプテキストを表示します。

### メッセージテンプレートに渡されるデータ

ルートオブジェクトとして以下の型のオブジェクトが渡されます。

```typescript
type AdventCalendar = {
    calendarTitle: string,            // カレンダーのタイトル
    calendarUrl: string,              // カレンダーの URL
    calendarUpdate: string,           // カレンダー Feed の更新日時文字列
    calendarEntries: CalendarEntry[], // 新着記事オブジェクトのリスト
}
```

`CalendarEntry` は以下の型のオブジェクトです。

#### 新着記事（`CalendarEntry`）オブジェクト

```typescript
type CalendarEntry = {
    entryTitle: string,          // 記事タイトル
    entryAuthor: string,         // 記事の著者名
    entryUrl: string,            // 記事 URL
    entrySummary: string | null, // 記事の要約
    entryPublished: {
        year: number,            // 記事公開年（西暦）
        month: number,           // 記事公開月（1-12）
        day: number,             // 記事公開日（1-31）
    },
}
```

`entrySummary` フィールドには `--summary-api` オプションで指定された要約生成 API の結果がセットされます。`--summary-api` オプションが指定されなかった場合は `null` がセットされます。

## 動作イメージ

実行コマンド

```
> advent-calendar-bot-exe.exe `
>> http://localhost:8065/hooks/xx_secret_xx `
>> https://qiita.com/advent-calendar/2021/rakus/feed `
>> --summary-api https://clapi.asahi.com/abstract `
>> --summary-api-key xx_secret_xx
```

Mattermost 画面

![mattermost-msg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216864/b89d5416-ab3a-ae73-8066-4a88d883ae1c.png)


これは自宅 PC に Docker で立ち上げた Mattermost Preview にボットからラクス Advent Calendar 2021 の新着記事通知メッセージを渡航したところです。デフォルトテンプレートを使用しています。要約生成 API として朝日新聞社が公開している長文要約生成 API [^2] を使わせてもらっています。

## 使ってみた

さて、このアドベントカレンダーくろーるくんを社内で使っみたという話です。

アドベントカレンダー自体を作ってから制作を開始して何とか基本機能は 12 月に間に合わせ、専用のテンプレートを作成して初日の投稿からボットによる新着記事通知を社内 Mattermost のほぼ全員が入っているパブリックチャンネルに投稿し続けました。

ボットが投稿した新着通知メッセージに対して時折レスポンスが付くこともありました。リンククリック数などの計測は出来ていませんのでどれだけの効果があったかを定量的に検証はできないものの、記事の宣伝に一役買っていたのだろうと思われます。

はじめのうちしばらくは手動でコマンドを実行しており、実行を忘れていた日には

> アドベントカレンダーくろーるくんはいつも何時ごろ実行されるのかしら

という投稿があるくらいには存在感を持っていたようです。ちなみにこの投稿が愛称命名の瞬間でした。
（投稿はうろ覚えで書いています。正確な内容をメモしておくのを忘れていました）

しかしさすがに毎日手動実行はめんどう＋忘れる＋私が休暇の時は投稿されず不都合なため、最終的には以下の記事を参考に GitLab CI を使って定期実行されるようにしました。

https://qiita.com/oohira/items/b67aaeb592ad6df6bc8e

これはこれでいろいろとハマって時間がかかってしまったのですが、その話は割愛します。27 日は休暇予定ですが、本記事を含む新着通知が自動で投稿されていることでしょう。

# おわりに

アドベントカレンダーくろーるくんを制作し、社内向けの記事宣伝に使用してみました。

ラクスのメンバーが好きな技術を語る場、あるいは何か作ってみるきっかけを提供したからには、そこに投稿された記事を多くのラクスのメンバーに知ってもらいたいと思いました。目的は達成できたでしょうか。

ちょっと気が早いですが、来年もこのボットでアドベントカレンダーを盛り上げられたらいいですね。興味を持っていただけたなら issue 報告、PR していただけると嬉しいです。

これにて今年もラクス Advent Calendar 完走です！ みなさんよいお年を！！ :bamboo: