Slackでの発言を強制させたいbot

# はじめに
慶應大学 理工学部 情報工学科 B3のぽこちゃんです

今回は自分が運営に携わっている
データサイエンス系学生団体
**DataCampus**にて利用している
**2週間、情報共有チャンネルに投稿がなかったら
本人と管理者に通知を送るbot**
👇

![スクリーンショット 2024-01-16 14.04.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/fc9d55b1-5b86-bfc5-bc94-168f38b358b3.png)

の実装を紹介します

2週間の部分は変えられるので
1日発言が無かったでも1時間動きがなかったでもなんでもできます
基礎機能ではありますが応用性は高いので是非是非

https://twitter.com/DataCampus_tw

Xもやってるのでぜひフォローお願いします
フォロバします!

https://twitter.com/tomato_boy77


# Slack bot
そもそもこのSlack botはGASという
Googleが提供するアプリケーション開発プラットフォームで動かしてて、JavaScriptがベースになってます。
Googleのさまざまなサービスと連携して動作を自動化でききて、スプレッドシートとかフォームとかにも対応してるので﻿めっちゃ便利です

👇いまにゅさんのGAS入門　参考に

https://youtu.be/1TzeiDxBXx4?si=4J0D8FGVoXIE4g2O

## bot作成の準備
1. Slackにログインしている状態で、[Slack API](https://api.slack.com/apps)へアクセスする
1. Your Appという画面が表示されるので「 **Create New App** 」をクリックする
1. App Nameにbotの名前, Select a workspaceでbotを追加したいワークスペースを選択しましょう. ![スクリーンショット 2024-01-16 11.23.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/c2b6ee25-b5b4-4ec4-6416-bfea54649130.png)

## 権限の付与
1. OAuth & Permissionsを選択![スクリーンショット 2024-01-16 11.30.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/757de987-90b7-0f24-1b9d-59226fcb30d1.png)
1. 下に行くとScope画面が表示されるので、Bot Token Scopesの「 **Add an OAuth Scope** 」をクリックする![スクリーンショット 2024-01-16 11.33.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/daf7ef35-96e4-b24e-73e5-415ef615e4be.png)
1. 必要な権限を追加していく(今回は以下の通り)
 ```
calls:read
calls:write
channels:history
channels:join
channels:read
chat:write
groups:read
im:read
im:write
mpim:read
users:read
users:read.email
```
各権限の説明は省きますが、botが関わるところは
- チャンネルに参加
- チャンネルの各ユーザーの最終投稿履歴の取得
- DMにメッセージを送信
といった感じです

## ボットをインストールする
1. **Install App** をクリックする![スクリーンショット 2024-01-16 11.40.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/d06ecba5-9de6-53b5-8211-6e14b7eebb2b.png)
1. 「 **Install App to Workspace** 」をクリックする
1. アクセス権限リクエスト画面が表示されるので、「 **許可する** 」をクリック

## ワークスペースから追加
Slack側で対象のワークスペースの「 **アプリを追加する** 」から先ほど作ったBotを選択する ![スクリーンショット 2024-01-16 11.44.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/994ade4a-debf-6c5c-daf4-b77390839ce7.png)

これでbotが追加できたので次はコードの実装に移ります

# GASの設定

とりあえずGASのスクリプトページまでいきましょう
![スクリーンショット 2024-01-16 11.50.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/005abc81-ed97-b84d-1553-4a5722acd6b9.png)


## GASに設定するコード
いかにコードを書きますがいくつか設定する部分があります
1. SLACK_TOKEN 
![スクリーンショット 2024-01-16 11.55.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/429648ca-91e7-a425-c040-d84251993c43.png)
xoxoで始まる Bot User OAuth Token
1. CHANNEL_ID![スクリーンショット 2024-01-16 11.58.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/abe505b2-86d5-454e-c560-966df98a0c7c.png)
適用したいチャンネルのチャンネル情報タブの下の方にあります
1. MANAGER USER_ID
誰が投稿していないかを管理するメンバーのメンバーIDを取得しましょう![スクリーンショット 2024-01-16 12.02.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/b7b72fcb-f22e-5e12-e9ca-869d47da5dbb.png)

他にも通知文は
```javascript
postMessageToSlack(userId, '2週間、情報共有に投稿がありません!!　何でもいいので軽く投稿してみましょう！');
```
通知を送る閾値は
NOTIFY_THRESHOLDで設定できます(1msc単位かな?)


## コード本文
```javascript
const SLACK_TOKEN = 'YOUR SLACK_TOKEN';
const CHANNEL_ID = 'CANNEL_ID';
const NOTIFY_THRESHOLD = 7 * 24 * 60 * 60 * 1000 * 2; // 2週間

function getChannelMembers() {
  const url = `https://slack.com/api/conversations.members?channel=${CHANNEL_ID}`;
  const response = UrlFetchApp.fetch(url, { 
    method: 'GET' ,
    headers: {
      "Authorization": `Bearer ${SLACK_TOKEN}`
    }
    });
  const result = JSON.parse(response.getContentText());
  if (result.ok && result.members) {
    return result.members;
  } else {
    Logger.log(`Error fetching channel members. Error: ${result.error}`);
    return [];
  }
}

