【Drupal/EC2/Slack/Qiita】QiitaトレンドにのったらSlack通知

Qiitaのトレンドに載ったらうれしいですよね。

今回は、Qiitaが提供しているRSSを使って自身の記事がトレンド入りしたらSlackで教えてくれる機能を実装してみました。

## はじめに （すみません、実はシリーズです。）

以前、QiitaのRSSをDrupalでAPI化する実装をしてみました。
せっかく実装したAPIですので、今回さらに活躍させてみようと思います。

https://qiita.com/umekikazuya/items/c20ac26b59670a72ca7b

https://qiita.com/umekikazuya/items/6379e3785b385bceabb3

https://qiita.com/umekikazuya/items/54b6ada9024f17499aa5

※ 「せっかく公式がRSSを提供しているのに、なんでAPI化するの？」っていう疑問あると思います。どれかの記事で触れてるので文句・不満がある人は読んでください。

## トレンド / Feed （Qiita公式）
どうやら、個人記事RSS/トレンド記事RSS どちらもXMLの構造に大きなフォーマットの違いがないようです。（APIをそのまま使えるからありがたい 🤲）
以下のURLが提供しているFeedです。（リーダー登録してもいいかもですね。）
https://qiita.com/popular-items/feed

## 実装しましょ
手順はこちらです。

1. API実装
2. Slack通知関数の実装
3. Cron の設定

そんな難しくないです。1つずつやっていきましょ。

今回はDrupalで実装しましたが、どの言語/どのフレームワークでも、ある程度は対応できる仕様だと思います。
ご自身のお得意なものを使ってください。

### 001. API実装
API実装（XMLパース）については、過去記事で紹介しています。
ほぼ変更ありませんので細かいところはそちらを参照ください。

https://qiita.com/umekikazuya/items/c20ac26b59670a72ca7b

ただ、**1点だけ過去記事と違う所** があります。

個人記事 / RSSにはなかった **Author** を今回使用します。
記事の投稿者が誰かわからないAPIは嫌ですよね。

```php:QiitaController::parseXml()を編集してください。過去記事もみてね 🤲
/**
 * Parses XML data and generates list item markup for each feed item.
 *
 * @param \SimpleXMLElement $xml
 *   The XML data.
 * @param string $url
 *   Rss url.
 *
 * @return array
 *   The array for the list items.
 */
private function parseXml(\SimpleXMLElement $xml, string $url): array {
  $articles = [];
  foreach ($xml->entry as $o) {
    // Get feed item.
    $title = (string) $o->title;
    $link = (string) $o->url;
    $author = (string) $o->author->name;
    $published = (string) $o->published;
    if (!isset($title) || !isset($link) || !isset($published)) {
      continue;
    }
    $published = new \Datetime($published);

    if (!$author) {
      // 個人RSSの場合.
      $articles[] = [
        'title' => $title,
        'link' => $link,
        'published' => $published->format('c'),
      ];
    }
    else {
      // トレンドRSSの場合Authorを考慮する.
      $articles[] = [
        'author' => $author,
        'link' => $link,
        'published' => $published->format('c'),
        'title' => $title,
      ];
    }
  }
  return [
    'title' => (string) $xml->title,
    'link' => $url,
    'data' => $articles,
  ];
}
```
以前の実装したメソッドをゴリ押しで対応させました。（雑だけど許して）

API化についてはこちらの記事で紹介しています。

https://qiita.com/umekikazuya/items/6379e3785b385bceabb3

APIにアクセスして、JSONデータが返ってくれば👌です。
これでAPIの実装は完了です。

:::note info
`/api/qiita/{id}`の`{id}`にユーザー名をセットする想定です。
例えば、`/api/qiita/umekikazuya`にアクセスする感じ。

今回のFeedのURLは[`https://qiita.com/popular-items/feed`](https://qiita.com/popular-items/feed) です。
`{ホスト名}/api/qiita/popular-items`でアクセスする感じですね。
:::

※ 以前の記事では、アカウント単位で 8時間 キャッシュさせる機構についても紹介しています。そちらもぜひ。

### 002. Slack通知関数の実装
次は、Slack通知関数の実装を行います。

イメージはこんな感じ。
1. APIからJSONデータを取得する
1. JSONデータに自身のアカウントが存在するかチェック
1. Slackのフォーマットに変換

```php:/script/batch/trend-notice-slack.php
$client = new Client();
try {
  $url = '{$ホスト名}';
  $endpoint = '/api/qiita/feed/popular-items';
  $http_request = $client->get($url . $endpoint);
  $raw_data = $http_request->getBody()->getContents();
  if (!$raw_data) {
    throw new \Exception();
  }
}
catch (\Exception $e) {
  echo 'APIの取得に失敗.';
  return;
}

// 取得JSONデータを配列に変換.
$data = json_decode($raw_data, TRUE);

// 自分のアカウントでデータを絞り込み.
$filtered_data = array_filter($data['data'], function ($item) {
  // 自身のアカウント名.
  return $item['author'] === 'umekikazuya';
});

// Slackに送信するメッセージの構築.
$blocks = [];
foreach ($filtered_data as $item) {
  $blocks[] = [
    "type" => "section",
    "text" => [
      "type" => "mrkdwn",
      "text" => "<{$item['link']}|{$item['title']}> - {$item['published']}",
    ],
  ];
}
$slack_message = [
  "blocks" => $blocks,
];

// 環境変数.
$token = getenv('slack_token');
$channel_id = getenv('slack_channel_id');

// Slackに送信.
$client = new Client();
$client->post('https://slack.com/api/chat.postMessage', [
  'headers' => [
    'Authorization' => "Bearer {$token}",
    'Content-Type' => 'application/json',
  ],
  'json' => [
    'channel' => $channel_id,
    'blocks' => $slack_message['blocks'],
  ],
]);
echo "Slackに送信しました。";
// ※ 記事内では細かい例外処理は省略しています。.
```

### 003. Cron の設定
#### その前に...。
先程のPHP関数を実行するBashを実装しました。
```bash:/script/qiita-trend-notice-slack.bash
$DRUSH_PATH php:script $PATH/script/batch/trend-notice-slack.php
```
**※ Drushは、Drupalと親和性の高いCLIツールです。**

https://www.drush.org/13.x/generators/all/

#### では ShellをCronで実行しましょう。

##### 1. Crontab を開く
```bash
crontab -e
```
##### 2. Cron の設定
> 最近人気の記事を毎日5時/17時に更新

とありますが、ちょっと時間のズレがある気かもしれないので 更新の1時間後に Cron を設定しましょう。

```bash
0 6,18 * * * bash {PATH}/script/batch/trend-notice-slack.bash
```
Cron設定は以上です。

## 執筆
記事を書きましょう。

## 待機
トレンド入りするかどうかは保証できません。

## まとめ
それぞれの機能の単体テストはしましたので動作は問題ないかと。
ただ、トレンド入りしてないので通しで動くかはわかりません。現在待機中です。（記事の質悪いのかな 😥）