🤖

召喚されたら召喚者が参加していないTimesチャンネルを教えてくれるSlack botを作りました

に公開

こんにちは、TRUSTDOCKのよもぎたです。

前回前々回とつづいたSlack botシリーズの、たぶん最終形態です。
botの名前も「シン・人工無能」にしました。庵野監督の「シン・」○○は「シン・ゴジラ」が好きです。

アプローチ

概要

前回、前々回で

  • Timesチャンネルの一覧を取得する
  • Slackのメッセージに返信する
    ことが出来るようになっています。

今回は、上記に加えて次の2つが出来るようにしました。

  • botが参加しているチャンネル(=私のTimesチャンネル)で特定のメッセージが投稿され時にそれに返信する
  • 各Timesチャンネルにメッセージを投稿したユーザーが参加しているかどうかを判定する

実行結果

こんな感じになります。

参加していないTimesチャンネルがある場合
botの実行結果

すべてのTimesチャンネルに参加している場合
botの実行結果

実装

Slackアプリの準備

今回も過去私が投稿したこちらの記事を参考にしました。

アプリに付与した権限は、

になります。

いざGASで実装

doPost()

function doPost(e) {
  let params = JSON.parse(e.postData.getDataAsString());

  const app_user_id = 'U...';

  if (params.type == 'url_verification') {
    return ContentService.createTextOutput(params.challenge);
  }

  if (params.event && params.event.type == 'message' && params.event.user != app_user_id && !params.event.subtype) {
    handleEvent(params.event);
  }

  return ContentService.createTextOutput(JSON.stringify({ "challenge": params.challenge }));
}

メッセージの投稿者(params.event.user)がbot自身でないことの判定は試験的に実装していた時の名残です。最終的には不要になりました。(不要のはずです)
bot自身のユーザーIDは、botにメッセージを投稿させて取得しました。もっとスマートなやり方があったら教えてください。

handleEvent()

function handleEvent(event){

  if(event.text.includes('人工無能召喚')){
    send_reply(event);
  }

}

send_reply()

function send_reply(event){

  const slack_oauth_token = 'xoxb-deadbeef...';
  const slack_dest_channel_id = 'C...';

  const all_times_channels = get_all_times_channels(slack_oauth_token);
  const not_joined_times_channels  = get_not_joined_times_channels(slack_oauth_token, event.user, all_times_channels);

  let response_message = '<@' + event.user + '>\n\n人工無能見参!!\n\n';

  if(not_joined_times_channels.length > 0){
    response_message = response_message + get_user_display_name(slack_oauth_token, event.user) + 'さんが参加していないSlackチャンネルをお知らせします\n\n';

    not_joined_times_channels.forEach((channel) => 
    {
      response_message = response_message + get_user_display_name(slack_oauth_token, channel[1]) + 'さんの<#' + channel[0] + '>\n';
    }
    )

    response_message = response_message + '\nぜひ新しいTimesチャンネルに参加してコミュニケーションの輪を拡げましょう!';

  }else{
    response_message = response_message + 'すばらしい!!\n' + get_user_display_name(slack_oauth_token, event.user) + 'さんはいまある全てのTimesチャンネルに参加しています';
  }


  let slack_chat_post_message_api = 'https://slack.com/api/chat.postMessage';
  let headers = {'Authorization': 'Bearer ' + slack_oauth_token, 'Content-types': 'application/json; charset=UTF-8'};
  let payload = {'channel': slack_dest_channel_id, 'thread_ts': event.ts, 'text': response_message};
  let options = {'method': 'POST', 'headers': headers, 'payload': payload};

  UrlFetchApp.fetch(slack_chat_post_message_api, options);

}

get_all_times_channels()

function get_all_times_channels(slack_oauth_token){

  const slack_conversations_api_url = 'https://slack.com/api/conversations.list?exclude_archived=true&limit=1000';
  const headers = {'Authorization': 'Bearer ' + slack_oauth_token};
  const options = {'headers': headers};

  const channels = JSON.parse(UrlFetchApp.fetch(slack_conversations_api_url, options).toString()).channels;

  let times_channels = [];

  channels.forEach((channel) =>
  {
    if(channel.name.match(/^z-times-/)){
      times_channels.push([channel.id, channel.creator]);
    }
  }
  )

  return times_channels;

}

get_not_joined_times_channels()

function get_not_joined_times_channels(slack_oauth_token, slack_target_user_id, all_times_channels){

  const slack_conversations_memers_api = 'https://slack.com/api/conversations.members'
  const headers = {'Authorization': 'Bearer ' + slack_oauth_token};
  const options = {'headers': headers};

  let not_joined_times_channels = [];

  all_times_channels.forEach((channel) =>
  {
    let channel_id = channel[0];
    let channel_creator = channel[1];
    let req_api = slack_conversations_memers_api + '?channel=' + channel_id + '&limit=200';
    let response = JSON.parse(UrlFetchApp.fetch(req_api, options).getContentText())
    let members = response.members;

    if(!members.includes(slack_target_user_id)){
      not_joined_times_channels.push([channel_id, channel_creator]);
    }
  }
  )

  return not_joined_times_channels;
} 

get_user_display_name()

この関数はまた以前の記事から流用しています。

コードは以上です。

最後に

最初は何気ない一言から始まったこのシリーズも、当初は考えてもいなかったところまで発展しました。探求心とか好奇心って大切ですね。

最後までお読みいただきありがとうございました。

TRUSTDOCK テックブログ

Discussion