フォローしているQiitaユーザが作成した記事を1日1回、スラックに通知してみた ～AWS100本ノック～ 13/100

# はじめに

Qiitaにはユーザのフォロー機能がありますが、フォローしている人が投稿した記事を確認するには、`フォロー中のユーザ`タブで記事を一覧表示して、当日の記事を確認するしか現状なさそうです :cry: 

まぁ、それでもいいっちゃいいんですが、せっかくなら日常で使っているコミュニケーションツールに通知してくれた方が良いなーと思い、自動で通知する機能を作ってみました！

![スクリーンショット 2024-11-05 153409.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/9acf3762-1985-2c11-1796-051d53f8075f.png)


# 試してみる

では、さっそく試してみます。
Qiita APIを利用して情報を取得し、Slackに通知します。
※公式のQiita APIリファレンスは[こちら](https://qiita.com/api/v2/docs)

全体像としては以下になります。
![フォローしているQiitaユーザの投稿をスラックに通知.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/9a1ebf9c-79a2-a04f-5b86-a0e5648e3257.png)

## Slackの設定

まず、WebhookURL発行のためにSlack Appを作成します。

:::note info
Slack Appでオリジナルのアプリを作ることで、作業の効率化などが行えます
:::

https://api.slack.com/apps にアクセスし、`Create New App`をクリックします。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/f7911072-fa27-9ffe-698a-cf6a745d4200.png)

`From scratch`をクリックします。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/389f063e-60bf-24b8-b82c-67d500d33877.png)

アプリの名前を入力し、スラックのワークスペースを選択し、`Create App`をクリックすればSlack Appが作られます。簡単ですね :smile: 
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/e2b81f94-f00a-cde1-d866-ecd258652d01.png)

次に、Webhookの設定です。
デフォルトだと、Webhookは`Off`になっているので、`On`にします。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/d81e6e74-25f4-a66f-46e6-60a45957a4ea.png)

`Add New Webhook to Workspace`をクリックするとアクセス許可が求められるので、`許可する`をクリックします。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/1042b98b-44d1-f6e2-7179-50f2617ff9a9.png)

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/7068b333-374c-11a6-3ed2-e478a5aa58d3.png)

すると、Webhook URLが払い出されます。こちらをLambdaで利用します。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/fcba9c00-e6b6-98c7-a3fc-171ac0db6511.png)

ついでに、`Basic Information`メニューの`Display Information`でSlackにメッセージを送る際のアイコンを設定しちゃいましょう :smiley: 
Qiitaのキャラクターにしました！
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/04f3b7de-716b-2177-6c50-aa3f69798ae4.png)


## Lambda関数の作成

次に`Lambda関数`を作成します。
Lambdaって何ですか？って思った方は、[こちらの記事](https://qiita.com/namasa/items/3a0d703565643f364133)で説明していますので、もしよければ見てください！

`関数名`を入力し、`ランタイム`を選択して`関数の作成`をクリックすると、関数が作られます。
※今回は`Node.js 20.x`のサンプルコードになります

![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/38e3c6a1-db8b-2b57-90c8-53623bc37459.png)

`環境変数`に自身のQiitaのuserIdと、先ほどSlackで払い出したWebhookUrlを設定します。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/850c58d1-738d-122d-f3b8-b2ab577b1538.png)


Lambda関数に以下のコードを記載します。
とりあえず試せればいいやの精神なのでコードの書き方がよくない、とかはご了承ください :bow: 

やっていることとしては、
- 自身のフォローユーザを100件取得　※100人以上いる場合は、修正が必要です
- 取得したユーザの投稿記事を、投稿日時の降順で5件ずつ取得　※1日に5件以上投稿する人はいないはず...
- 取得した投稿の投稿日時が当日の場合は、WebhookURLに記事のURLなどを送信
```js:index.js
export const handler = async (event) => {
  const now = new Date();
  const today = now.toISOString().split('T')[0];
  const userId = process.env.userId;

  const followedUsers = await fetchfollowedUsers(userId);

  for(const user of followedUsers) {
    const items = await fetchItems(user.id);
    for(const item of items) {
      const createdAt = new Date(items[0].created_at);
      const createdDate = createdAt.toISOString().split('T')[0];
      
      if (createdDate == today) {
        console.log(item);
        await sendItemToSlack(user, item);
      }
    }
  };
};

async function fetchfollowedUsers(userId) {
  const perPage = 100;
  const ENDPOINT = `https://qiita.com/api/v2/users/${userId}/followees?per_page=${perPage}`;
  const REQUEST_TIMEOUT_MS = 5000;

  try {
    const res = await fetch(ENDPOINT, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      },
      signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
    });
    return res.json();
  }
  catch (e) {
      console.error(e);
      return 500;
  }
}

async function fetchItems(userId) {
  const perPage = 1;
  const ENDPOINT = `https://qiita.com/api/v2/users/${userId}/items?per_page=${perPage}`;
  const REQUEST_TIMEOUT_MS = 5000;

  try {
    const res = await fetch(ENDPOINT, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      },
      signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
    });
    return res.json();
  }
  catch (e) {
      console.error(e);
      return 500;
  }
}


async function sendItemToSlack(user, item) {
  const webhookUrl = process.env.webhookUrl;  
  const slackResponse = await fetch(webhookUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
          text: `${user.id}さんが本日、以下の記事を投稿しました :smiley:  \n ${item.title} \n ${item.url}`
      })
  });
}
```


## EventBridge Schedulerの作成

最後に、Lambdaを定期的に実行するための`EventBridge Scheduler`を作成します。
EventBridge Schedulerって何ですか？って思った方は、[こちらの記事](https://qiita.com/namasa/items/dc3f8f8e6934f5aa6a45
)で説明していますので、もしよければ見てください！

`スケジュール名`を入力します。
`頻度`は`定期的なスケジュール`にして、cron式を`40 16 * * ? *`にします。
これで毎日16:40にLambdaを実行するようになります。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/60f729f6-7def-1c45-fa71-31f149b89ab2.png)
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/4861bc2d-83d6-125e-2430-fecaa386e56f.png)

`ターゲット`に`Lambca`の`Invoke`を選択し、先ほど作成したLambda関数を選択します。
後はデフォルト値で設定していけば、作成出来ます。
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/154ed532-dc4c-a2eb-2d14-96ec0f8ef491.png)

## 実行してみる

では、最後に実行結果を確認してみましょう。時間になるとEventBridge SchedulerがLambda関数を実行し、当日に投稿された記事をSlackにメッセージ送信してくれるはずです！
👇当日の記事が2件あった場合、こんな感じになります

![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/798d9ed3-1c7a-be2e-19c9-70a7f74be49b.png)


# もっとこうしたらよさそう

今回は1日1回の頻度で、Lambda関数を実行し、Qiita APIから当日に投稿された記事を取得して、スラックに通知しました。
もっとリアルタイムに近い形で取得したいなら、Lambdaを10分に1回などにしたりするのが良さそうです！


:::note warn
実行頻度を上げる場合は、APIドキュメントにある通り認証しているユーザでないと1時間に60回までしかリクエスト出来ないようです。
必要に応じてアクセスキーを発行して利用しましょう！

> 利用制限
> 認証している状態ではユーザーごとに1時間に1000回まで、認証していない状態ではIPアドレスごとに1時間に60回までリクエストを受け付けます。
:::




# おわりに

とりあえず、ぱっと思いついた方法でフォローしたユーザの当日投稿した記事をスラックに通知する仕組みを作ってみました :smile: 
もっといい方法あるなどありましたら、コメント頂けると幸いです :bow: