つれづれなるままに日々の色々なことを綴ります

【GAS】Slackの特定のChで発言に特定のスタンプがついたときに、その発言をスプレッドシートに書き出す

GASの勉強中です。自分用に学んだことをメモする用の記事です。 とりあえず動けばいいの精神なのでコードのきれいさには目を瞑ってもらえると…。

自分用なので、そのままコピペしても動かないよ、多分。

やったこと

特定のChで特定のスタンプが付いたときに、その発言をスプレッドシートに書き出す

コード全文

function doPost(e) {
  // Event API Verification 時のコード
  try {
    const json = JSON.parse(e.postData.getDataAsString());

    if (json.type == "url_verification") {
      return ContentService.createTextOutput(json.challenge);        
    }
  }
  catch (ex) {
    Logger.log(ex);
  } 

    const json = JSON.parse(e.postData.getDataAsString());
    
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = ss.getSheetByName("シート1");
    sheet.getRange("F2").setValue(json);

    
    // 特定のチャンネルIDと絵文字
    const targetChannel = "チャンネルID"; // 対象のチャンネルIDを設定
    const targetReaction = "balance"; // 対象の絵文字を設定

    sheet.getRange("F3").setValue(json.event.type);

    // targetChannelとtargetReactionが一致する場合にのみ、getMessage関数を呼び出す
    if (json.event.type === 'reaction_added'){
      const channel = json.event.item.channel; // チャンネルIDを格納
      const ts = json.event.item.ts; // タイムスタンプを格納
      const reaction = json.event.reaction; // リアクションの絵文字を格納
        if (channel === targetChannel && reaction === targetReaction){
          getMessage(ts, channel);
        }
    }
  
    if ( json.event.type === 'message') {
    sheet.getRange("F4").setValue(json);
    handleEvent(json.event);
    } 

    sheet.getRange("F7").setValue("処理終了");
  }




function getMessage(ts, channel) {
  const url = "https://slack.com/api/conversations.replies";
  const slack_app_token = "xoxb-HOGEHOGE"; // トークンを設定
  const limit = 10;
  const options = {
    "method": "get",
    "headers": {
      "Authorization": `Bearer ${slack_app_token}`
    },
    "payload": { 
      "channel": channel,
      "ts": ts
    }
  };
  

  const response = UrlFetchApp.fetch(url, options);
  const json = JSON.parse(response.getContentText());
  const text = json.messages[0].text;
  const baseUrl = "https://HOGEHOGE.slack.com/archives/";
  const messageLink = `${baseUrl}${channel}/p${ts.replace('.', '')}`;
  const date = new Date();
  const channelname = "#CHのお名前"
  
  
  SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().appendRow([date, text, messageLink, channelname]);
}

分解して詳しく見ていく

それでは下記でコードを分解して考えてみましょうね。

Event SubscriptionsとdoPost関数での疎通確認

  // Event API Verification 時のコード
  try {
    const json = JSON.parse(e.postData.getDataAsString());

    if (json.type == "url_verification") {
      return ContentService.createTextOutput(json.challenge);        
    }
  }
  catch (ex) {
    Logger.log(ex);
  } 
  • GAS上でデプロイすると、WebアプリURLが発行されます。それをSlack apiのEvent SubscriptionsのRequest URLに登録する必要があります。登録する際にSlackから検証リクエストが送信されます。
  • 新しくデプロイするとURLが変わってしまうので登録のし直しが必要。
  • ただし、デプロイの管理を選ぶことでURLを変えずにデプロイすることが可能です。
  • 上記の検証リクエストやイベントが起こったときは上記で登録したURLにリクエストが送られます。
  • doPostはWebアプリにPOSTリクエストが送られたときに実行される関数。リクエストは下記が含まれた形で送られてくる。
{
"token": "example-verification-token",
"challenge": "random-string-challenge",
"type": "url_verification"
}
  • if (json.type == "url_verification")で、受け取った上記のJSONのtypeがurl_verificationだったらjson.challengeの内容を返すことで検証が完了する。

reactionをSlack上で付けたよというリクエストをJSON.parseで解析する。

const json = JSON.parse(e.postData.getDataAsString());
        
    // 特定のチャンネルIDと絵文字
    const targetChannel = "チャンネルID"; // 対象のチャンネルIDを設定
    const targetReaction = "balance"; // 対象の絵文字を設定

    // targetChannelとtargetReactionが一致する場合にのみ、getMessage関数を呼び出す
    if (json.event.type === 'reaction_added'){
      const channel = json.event.item.channel; // チャンネルIDを格納
      const ts = json.event.item.ts; // タイムスタンプを格納
      const reaction = json.event.reaction; // リアクションの絵文字を格納
        if (channel === targetChannel && reaction === targetReaction){
          getMessage(ts, channel);
        }
    }
  • 届いたリクエストの内容はe.postData.getDataAsString()にJSON.parse()をかけることでObject形式で得ることができる。
  • reaction_addedをゲッチュするため、忘れずにSlack appのSubscribe to bot eventsでイベントを追加しておきます。
  • 取り出したデータをjsonで保持し、そこから更にChannleやts、reactionで分解して情報を保持します。
  • targetChannelとtargetReactionが一致する場合にのみ、getMessage関数を呼び出します。
  • json.event.item.channelは下記画像の赤丸を取って来いってことですね。
  • 詳しくは公式のドキュメントを見てください

reaction_addedで送られてくるイベントの例

api.slack.com

getMessage関数でmessage内容を取得する

 const url = "https://slack.com/api/conversations.replies";
  const slack_app_token = "xoxb-HOGEHOGE"; // トークンを設定
  const limit = 10;
  const options = {
    "method": "get",
    "headers": {
      "Authorization": `Bearer ${slack_app_token}`
    },
    "payload": { 
      "channel": channel,
      "ts": ts
    }
  };


  const response = UrlFetchApp.fetch(url, options);
  • 受け取ったイベントデータのChannelIDとtsを使用してSlackAPIのconversations.repliesにリクエストを送信し、メッセージ内容を取得します。
  • conversations.repliesを使用するにはtoken,channel,tsが必要です。そのため、PayloadにChannleとtsを持たせてあげます。
  • UrlFetchAppは外部のAPIやサイトにHTTPリクエストを送るためのGASのメソッドです。

api.slack.com

さらにJSON.parseで解体。スプレッドシートに書き出す。

const json = JSON.parse(response.getContentText());
  const text = json.messages[0].text;
  const baseUrl = "https://HOGEHOGE.slack.com/archives/";
  const messageLink = `${baseUrl}${channel}/p${ts.replace('.', '')}`;
  const date = new Date();
  const channelname = "#CHのお名前"
  
  
  SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().appendRow([date, text, messageLink, channelname]);
}
  • const responseで帰ってきたものをJSON.parseで解体します。
  • Slackのメッセージのリンクは"https://HOGEHOGE.slack.com/archives/"というベースとなるURLにチャンネルIDとタイムスタンプが合体したものになります。(HOGEHOGEにはワークスペースのお名前がはいります。)
  • 最後にappendRowでシート最終行へ書き出しています。

まとめ

動かないなと思ったら、こまめにJSONがどこまで来てるかなど吐き出すのが大事かなと思いました。