#本田とじゃんけん ができなくなって寂しいのでSlackAppで戯れた

皆さんは #本田とじゃんけん というTwitterで結構流行ったものを覚えていますでしょうか．流石にこの記事が投稿された2019/4で忘れてしまったという方は少ないでしょうが，時がたつにつれ，記憶というものは風化され，誰からも思い出されなくなってしまいます．そんなの寂しいですよね．

そこで，Slackの中でも本田圭祐と無限にじゃんけんをできるようにすることでいつでもどこでもいつまでも本田とじゃんけんができるわけです．こうすれば，風化されずに済みますね！

## Slackapp

Slackには[Slash Command](https://api.slack.com/slash-commands)というものが存在します．例えば

```
/remind 04/21 11:00 am 書類郵送
```

などとメッセージボックスに打てば，指定日指定日時にリマインダを設定できます．

今回利用するのは，これを自作できる[Custom Slash Commands](https://slack.com/apps/A0F82E8CA-slash-commands?next_id=0)です．このインテグレーションを自グループのslackにログイン後，追加しましょう．

![](https://i.gyazo.com/1ea3260f4a2613fa4a6c842f6d29b932.png)

そのあと，設定を追加をクリックすることで，自作コマンドの追加が行えます．

![](https://i.gyazo.com/41f75abed58aa8d7bcdaf79a978be54e.png)

適当に名前を設定しましょう．

画面を下に送るとURLとメソッドとラベルが付いたフォームがあるかと思います．要するに，どこかのサーバにPOSTやGETを送って，その結果をSlackに表示できるというわけです．

自分でサーバを借りてGETやPOSTを構築してもいいですがたかが本田とじゃんけんのためにサーバを借りるのはなんか癪です[^1]．

そこでGoogle Apps Scriptを利用して，サーバを借りたりせずにじゃんけんを作ってみましょう．

## Google Apps Script

起動方法は何でもいいのですが，https://script.google.com/ に移動して新規スクリプトを作るのが一番早いでしょう．このScriptでは，SlackからのGETやPOSTを受けて，何かしらの処理を行うことができます．

GETを受けるには`function doGet(e)`という関数を用います．POSTは`doPost(e)`です．`e`にはSlackからの送信データが`e.parameter`以下のように入っています．

```
token=********
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
user_id=U2147483697
user_name=Steve
command=/weather
text=94070
response_url=https://hooks.slack.com/commands/1234/5678
```

この場合はSteveがtestチャンネルで`/weather 94070`というコマンドを打ったということを意味しています．

ですので`/janken チョキ`のようなデータを送られてくるものとして，本田にじゃんけんを行わせましょう．

スクリプトは以下です．

```javascript

function doGet(e) {
  var text = e.parameter.text;
  var hand = 0;
  // 正規表現を用いて手がなんなのかをhandに保存しときます
  if((/(グー|こぶし|fist|グーパンチ|パンチ|facepunch)/g).test(text)) hand = 1;
  if((/(チョキ|ピース|v)/g).test(text)) hand = 2;
  if((/(パー|手|手のひら|hand|開いた手を挙げる|raised_hand_with_fingers_splayed)/g).test(text)) hand = 3;
  var restxt = "";
  var resurl = "";
  var win = 0.98 < Math.random();
  if(hand === 1){
    if(win){
      restxt = "ジャンケンポン！\n\nYOU WIN!\n\nやるやん\n\n明日俺にリベンジさせて\n\nでは，どうぞ";
      resurl = "https://チョキの画像.png";
    }else{
      restxt = "ジャンケンポン！\n\nYOU LOSE\n\n俺の勝ち！\n\n負けは次につながるチャンスです\nネバーギブアップ\n\nほな，いただきます";
      resurl = "https://パーの画像.png";
    }
  }
  if(hand === 2){
    if(win){
      restxt = "ジャンケンポン！\n\nYOU WIN!\n\nやるやん\n\n明日俺にリベンジさせて\n\nでは，どうぞ";
      resurl = "https://パーの画像.png";
    }else {
      restxt = "ジャンケンポン！\n\nYOU LOSE\n\n俺の勝ち！\n\nたかがジャンケン，そう思ってないですか\nそれやったら明日も\n俺が勝ちますよ\n\nほな，いただきます";
      resurl = "https://グーの画像.png";
    }
  }
  if(hand === 3){
    if(win){
      restxt = "ジャンケンポン！\n\nYOU WIN!\n\nやるやん\n\n明日俺にリベンジさせて\n\nでは，どうぞ";
      resurl = "https://グーの画像.png";
    }else{
      restxt = "ジャンケンポン！\n\nYOU LOSE\n\n俺の勝ち！\n\nなんで負けたか明日まで考えといてください\nそしたら何かが\n見えてくるはずです\n\nほな，いただきます";
      resurl = "https://チョキの画像.png";
    }
  }
  if(hand === 0)restxt = "お前ケイスケホンダやな?"
  var res = {
    "parse": "full",
    "response_type": "in_channel",
    "text": restxt,
    "attachments":[
      {
        "image_url": resurl
      }
    ]
  };
 
  return ContentService.createTextOutput(JSON.stringify(res)).setMimeType(ContentService.MimeType.JSON);
}
```

順にみていきましょう．

```javascript
  if((/(グー|こぶし|fist|グーパンチ|パンチ|facepunch)/g).test(text)) hand = 1;
  if((/(チョキ|ピース|v)/g).test(text)) hand = 2;
  if((/(パー|手|手のひら|hand|開いた手を挙げる|raised_hand_with_fingers_splayed)/g).test(text)) hand = 3;
```

は正規表現にテキストがマッチするかをboolで返す`/regexp/.test(text)`の形式のtest関数を利用します．色々種類があるのは絵文字に対応するためです．(/janken :hand_splayed:　と打っても反応してくれたら嬉しいじゃないですか)

```javascript
  var win = 0.98 < Math.random();
```

本田の勝率はなんと怒涛の98%以上らしいです．0-1の乱数が0.98を超えていたらあなたの勝ちです．この数値を変えれば本田の強さが変えられます．ケイスケホンダの真髄がこの一行に込められています．

```javascript
  if(hand === 1){
    if(win){
      restxt = "ジャンケンポン！\n\nYOU WIN!\n\nやるやん\n\n明日俺にリベンジさせて\n\nでは，どうぞ";
      resurl = "https://チョキの画像.png";
    }else{
      restxt = "ジャンケンポン！\n\nYOU LOSE\n\n俺の勝ち！\n\n負けは次につながるチャンスです\nネバーギブアップ\n\nほな，いただきます";
      resurl = "https://パーの画像.png";
    }
  }

// ...中略...

  if(hand === 0)restxt = "お前ケイスケホンダやな?"
```

予め本田の画像がどこかに上がっているとしてそのURLをresurl,ホンダの至言をrestxtに代入します．

ついでに該当しなかった場合の処理を書いています．

```javascript
  var res = {
    "parse": "full", // URL エスケープする
    "response_type": "in_channel", // 送られた場所に返してあげよう
    "text": restxt,
    "attachments":[ // 追加でつける装飾
      {
        "image_url": resurl // 画像のURL
      }
    ]
  };
```

レスポンスをJSONで書きます．Attachmentには色々種類があり，[ここ](https://api.slack.com/docs/messages/builder?msg=%7B%22text%22%3A%22I%20am%20a%20test%20message%20http%3A%2F%2Fslack.com%22%2C%22attachments%22%3A%5B%7B%22text%22%3A%22And%20here%E2%80%99s%20an%20attachment!%22%7D%5D%7D)で実際にAttachmentがどのように投稿に影響するかを試すことができます．

```javascript
  return ContentService.createTextOutput(JSON.stringify(res)).setMimeType(ContentService.MimeType.JSON);
```

JSONを送り返します．

これだけで非常に簡単にチャットボットまがいのものが作れます．

## Slackに登録しよう

これでできたGASからWebアプリケーションとして公開して，slackのcustom slash commandsに登録します．URLを得るためには，GASのスクリプトエディタのツールバーより，「公開」→「ウェブ アプリケーションとして導入...」とします．この際，アプリケーションにアクセスできるユーザのところは「全員(匿名ユーザーを含む)」を選択します．その後，URLが生成されます．そのURLを先ほどのSlackのCustom Slash Commandsに入力します．

![](https://i.gyazo.com/2ff4b33b3cd44554aec93b23e42087b3.png) 

GETなのでメソッドは「取得する」にします．

そして保存しましょう．

これで完成です！

実際に呼び出してみた画像はこちらです．

![Image from Gyazo](https://i.gyazo.com/c232216290eae6b368b4e638389ade36.png)

いい感じです．なかなか勝てないところにケイスケホンダを感じます．

## おわりに

遊び心もボットの醍醐味ですよね．そこそこ羽休め的コーディングになるかと思います．

[^1]: なんかすごく失礼なこと言ってますね．すみません...