Slackで疑似ユーザーグループにメンション機能を付ける(GAS+SlackApp+OutgoingWebhook)

#はじめに
去年の今頃に、自分が所属している団体でslackを使わなくてはいけなくなり、色々設定した中で特に大変だったのが、以下の機能です

**・グループ名に反応し、そのメンバー全員をスレッドでメンションを付ける**

例えば、Aグループのメンバーに通知をしたい時は、@Aグループなどとすれば、そのスレッドにメンバーへのメンション（@太郎、@次郎、@花子など）が飛ぶ仕様です。
これ、実はslackの課金機能のユーザーグループを使えば楽っぽいのですが、課金することは許されず、なんとかbotで頑張るしかない状況でした。
やっかいなところは、スレッドにメンションを付けるところで、チャンネルにそのまま投稿して良いなら以下のようにslack botを使えばすぐできることでした。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/6d00e09e-57b9-f9f0-da49-e848a851446a.png" width=30%>
これだと、スレッドに続けて会話できなくなってしまいます（どこにスレッド付ければいいの？ってなる）。
なので、下のようにしたいのです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/4dedb8a2-810b-9ccb-e0ba-abbe3bbac4cd.png" width=30%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/7a19e535-095c-44b6-27c7-9eea79c249d8.png" width=30%>
（Aチームという言葉に反応して、スレッドにてAチームの人たちにメンション投げる）

**メリット**

- いちいちメンション付けなくてよい（グループの人数が多い場合）
- 無駄にチャンネルが騒がしくならない



これが結構めんどくさかったので記事にしました。
去年やったことなので、忘れていることもあるかもしれません。
あと、outgoingwebhookは使わなくてもSlackAppだけでもいけそうですね。
またどなたかがすでに記事にされていたらすみません！

