Google Apps Script(GAS)を用いてSlackからLINE Botを操作する

どうもオーカワ(@okawa_compass)です。前回の受信編に続いて、今回は送信編です。Slackを使ってLINE Botからテキストを送信できます。前回の記事をご覧になっている前提で記事を書いていますので、見てない方はご覧ください。

今回はテキストを送信(Push API)する関係で、Messaging APIのアカウントに制限があります。私はDeveloper Trialのアカウントを使っています(フリーのアカウントだと出来ないです)。わからない方はこちらから。

自分が想像したよりも前回の反響は無かったですが、今回も頑張って書いていきたいと思います。

作成したコード

ここに直接コードを載せたかったのですが、気づいたら行数が300近かったので、全体のコードはGitHubで公開することにします。

設定

GAS側

今回はスプレッドシートを使用します。Googleドライブからスプレッドシートを作成して以下を入力します。

「ツール」→「スクリプトエディタ」からGASを起動し、GitHubで公開しているコードを貼り付けます。スプレッドシートとGASを連携するために、必ずスプレッドシートからGASを起動してください。

あとは前回と同じ設定をして頂ければ大丈夫です。

Slack側

Outgoing Webhooksの設定をします。この設定をする事によって、LINE Botを操作する事が出来ます。

  • Channel
    • 投稿を取得するチャンネル
      • どのチャンネルでも可能
  • Trigger Word(s)
    • Outgoing Webhooksのトリガーになる単語
      • どの単語でもほぼ可能(先頭に@をつけなければ)
  • URL(s)
    • POST送信先のurl
      • 今回はGASのウェブ アプリケーションURLを入力

私の設定例

Outgoing Webhooksの詳しい説明はこちらから

使い方

Slackを使ってLINE Botからユーザーにテキストを送信する方法です。

1. フォロー

Botからテキストを送信するには、相手が必要です。

Botがフォローされたら、Slackに友達追加の通知が行き、スプレッドシートに自動でLINEのアカウント情報が追加されます。

アカウントがスプレッドシートに追加されるタイミングは、フォローイベントだけです。

2. テキストの送信

Slackからテキストを送信します。

コマンドはOutgoing Webhooksの設定で変わります。


TriggerWord @相手の名前: 送信するテキスト

相手の名前はスプレッドシートに登録されているdisplayName(名前)を使います。

例えばTrigger Wordが :line: , 相手の名前がMasaya Okawaだった場合は


:line: @MasayaOkawa: 送信するテキスト

になります。

またテキストを送った相手はキャッシュしますので、続けて同じ相手に送信する場合は相手の名前を省略する事が可能です。

 

解説

基本的に前回の記事で紹介したプログラムを改造して今回のプログラムを作りました。前回のプログラムと比較して頂ければわかると思いますが、同じようなプログラムでも書き方が少し変わっています。書いていくうちに、行数が多くなってしまい、今までのような書き方だと作者である私ですらよくわからなくなっていました。なので今回からクラスを用いています(オブジェクト指向様様ですね)。

正直、GASもJavaScriptも本を読んで勉強した訳ではなく、完全にノリで書いてるので書き方があっているかわかりませんが、動いているのでご了承を(間違っていたら指摘していただけたらと)。

前回のプログラムへの追記

前回の記事で紹介しているプログラムの、フォローイベント, フォロー解除イベントはプログラムの追記をしています。他も少々変わっていますが、動作は基本的に変わっていません。

フォローイベント

フォローイベント時にスプレッドシートにプロフィール情報を追記します。

追記するのはIDとUser IDと名前です。IDは特別必要では無いですが今後の為に追記をしています。IDがあると並び替えをする時に便利だったりします(普通のExcelと一緒ですな)。


      case 'follow':
        profile = this.get_line_profile(this.line);
        this.sk.postSlackMessage('```follow```', profile);
        // set sheet
        this.sh.setvalue(this.last_row + 1, 1, this.last_row);
        this.sh.setvalue(this.last_row + 1, 2, this.line.source.userId);
        this.sh.setvalue(this.last_row + 1, 3, profile.displayName);
        break;

フォロー解除イベント

フォロー解除イベント時にスプレッドシートにあるプロフィール情報を削除します。人によって、この操作はいらないかもしれません。

行ごと削除しますので、なにかスプレッドシートに追記する予定がある方は注意が必要です。


      case 'unfollow':
        profile = this.get_line_profile(this.line);
        this.sk.postSlackMessage('```unfollow```', profile);
        // Deletes the row of the user ID.
        this.sh.delete_row(this.sh.find_row(2, this.line.source.userId));

GASが受け取るPOSTデータ

GASはdoPost関数でPOSTデータを受け取ります。doPost関数はGASに元からある関数です。

今回はLINE(Messaging API), Slack(Outgoing Webhooks)からPOSTデータを受け取りますが、POSTデータを受け取れる場所がdoPost関数だけなので、どこから来たPOSTデータかを識別する必要があります。doPost関数は全てを受け止める、M性感にいるオッサンのようなイメージ

