【GAS】Slackの分報（times）の集約チャンネルを作ってみた

# はじめに
弊社ではエンジニア・デザイナーを中心にSlackで分報（timesチャンネル）作成しています。
今回は「**timesチャンネルの集約チャンネルがあったらいいなぁ**:dolphin: 」
という要望をいただいたので、作ってみました。

# 実装したこと
- timesチャンネルに投稿された内容を、times_allチャンネルに自動投稿する

# 参考記事
以下の記事を参考にさせていただきました。ありがとうございます。

[Slackの分報を社内Twitterに！皆の分報を一つのチャネルに集約するSlackボットを作ってみた](https://zenn.dev/ryo_kawamata/articles/times-all-bot)
[Slackのチャンネルのメッセージを1つのチャンネルに集約するSlackボットを作ってみた](https://zenn.dev/igara/articles/bot_slack_matome_channnel)

# 実際の画面
![times_all.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577266/c7507148-e443-0e9c-d7c6-a847e023a79f.gif)

# times_allチャンネルに投稿されるまでの流れ
1. timesチャンネルに新しいメッセージが投稿される
2. GASでメッセージのパーマリンクURLを取得
3. SlackのAPIにPOSTリクエストを送信
4. times_allに投稿される

# Slackアプリの作成と設定
https://api.slack.com/apps からアプリを作成します。

OAuth & PermissionsのScopesで以下の項目を設定します。
- channels:history
- channels:read
- chat:write
- incoming-webhook

同ページ内の**Bot User OAuth Token**（「xoxb-」から始まる）は後で使用します。

# Spreadsheetの作成とGASの開発
Google Spreadsheetを作成します。

拡張機能のApps Scriptを選択し、以下のコードを記述します。

```slack.gs
function doPost(e) {
  const params = JSON.parse(e.postData.contents);
  
  // SlackからのURL検証リクエストの確認
  if (params.type === 'url_verification') {
    return ContentService.createTextOutput(params.challenge);  // challengeパラメータをそのまま返す
  }

  // メッセージがtimes_userN
  ameチャンネルから来た場合に処理を実行
  const event = params.event;
  if (event.type === 'message' && event.channel && event.text) {
    // 複数のtimes_userNameチャンネルIDを配列に格納
    const TIMES_USERNAME_CHANNEL_IDS = [
      'channelId',   // times_userName1
      'channelId',   // times_userName2
      'channelId',   // times_userName3
      // 必要なだけチャンネルIDを追加
    ];
    
    const TIMES_ALL_CHANNEL_ID = 'channelId';  // times_all チャンネルのID

    // 「投稿元のチャンネルがtimes_userNameチャンネル」かつ「ワークフローやbotメッセージではない」場合に処理を実行
    if (TIMES_USERNAME_CHANNEL_IDS.includes(event.channel) && event.subtype !== 'bot_message' && event.subtype !== 'workflow_run') {
      // ワークスペース名を含めたSlackメッセージのパーマリンクURL
      const workspace = 'companyName'; // Slackワークスペース名
      const messageUrl = `https://${workspace}.slack.com/archives/${event.channel}/p${String(event.ts).replace('.', '')}`;

      // messageUrlが正しく作成されたか確認
      Logger.log('Generated message URL: ' + messageUrl);
      
      postToTimesAll(messageUrl, TIMES_ALL_CHANNEL_ID);
    } else {
      Logger.log('Excluded message: ' + event.text);
    }
  }

  return ContentService.createTextOutput('OK');
}

const SLACK_API_TOKEN = 'xoxb-hogehoge';  // SlackのBot User OAuth Token

// Slackのメッセージをtimes_allに送信する関数
function postToTimesAll(messageUrl, channelId) {
  const payload = {
    channel: channelId,
    text: `新しい投稿です: <${messageUrl}|投稿を見る>`  // パーマリンク形式で投稿
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      'Authorization': 'Bearer ' + SLACK_API_TOKEN
    },
    payload: JSON.stringify(payload)
  };

  try {
    // SlackのAPIにPOSTリクエストを送信
    const response = UrlFetchApp.fetch('https://slack.com/api/chat.postMessage', options);
    const jsonResponse = JSON.parse(response.getContentText());
    
    if (!jsonResponse.ok) {
      Logger.log('Error posting to Slack: ' + jsonResponse.error);
    }
  } catch (error) {
    Logger.log('Error in postToTimesAll: ' + error.message);
  }
}

```

# GASのプロジェクトをデプロイする
![deploy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577266/5e41b991-8625-348e-9546-7f483e26ac4e.png)

Apps Scriptページのデプロイボタンから新しいデプロイを選択します。
種類は「**ウェブアプリ**」、アクセスできるユーザーは「**全員**」を選択します。
ウェブアプリのURLが発行されるのでコピーします。

# SlackアプリでEvent Subscriptionsの設定
SlackアプリのEvent Subscriptionsに移動します。

**Request URL**に先ほどコピーしたURLを貼り付けて、Verifiedが表示されたら成功です。

次に**Subscribe to bot events**で、**message.channels**を追加して完了です。

![eventSubscriptions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577266/a1798cde-5b05-5178-a2cf-812190754df4.png)

# 完成
times_allチャンネルには、パーマリンク形式で投稿されます。
（botアイコンは[shigureni free illust](https://www.shigureni.com/)からお借りしました。可愛い）
![times_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3577266/a7469303-dda9-4b5e-8395-01b092e68a80.png)

誰かがtimesチャンネルに参加したときも自動投稿されてしまうので、
「チャンネルに参加しました」を含むメッセージは除外でもいいかもしれません。

:::note warn
※今回の実装でtimes_allチャンネルに自動投稿されないメッセージの種類
- ワークフローによる投稿
- botメッセージ
- 画像のみの投稿
:::

# まとめ
参考記事の内容が私には難しかったので、よりシンプルな方法でチャレンジしてみました。

X（旧：Twitter）感覚で、メンバーのtimesを確認できるので、お気に入りです
これからも社内外の要望を実現できるように、色々な分野で知識を身につけていきます。