#やったこと
- OutgoingWebhookでキーワードを検知、GASに送る
- GASのコードを編集
- SlackAppの設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/991baada-de7a-3be2-8b4a-6b59166e7894.png)
(イメージ図）


#OutgoingWebhook
webhookは外部サービスからslackへと送受信する方法で、Incoming WebhookとOutgoing Webhookがあり、今回は主にOutgoing Webhookを使います。
[Outgoing Webhook](https://api.slack.com/legacy/custom-integrations/outgoing-webhooks) はSlackのメッセージの中からキーワードを検知し、指定したURLにPOSTすることができます。json形式で返すとレスポンスを返してくれるっぽいでのですが、今回は使いません（スレッドに返す必要があるため）。
まず、このOutgoingWebhookの設定を行います。
#####アプリの追加
slackのアプリを追加するボタン→検索→outgoing webhookで検索→追加→設定
でアプリの追加、設定を行います。
####outgoing webhookを設定
設定を押すとoutgoing webhookのページに飛ぶと思います。
そこで、slackに追加って押すと、設定が始まります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/781462d7-bb12-fe46-d605-5705a5d6e104.png" width=70%>
URLはGASのウェブアプリケーションのURLを貼ります（後述取得）
ここでトークンをメモしておきます。

#Slack App
以下のリンクからslackのアプリを作成します。
https://api.slack.com/apps/
Crate New Appを押せば作成が始まり、アプリ（bot)の名前と使うワークスペースを決めます。
OAuth & PermissionsのscopesでBot Token Scopesに`Add an OAuth Scope`からchat:writeを追加します。
(User Token Scopesに追加すると、ユーザー自身としてアクションが起きます）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/e3cfedaa-bb46-a1b5-e287-36f5db9496e6.png" width=50%>

その後、Install Appから`Install to Workspace`で権限のリクエストを許可し、ワークスペースに追加します。
`Bot User OAuth Access Token`が生成されるので、メモしておきます。

そして、この機能を使いたいチャンネルにアプリを追加します。
チャンネルの設定→その他→アプリの追加からアプリを追加します。
#GAS
主に以下の記事を参考にさせていただきました
・[Slack上のメッセージをGoogleAppsScriptで受け取ってよしなに使う](https://qiita.com/kyo_nanba/items/83b646357d592eb9a87b)

GoogleAppScriptにコードを書いていきます。まとめると次のようなコードになりました。

```javascript

function doPost(e) {
  var text = "<@user_id>,<@user_id2>,<@user_id3>";
  var verify_token = "OutgoingWebhookのトークン";
  if (verify_token != e.parameter.token){
  throw new Error("invalid token.");
  }
  var channel = e.parameter.channel_name;
  var ts = e.parameter.timestamp;
  if(e.parameter.thread_ts){
    ts = e.parameter.thread_ts;
  }
  var app = SlackApp.create("SlackAppのトークン");
  var options = {
    thread_ts: ts,
  }
  app.chatPostMessage(channel,text,options );
}
```
####メンションを飛ばす
`<@user_id>`ではuser_idを指定します。
user_idの取得方法や記述方法は以下の記事を参考にしました。
・[SlackのIncoming Webhooksでメンションを飛ばす方法](https://qiita.com/ryo-yamaoka/items/7677ee4486cf395ce9bc)
slackのユーザープロフィール→その他→メンバーidをコピーで取得できるみたいです。

###Post Data取得
[slackapiのドキュメンテーション]('https://api.slack.com/legacy/custom-integrations/outgoing-webhooks')によればpost Dataは以下のような形で来るらしいです。

```
token=XXXXXXXXXXXXXXXXXX
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
thread_ts=1504640714.003543
timestamp=1504640775.000005
user_id=U2147483697
user_name=Steve
text=googlebot: What is the air-speed velocity of an unladen swallow?
trigger_word=googlebot:
```
このうち、`timestamp`とはそのメッセージが投稿された日時のことで、リンクになっていることから共有・参照することができます。
`thread_ts`は、`thread_timestamp`の略で、スレッドになっている場合の親のタイムスタンプになります。また、これを使ってそのメッセージがスレッド中かどうか判断することができます。

```javascript

  var ts = e.parameter.timestamp;
  if(e.parameter.thread_ts){
    ts = e.parameter.thread_ts;;
  }
```

ここでは、メッセージチャンネルの中に地で打たれたら、そのメッセージのタイムスタンプを、スレッド中の場合はそのスレッドの親のタイムスタンプを変数にいれています。
これは、スレッド中にキーワードが出てもそれに反応し、スレッドの中でそのままメンションを飛ばせるようにするためです。

####SlackApp

```javascript
  var app = SlackApp.create("SlackAppのトークン");
  var options = {
    thread_ts: ts,
  }
  app.chatPostMessage(channel,text,options );
```
SlackAppのトークンに先ほど作成した`Bot User OAuth Access Token`を添付します。
[chatPostMessage]('https://api.slack.com/methods/chat.postMessage')のoptionの`thread_ts`使ってスレッドにメッセージを投げます。
ドキュメンテーションには、
`Provide another message's ts value to make this message a reply. Avoid using a reply's ts value; use its parent instead.`
と書かれてあり、だから前項で親のタイムスタンプがある場合はそれを取得しなくてはいけなかったんですね。
あと、忘れてはいけないのは、SlackAppをLibraryに追加することです([参考](https://qiita.com/yukihirai0505/items/cbb9832d42dc627800fb))。
SlackAppのLibrary Keyは以下 
`M3W5Ut3Q39AaIwLquryEPMwV62A3znfOO`

add Libraryに追加すると以下のようになります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/ec9d84a0-370a-78dc-9ebd-dfb2912a4723.png" width=50%>

####ウェブアプリケーションの公開
※GASの仕様が変わったみたいで、新しいエディタだと仕様が違うかもしれません。
公開→ウェブアプリケーションの公開から下のように設定します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/19eafd3f-da0e-1303-4a07-e5b389f59ace.png" width=50%>
Project versionをNewにすること、Who has access to the app:の覧を「全ユーザー」ではなく、匿名のものも含めること(Anyone, even anonymous)に注意してください。
更新し、生成されたweb app URLを先ほどのOutgoing WebhookのURLに添付します（Outgoing Webhookの更新）。

#完成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/4dedb8a2-810b-9ccb-e0ba-abbe3bbac4cd.png" width=30%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/7a19e535-095c-44b6-27c7-9eea79c249d8.png" width=30%>
完成です。このようにスレッド中でもしっかり反応します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422074/4555278a-cced-dec3-00a0-e0f5c10c2496.png" width=30%>

つまずいたところは、
- SlackAppのライブラリを追加しなくてはいけない点
- バージョンをNewにする必要がある点
- 全ユーザーではなく、匿名も可にする必要がある点
- スレッドの取得方法
- チャンネルにアプリを追加するのを忘れていた

でした。

#終わりに
スレッドに関して反応する記事が点在としていてまとまっていなさそうだったので、今回の記事を書かせていただきました。
意外に、やることが多くてびっくりです笑
Slack apiの更新で、OutgoingWebhookを使わなくても良さそうなんですが、今回はこの方法を取ってしまいました。
なにか間違いなどあったらすみません・・・