function checkUserLastPost() {
  const members = getChannelMembers();
  members.forEach(userId => {
    const url = `https://slack.com/api/conversations.history?channel=${CHANNEL_ID}&limit=100`; 
    const options = {
      method: 'GET',
      headers: {
        "Authorization": `Bearer ${SLACK_TOKEN}`
      }
    };
    const response = UrlFetchApp.fetch(url, options);
    const result = JSON.parse(response.getContentText());

    if (result.messages && result.messages.length > 0) {

      const userMessages = result.messages.filter(message => message.user === userId);
      if (userMessages.length > 0 ) { 
        const lastPostTime = parseFloat(userMessages[0].ts) * 1000;
        const currentTime = new Date().getTime();

        if (currentTime - lastPostTime > NOTIFY_THRESHOLD) {
          sendNotification(userId);
          Logger.log(`通知が送られました: ${userId} ${getUserName(userId)}`);
        }
      }
    } else {
      Logger.log(`Error or no messages for user: ${userId}. Error: ${result.error}`);
    }

  });
}

function sendNotification(userId) {
  const managerUserId = "MANAGER UDER_ID";

  // 通知を送るべきユーザーの名前を取得
  const userName = getUserName(userId);
  // 本人に通知を送る
  postMessageToSlack(userId, '2週間、情報共有に投稿がありません!!　何でもいいので軽く投稿してみましょう！');
  // 管理者に通知を送る
  postMessageToSlack(managerUserId, `${userName} (ID: ${userId}) は一定期間投稿がありません。`);
}


function postMessageToSlack(channelId, messageText) {
  const url = 'https://slack.com/api/chat.postMessage';
  const payload = {
    channel: channelId,
    text: messageText,
  };
  const options = {
    method: 'POST',
    headers: {
      "Authorization": `Bearer ${SLACK_TOKEN}`,
      "Content-Type": "application/json"
    },
    payload: JSON.stringify(payload)
  };

  UrlFetchApp.fetch(url, options);
}

function getUserName(userId) {
  const url = `https://slack.com/api/users.info?user=${userId}`;
  const options = {
    method: 'GET',
    headers: {
      "Authorization": `Bearer ${SLACK_TOKEN}`
    }
  };
  const response = UrlFetchApp.fetch(url, options);
  const result = JSON.parse(response.getContentText());

  if (result.ok && result.user) {
    return result.user.real_name || 'Unknown User';
  } else {
    Logger.log(`Error fetching user info. Error: ${result.error}`);
    return 'Unknown User';
  }
}
```

## トリガー設定
最後にこのスクリプトをいつ動かすかの設定が必要になります。
GASの左側のメニューからトリガーを選択しましょう
![スクリーンショット 2024-01-16 12.10.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/35e82e6d-fb8b-2e9d-a38d-175b4b45053d.png)

右下の青ボタン「**トリガーを追加**」を押して

実行する関数を「**checkUesrLastPost**」を選択
あとは下にある
時間ベースで1時間おきに実行とか
とか
日付ベースで毎日0時から1時の間に実行とか選びましょう!

![スクリーンショット 2024-01-16 12.09.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436034/def5b132-d547-f85a-b5b1-eba3b53a0572.png)


## まとめ
今回は**2週間、情報共有チャンネルに投稿がなかったら
本人と管理者に通知を送るbot**の作り方紹介でした

勉強したくなくて書いてただけですw
色々応用あると思うのでぜひ作ってみてください

Xもやってるのでぜひフォローお願いします

https://twitter.com/tomato_boy77

ご読了ありがとうございました!