Slackのスラッシュコマンドひとつで勤怠連絡をしたかった

## やりたかったこと
下記2つの作業をSlackのスラッシュコマンドひとつで終わらせたい。
1. Slackへ勤怠連絡メッセージを送信
2. メールでも同様に勤怠連絡メッセージを送信
※漢字フルネーム、日付、休み理由を記載し、1件ずつ送信するの地味にめんどくさい。
※なぜ2カ所に連絡するかは聞かないでください。そういう仕様です。

## 最終結果
スラッシュコマンドは使えなかったのですが、GASを直接実行することで、Slackとメールの送信の半自動化には成功しました。
なぜSlackのスラッシュコマンドを使えなかったかについては[後述](#はまった点注意点)します。

## 手順
Slackで必要な設定はこちらの記事を参考にさせていただきました。
GASからSlackメッセージを送信するまでの手順はこちらを見ればすべてわかると思いますので、本記事では割愛します。[^1]

https://qiita.com/flowernotfound/items/318daa37b72558175b4a

## GASの内容
```js
// 🎅できるだけ入力する手間を減らしたいので、名前には略称を指定し、略称に紐づくフルネームをスプレッドシートから取得
// 以下のような形式でSLASH_COMMANDを指定します
// name zen|han(全休|午前休) reason#
// 🎅スラッシュコマンドをどうしても使いたかったようです
const SLASH_COMMAND = 'kage zen 1';
const YOUR_NAME = 'YOUR_NAME';
const ss = SpreadsheetApp.openByUrl('YOUR_URL');

const doPost = () => {
  const REASON_1 = 'よくある理由1';
  const REASON_2 = 'よくある理由2';
  const [nameAbbr, type, reason] =  SLASH_COMMAND.split(' ');
  const textFinder = ss.createTextFinder(nameAbbr);
  const targetRowIndex = textFinder.findNext().getRowIndex();
  const lastName = ss.getRange(`B${targetRowIndex}`).getValue();
  const firstName = ss.getRange(`C${targetRowIndex}`).getValue();

  if (type !== 'zen' && type !== 'han') {
    console.error('zen(全休), han(午前休) のいずれかを指定してください');
    return;
  }
  const typeText = type === 'zen' ? '全' : '午前';

  let reasonText = '';
  switch (reason) {
    case '1':
      reasonText = REASON_1; break;
    case '2':
      reasonText = REASON_2; break;
    default:
      reasonText = reason ? reason :'私用';
  }

  const text = `${lastName}さんですが、${reasonText}のため${typeText}休との連絡がありました。`  

  sendSlackMessage(text);
  sendEmail(typeText, firstName, lastName, text);
}

const sendSlackMessage = (text) => {
  const endpoint = 'https://slack.com/api/chat.postMessage' // Slackメッセージ投稿のためのエンドポイント 
  const token = PropertiesService.getScriptProperties().getProperty("SLACK_OAUTH_TOKEN")
  const channel = 'CHANNEL_ID'; // 勤怠連絡チャンネル
  const payload = {
    token, channel, text
  }
  const options = {
    method: 'POST',
    payload,
  }
  UrlFetchApp.fetch(endpoint, options);
}

const sendEmail = (typeText, firstName, lastName, reasonText) => {
  const TO_ADDRESS = "TO_ADDRESS"; // 勤怠連絡用メールアドレス
  const today = new Date();
  const query = `to:(${TO_ADDRESS}) 勤怠連絡, ${lastName}, ${today.getMonth() + 1}/${today.getDate()}`;
  const threads = GmailApp.search(query);
  if (threads.length > 0) {
    console.error(`${lastName}さんの勤怠連絡メールはすでに送信済みです`);
    return [];
  }

  const subject = `【勤怠連絡】${lastName}${firstName} ${today.getMonth() + 1}/${today.getDate()} ${typeText}休`;
  const body = `
    各位

    お疲れ様です。${YOUR_NAME}です。

    ${reasonText}

    以上、よろしくお願いいたします。
    `;
  GmailApp.sendEmail(TO_ADDRESS, subject, body);
}

```

スプレッドシートはこんな感じ
![スクリーンショット 2024-12-22 21.28.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908779/9ce65c23-96c4-f642-f580-1b4b988b701e.png)

## 結果
無事Slackとメールの送信ができました！GAS実行してからは約2秒で完了！
※文言は記事用に変更しています。

![スクリーンショット 2024-12-22 22.29.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908779/3bd9e49a-c11f-84a4-1e8d-7284bd9889fd.png)

![スクリーンショット 2024-12-23 10.17.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908779/1468bf59-3c8d-6f15-03c1-8654714fec0b.png)


## はまった点、注意点
1. Slackにメッセージを送信するだけであればIncoming Webhookの方が簡単にできそうだが、非推奨になっているためAppを作成した方が良さそう[^2]
2. 当初スラッシュコマンドからGASを実行したかったが、何度試しても`dispatch_failed`とだけ表示されスラッシュコマンドのリクエストが失敗… 
色々試している中で、私用のGmailアカウントとSlack環境で試すと成功したため、社用のGoogle Workspace側で制限がかかってるっぽい（実行する方法知ってる方いたら教えてください！）
3. 通知したいチャンネルに、作成したApp自身を追加することも忘れずに

## 最後に
当初やりたかったことはできませんでしたが、久しぶりにGAS書くの楽しかったです。
コードが雑なのと、オプション機能がつけられていないので、また気が向いたら修正していこうと思います。

## 参考
[^1]:https://qiita.com/flowernotfound/items/318daa37b72558175b4a
[^2]:https://api.slack.com/legacy/custom-integrations/messaging/webhooks