LINEから送られるPOSTデータ例


{
  "events": [
      {
        "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
        "type": "message",
        "timestamp": 1462629479859,
        "source": {
             "type": "user",
             "userId": "U4af4980629..."
         },
         "message": {
             "id": "325708",
             "type": "text",
             "text": "Hello, world"
          }
      }
  ]
}

Slackから送られるPOSTデータ例


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:

コード

LINE, SlackのPOSTデータ例を見ていただけたらわかると思いますが、全然違います。今回は送られたデータのtextがあるか無いかで識別をしています。textはSlackから送られるPOSTデータにはありますが、LINEから送られるPOSTデータにはありません。


function doPost(e) {
  if (e.parameter.text != undefined) {
    // 今回紹介するプログラム
    var sl = new get_slack(e);
    sl.main();

  } else {
    // 前回紹介したプログラム
    var ln = new line(e);
    ln.main();
  }
}

SlackからのPostデータが来た時の処理

処理の流れはPOSTデータからテキストを取得して、それをMessagin APIのPush APIを用いてメッセージを送信するだけです。それだけなんです。


  this.main = function () {
    var text = this.get_text(this.slack);
    var userId;
    if (text[1] != undefined) { 
      userId = this.sh.getvalue(this.sh.find_row(3, text[1]), 2);
      PropertiesService.getScriptProperties().setProperty('userId', userId);
      text = text[2];
    }
    else {
      userId = PropertiesService.getScriptProperties().getProperty('userId');
      text = text[0];
    }
    this.ln.send_line(userId, text);
  };

テキスト処理

Slackから送られたテキストだと無駄な情報が多いので、処理をします。

  1. Trigger Wordがある場合、テキストからTrigger Wordを削除
  2. 正規表現を用いて送信する相手の名前と送信するメッセージを分割

  this.get_text = function (slack) {
    var text;
    if (slack.trigger_word != undefined) text = slack.text.replace(slack.trigger_word, '');
    else text = slack.text;
    text = text.split(/@([\s\S]*?):/);
    return text;
  };

User IDの取得

LINEのPush APIを使うためにはUser IDが必要です。User IDの取得はテキストに送信する相手の名前があるかないかで動作が変わります。

送信する相手がある場合は、スプレッドシートからUser IDを取得します。スプレッドシート側の処理は、for文を使って1番上から送信する相手と対応しているUser IDの検索をしています。もし、同姓同名の方がスプレッドシートに登録されている場合、先に登録された人が優先されます(同姓同名は対応していないので)。

送信する相手がない場合はスクリプトのプロパティにキャッシュしているUser IDを取得します。


      // テキストに送信する相手がある場合
      userId = this.sh.getvalue(this.sh.find_row(3, text[1]), 2);
      // テキストに送信する相手がない場合
      userId = PropertiesService.getScriptProperties().getProperty('userId');

グループ, トークルームの対応

ここまで読んだ方はわかると思いますが、今回作ったプログラムは、LINEのグループやトークルームには対応していません(面倒で書かなかった)。グループやトークルームの部屋名がMessaging APIで取得できれば対応したのですが。

しかし、裏技があって、手動で情報をスプレッドシートに追記すれば、コードを書き換えなくても出来るのです。一応裏技なのでこの辺で。

グループについての余談

私はグループでLINE Botを運用する気は全く無いので、そのようなコードを書いてませんが、”私だったらこう作る”というのをここに軽く書いときます。というのも、グループでガッツリ運用しそうな人が私のTwitterのフォロワーに数名いるので。こういう考えもあるよとだけ。私やさしい。みんなもふぉろーしてね

 

今回のような送信はSlackのチャンネルを分けた方がいいと思います。チャンネルを分ければ、いちいち送る相手(groupID)を指定しなくてもいいようなコードが書けるはずです(事前にコードに情報を書く必要はありますが)。SlackのOutgoing WebhooksのChannel設定もAny(どこでも作動する)にしとくのがbetterかなと。Outgoing WebhooksのPOSTデータにはテキストやTrigger Wordの他にどのチャンネルから送信されたかがわかるので、その情報を使って処理を分ければいいと思います。

それにともない、Slackの通知も受信するグループによって通知を送るチャンネルを分けた方がいいと思います。処理的にはgroupIDで分けるしかないと思うので、コードが汚くなったり、事前準備が大変なのがネックですね。


switch (this.slack.channel_name) {
  case 'random': 
    // randomチャンネルから送信されたら
    break;
  case 'general':
        // generalチャンネルから送信されたら
    break;
  default:
        // それ以外のチャンネルから送信されたら
    break;
}

感想

Slackを使ってLINE Botからテキストを送信できるのは割と便利ですね。作るのも個人的に簡単でしたし。前まではコマンドやスマホアプリを作ってテキストを送信していましたが、それらはもう昔の話ですね。

これでSlackはやっているけどLINEはやっていない上司に間接的にLINEをさせる事も出来ますね。しないけど。