Slack Bot開発の右腕に！Google Apps ScriptでBotの投稿削除フォームを作成する

# はじめに

SlackでBotの開発をしようとしていると、どうにもめんどくさいことがありますよね。それは何かというと、Botの投稿を簡単に削除できないということです。Botの開発はテスト投稿をしながら進めていきますが、残っていて欲しい投稿と、消してしまいたい投稿が混ざっていって、とはいえ不要なものを削除しようとすると地味に労力がかかるという・・・。

そんな思いをしているあなたに、大変便利なものを作成しました。それが今回紹介する、Google Apps ScriptのWebアプリを活用した、Botの投稿削除フォームです。SlackのBot開発を進めているあなたの右腕として、ぜひ活用してみてください。

:::note warn
書き上げてから気が付いてしまったのですが、Incoming Webhookではこのフォームは使えません。chat.writeの投稿しか、chat.deleteで消せないということ？
:::

# こんな方におすすめ

- SlackのBot開発をしているが、テスト投稿を削除したくてたまらない
- Google Apps ScriptでWebアプリを作ってみたい


# 投稿削除フォームでできること

投稿フォームは下記のような画面になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492462/64dbada1-e336-a7a6-98c7-0701dadbcb66.png)

削除したいBotの名前を選択し、投稿のURLを改行つきで入力した状態で削除ボタンをクリックすると、対象のメッセージが削除されたレスポンスが表示されます。通常の投稿だけでなく、スレッドでのリプライにも対応しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492462/32088df9-cb3c-2e59-c116-d23fdf0d12f2.png)

現状ではメッセージを選択的に削除することを想定しているので、対象のメッセージ一覧から選択するような機能は実装していませんが、個人的にはこれだけでも大変便利になりました。私にとってはSlack Bot開発の右腕になってもらっています！


# 投稿削除フォームの作り方

Google Apps Scriptで下記のコードを書くことで実装できます。コード自体はめちゃくちゃ簡単です。

```javascript:deleteSlackBotPost.gs
function doGet(e) {
  return HtmlService.createTemplateFromFile('index').evaluate()
}


function deleteSlackBotPost(target_bot, post_link) {
  const token = PropertiesService.getScriptProperties().getProperty('SLACK_BOT_TOKEN' + '_' + target_bot)
  const channel = post_link.split('/')[4]
  const ts = post_link.split('/')[5].substring(1, 11) + '.' + post_link.split('/')[5].substring(11, 17)

  const res = deleteSlackMessage(token, channel, ts)
  return ' - Bot Name: ' + target_bot + ' / Channel: ' + channel + ' / ts: ' + ts + ' / Response: ' + res.getContentText()
}


function deleteSlackMessage(token, channel, ts) {
  const url = 'https://slack.com/api/chat.delete'
  const headers = {
    'contentType': 'application/json',
    'authorization': `Bearer ${token}`
  }
  const payload = {
    'channel': channel,
    'ts': ts
  }
  const options = {
    'method': 'post',
    'headers': headers,
    'payload': payload
  }
  const res = UrlFetchApp.fetch(url, options)
  Logger.log(res)
  return res
}
```

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <h1>Form for Deleting Slack Bot Posts</h1>
    <span>Select Target Bot Name: </span>
    <select id="target_bot">
      <option value="BOT_1">Bot 1</option>
      <option value="BOT_2">Bot 2</option>
    </select><br>
    <textarea id="links" placeholder="Input URL(s) with Line Breaks" rows="5" cols="115"></textarea><br>
		<button type="button" onClick="runScript()">Click to Delete</button>
    <p id="output"></p> 

    <script>
      function runScript() {
        let target_num = document.getElementById('target_bot').selectedIndex
        let target_bot = document.getElementById('target_bot').options[target_num].value
        let links = document.getElementById('links').value.split('\n')
        for (post_link of links){
          google.script.run.withSuccessHandler(displayResponse).deleteSlackBotPost(target_bot, post_link)
        }
      }

      function displayResponse(response) {
        let output_area = document.getElementById('output')
        output_area.innerHTML += response + '<br>'
      }
    </script>
  </body>
</html>
```


# コードの解説
それでは、3つに分けてコードを解説していきます。


## index.htmlの表示

下記のコードによって、Webアプリへアクセスすると、index.htmlに基づいて画面が表示されます。doGet(e)については[以前の記事で簡単にまとめた](https://qiita.com/SoySoySoyB/items/0519e3be92bed02bc6b8#dogetedoposte%E3%81%A8%E3%81%AF%E3%81%AA%E3%81%AB%E3%81%8B)ので、よろしければそちらもご参考ください。

```javascript
function doGet(e) {
  return HtmlService.createTemplateFromFile('index').evaluate()
}
```


## 利用するトークンの選択とtsの変換

下記のコードでは、Webアプリ上で選択されたBotの名前に応じて、スクリプトプロパティに保存したトークンを設定しています。またSlackでは投稿時に時間の情報を付与することで投稿に一意キーをつけているのですが、URLとAPIでの形式が異なっているので、p＋16桁のものを11桁.5桁の形式に変換しています。

そして、投稿の削除のためのAPIを叩いたあとに、その結果を戻り値として戻しています。

```javascript
function deleteSlackBotPost(target_bot, post_link) {
  const token = PropertiesService.getScriptProperties().getProperty('SLACK_BOT_TOKEN' + '_' + target_bot)
  const channel = post_link.split('/')[4]
  const ts = post_link.split('/')[5].substring(1, 11) + '.' + post_link.split('/')[5].substring(11, 17)

  const res = deleteSlackMessage(token, channel, ts)
  return ' - Bot Name: ' + target_bot + ' / Channel: ' + channel + ' / ts: ' + ts + ' / Response: ' + res.getContentText()
}
```


## chat.deleteのAPIの利用

下記のコードで、投稿の削除のためのAPIを叩いています。

```javascript
function deleteSlackMessage(token, channel, ts) {
  const url = 'https://slack.com/api/chat.delete'
  const headers = {
    'contentType': 'application/json',
    'authorization': `Bearer ${token}`
  }
  const payload = {
    'channel': channel,
    'ts': ts
  }
  const options = {
    'method': 'post',
    'headers': headers,
    'payload': payload
  }
  const res = UrlFetchApp.fetch(url, options)
  Logger.log(res)
  return res
}
```


## Webアプリの画面上での処理

ボタンのクリックを起点にして、下部のJavaScriptのコードを実行しているというのが大枠の流れです。その際に、入力された項目の値を抽出しています。

リンクの数だけ、先ほど解説したdeleteSlackBotPostを実行し、withSuccessHandlerでその処理が成功した後にレスポンスを引数としてdisplayResponseを実行しています。ここでは、output_areaというHTMLの領域にレスポンスの内容を追記することで、APIの実行結果を表示するようにしています。

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <h1>Form for Deleting Slack Bot Posts</h1>
    <span>Select Target Bot Name: </span>
    <select id="target_bot">
      <option value="BOT_1">Bot 1</option>
      <option value="BOT_2">Bot 2</option>
    </select><br>
    <textarea id="links" placeholder="Input URL(s) with Line Breaks" rows="5" cols="115"></textarea><br>
		<button type="button" onClick="runScript()">Click to Delete</button>
    <p id="output"></p> 

    <script>
      function runScript() {
        let target_num = document.getElementById('target_bot').selectedIndex
        let target_bot = document.getElementById('target_bot').options[target_num].value
        let links = document.getElementById('links').value.split('\n')
        for (post_link of links){
          google.script.run.withSuccessHandler(displayResponse).deleteSlackBotPost(target_bot, post_link)
        }
      }

      function displayResponse(response) {
        let output_area = document.getElementById('output')
        output_area.innerHTML += response + '<br>'
      }
    </script>
  </body>
</html>
```


## Webアプリとしての公開

Google Apps Scriptの右上の「デプロイ」をクリックし、種類をウェブアプリとすることでWebアプリとして公開することができます。その際、複数のトークンを扱うということもあるので、アクセスできるユーザーは自分のみに限定しておくとよいでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492462/0b4b17be-8c19-0163-84f7-0e8c9b12571c.png)


# さいごに

仕組みとしてもコードとしても大変単純な仕組みですが、これでBot投稿の削除が楽になりました。Slack Bot開発の右腕として、ぜひ活用してみてください！


# 参考

https://api.slack.com/methods/chat.delete

https://dev.classmethod.jp/articles/chatgpt-gas-webapp/

https://techis.jp/guide/js/js_object_display

https://itsakura.com/js-selectbox