すてにゃん氏の技術ぶろぐ

技術っぽいこと書きます

チャットの途中に乱入してくるGet Wild Slack Botを作った

この記事はGetWild Advent Calendar 2016 3日目の記事です。
qiita.com

今回はGet Wild Slack Botを作りました。

作り方

  • Google Apps ScriptでSlack botを実装する
  • SlackのIncomingとOutgoingなWebhookを設定する
  • 完成

コード

だいたいこういう感じのものを書きました。変数名にWildやToughを使ったところよくわからない状態のままバグったりして困ったので、一般的なコードを書くとき変数名はちゃんとしましょう。

var INCOMING_WILD_URL = 'https://hooks.slack.com/services/hoge/fuga/piyo';
var WILD_USERNAME = 'getwildbot';
var WILD_ICON_EMOJI = ':get_wild:';
var WILD_TOKEN = 'SlackのOutgoing Webhookの設定にあるトークン';
var WILD_USER_ID = 'Slackの自分のユーザID';

var GET_WILD_AND_TOUGH = [
  'アスファルトタイヤを切りつけながら',
  '以下略...',
];

// SlackからのOutgoing webhookを受け取る関数
function doPost(e) {
  if (e.parameters.token == WILD_TOKEN && e.parameters.user_id == WILD_USER_ID) {
    var wildtext = e.parameters.text[0];
    var wildStruct = getWildStruct(wildtext);
    startToughSequence(wildStruct);
    return null;
  }
}

// Slackの投稿を元に、一番長いsuffixを探索し返す
function getWildStruct(wildText) {
  var wildIndex = -1;
  var wildMatch = '';
  var wildSuffixLength = 0;
  for (var i = 0; i < GET_WILD_AND_TOUGH.length; ++i) {
    var currentWildSuffixLength = 0;
    for (var j = 0; j < GET_WILD_AND_TOUGH[i].length - 1; ++j) {
      if (wildText[wildText.length - j - 1] == GET_WILD_AND_TOUGH[i][GET_WILD_AND_TOUGH[i].length - j - 1]) {
        currentWildSuffixLength++;
      } 
      else {
        if (currentWildSuffixLength > wildSuffixLength && currentWildSuffixLength > 2) {
          wildSuffixLength = currentWildSuffixLength;
          wildIndex = i;
          wildMatch = GET_WILD_AND_TOUGH[i].substring(GET_WILD_AND_TOUGH[i].length - j);
          break;
        }
      }
    }
  }
  return {
    wildIndex: wildIndex,
    wildMatch: wildMatch
  };
}

// wild structを元に続きの歌詞を投稿していく
function startToughSequence(wild) {
  if (wild.wildIndex != -1) {
    postWildText(wild.wildMatch + '...?');
    sleepTough(2000);
    for (var getWildLine = wild.wildIndex+1; getWildLine < GET_WILD_AND_TOUGH.length; ++getWildLine) {
      postWildText(GET_WILD_AND_TOUGH[getWildLine]);
      sleepTough(3500);
    }
  }
}

// n millisecond sleepする
// http://stackoverflow.com/a/17936490
function sleepTough(toughMilliseconds){
  var now = new Date().getTime();
  while(new Date().getTime() < now + toughMilliseconds){} 
}

// Slackに投稿
// ref. https://github.com/motemen/gas-cronsheet-slack
function postWildText(text) {
  var msg = new SlackMessage({
    username: WILD_USERNAME,
    icon_emoji: WILD_ICON_EMOJI,
    header: text,
  });
  var options = {
    method: 'post',
    payload: JSON.stringify(msg.toPayload())
  };
  var res = UrlFetchApp.fetch(INCOMING_WILD_URL, options);
  if (res.getResponseCode() != 200) {
    Logger.log(res);
  }
}

// SlackMessage
// ref. https://github.com/motemen/gas-cronsheet-slack
var SlackMessage = (function () {
  function SlackMessage(config) {
    this.fields = config.fields;
    this.username = config.username;
    this.icon_emoji = config.icon_emoji;
    this.header = config.header;
    this.footer = config.footer;
  }
  SlackMessage.prototype.addField = function (title, value) {
    this.fields.push({ title: title, value: value });
  };
  SlackMessage.prototype.toPayload = function () {
    var payload = {
      attachments: [
        {
          fallback: this.header,
          pretext: this.header,
          fields: this.fields,
          footer: this.footer
        }
      ],
      username: this.username,
      icon_emoji: this.icon_emoji,
    };
    return payload;
  };
  return SlackMessage;
})();

様子

f:id:stefafafan:20161203160426p:plain
自分の発言のsuffixが歌詞の行のsuffixに含まれてるとその時点でgetwildbot氏が「おっ」と反応し、その続きの歌詞をドンドン投稿してくれます。なるほど、便利ですね。

注意点

  • Google Apps Scriptの方は変更のたびに「ウェブアプリケーションとして導入」でプロジェクトバージョンをアップデートするようにすれば安心っぽい。またそこで取得できるURLをSlackのOutgoing Webhookに指定すればOK
  • SlackのOutgoing Webhookでトークンが取得できるのでそれでGAS側でバリデーションすると良さそう
  • botが自分自身の発言に反応しないように気をつけたい (以下のようになる)

f:id:stefafafan:20161203153918p:plain

  • SlackからのOutgoing Webhookを受け取った際、GASのLogger.logでログを確認できると思いきや上手くできなくて困った、手動でdoPost関数などをGAS上で実行すればログは確認できるのでそれでやっていった
  • GAS上でコード書いてるとたまにインデントおかしくなったので、手元で書いてGASのエディタにコピペするとやりやすそう
  • Incoming/Outgoing WebhookのURLをミスってしまうと、間違って会社のSlackに投稿してしまうなどやっちゃうので細心の注意で (以下は、個人用Slackで自分が投稿したところ、Incomingが会社用Slackのものに設定されていたため、突然getwildbotが登場してしまった様子)

f:id:stefafafan:20161203153733p:plain

  • 動作確認でミスってSlackに投稿しまくってしまうとSlackに怒られる

f:id:stefafafan:20161203153746p:plain

まとめ

  • GASでシュッとSlackのbot作れてとても便利だけどデバッグがやりづらい気がした
  • Get Wildは名曲
  • みんなもGet Wildしましょう!

※この記事はAmazon Prime MusicのGET WILD '89を聴きながら書かれたものです。