🐷

Spreadsheet の情報をもとにSlack へ通知するBot をつくる

2022/12/06に公開

対象となる読者

  • Google WorkspaceとSlackを利用している人
  • Spreadsheetで管理している情報を定期的にチャットツールへ通知を出したい人
  • Google App Script(以降GAS)を利用して面倒くさい業務をスクリプトにおまかせしたい人
  • 企業の情報システム・コーポレートITなりたて、兼務でGASよく分からんって人

なにが実現できるか

Spreadsheetにある表の情報を取得し、対象ユーザにメンションをつけてSlackへ通知できるようになります。

実際の動き

  • Slackに投稿される文面
  • Macにおける通知

設定作業の流れ

  1. SlackのApp Directory からBotを追加する。(参考)
    追加する際、「From Scratch」で作成する。
  2. 以下の項目にて諸々設定する
    Basic Information > Display Information
    Install App > OAuth Tokens for Your Workspace
    Incoming Webhook > Webhook URLs for Your Workspace
    OAuth Permissions > Scopes (以下の画像参考)
  3. Slackの設定が完了したら、データソースとなるSpreadsheetにてスクリプトを作成する。
    Spreadsheet > 拡張機能 > Apps Script から追加可能

スクリプトの全体像

ソースコード
function main(){
  const {rolloverDate,serial,usersEmailAddress} = getInformation();
  const slackIdList = usersEmailAddress.map(getSlackId);
  let fieldValues = [];

  fieldValues = fields(rolloverDate,slackIdList, serial);
  if(fieldValues.length != 0){
    sendToSlack(fieldValues,"#XXX"); //XXXは任意のチャンネル名を入力
  }
}

//Slackに投稿する文面を設定する。
function fields(rolloverDate,slackIdList,serialList){
  const today = new Date();
  const criterionDate = new Date(today.getFullYear(),today.getMonth(),today.getDate()-5);
  let fields = [];

  for (let i = 0;i < slackIdList.length; i++){
    if(rolloverDate[i].getTime() <= criterionDate.getTime()){
      fields[i] = {
        "value"  :"value", //valueには任意の文字列を入力
        "short" :"true"
      };
    }
  }
  return fields
}

//Slackへメッセージを送信する
function sendToSlack(fields, channel) {
  const webhookUrl = "XXX" //XXはSlack Incoming WebhookのURL
  const data = {
    "attachments" : [{
      "channel" : channel,
      "pretext" :"pretext",
      "fallback" : "fallback",
      "fields": fields,
      "color": "warning",  // Slackメッセージの装飾
    }],
  };

  const payload = JSON.stringify(data);
  const options = {
    "method" : "POST",
    "contentType" : "application/json",
    "payload" : payload,
    "muteHttpExceptions": true,
  };
  const response = UrlFetchApp.fetch(webhookUrl, options);
  Logger.log(response)
}

function getSlackId(value){
  //APIリクエストのパラメータ設定
  const apiUrl = "https://slack.com/api/users.lookupByEmail";
  let payload = {
    "token" : "xoxb-XXX", //Slack APIのTokenを入力
    "email" : value
  };
  let options = {
    "method" : "GET",
    "contentType" : "application/x-www-form-urlencoded",
    "payload" : payload
  };
  
  //APIリクエスト
  let response = UrlFetchApp.fetch(apiUrl, options);
  
  //取得したJSONのデコード
  response = JSON.parse(response);
  
  return response.user.id
}

function getInformation(){
  //各種変数の定義
  const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('XXX'); //XXXは利用しているシートの名前
  const ssLastRow = ss.getLastRow();
  const ssLastColumn = ss.getLastColumn();
  let serialColumn = 0;
  let rolloverDateColumn = 0;
  let aliasColumn = 0;
  let serial = [];
  let rolloverDate = [];
  let usersEmailAddress = [];
  let usersAlias = [];


  //指定した名前の列を指定
  for (let i = 1; i <= ssLastColumn; i++){
    if (ss.getRange(1,i).getValue() == "XXX"){ //XXXは利用している表の列のラベルを指定
      serialColumn = i;
    }
    if (ss.getRange(1,i).getValue() == "XXX"){ //XXXは利用している表の列のラベルを指定
      aliasColumn = i;
    }
    if (ss.getRange(1,i).getValue() == "XXX"){ //XXXは利用している表の列のラベルを指定
      rolloverDateColumn = i;
    }
  }

  //Spreadsheetの情報を変数にぶっこむ
  if (ssLastRow > 1){
    usersAlias = ss.getRange(2,aliasColumn,ssLastRow-1).getValues(); //getRange(行, 列, 行数)
    usersEmailAddress = usersAlias.map(convert2Email);

    serial = ss.getRange(2,serialColumn,ssLastRow-1).getValues().flat(); //getRange(行, 列, 行数) 

    rolloverDate = ss.getRange(2,rolloverDateColumn,ssLastRow-1).getValues().flat(); //getRange(行, 列, 行数)
  }
  return {rolloverDate,serial,usersEmailAddress}
}

//エイリアス → ユーザのメールアドレス形式に変換
function convert2Email(value){
  return value + '@XXX.com' //利用しているドメインを入力
}

大まかな動き

  1. GASのトリガーで定期的に関数 main()を実行する
  2. Spreadsheet上のデータを取得する
  3. GASで取得したデータを加工し、ユーザのSlack IDを取得する
  4. 手順2-3で取得したデータを、Slackへの投稿用にデータを加工する
  5. 整形したデータ、指定したチャンネルを元に送信する
  6. 以上で完了

Botでメンションさせる方法について

Botの投稿でユーザやユーザグループへメンションする場合、IDを取得して埋め込む必要があります。(参考)
ユーザが動的に変更される場合は上述のスクリプトの通り、メールアドレスを元にSlack APIでIDを取得する方法が一番便利そうです。

スクリプト作った背景

※ 情シスとしての事情が含まれているので、どうでもいい方はすっ飛ばしてください。
企業内のPC管理において、様々な理由で新しい端末を渡しており、返却されるはずの古い端末を回収できていない場合に検知したいということが背景にあります。
どの端末が返却されていないか確認してチャットで返却してねとリマインドする・・・って人間がわざわざ手作業でやるのアホくさいので、スクリプトを作りました。

また、通知する条件として以下のようなものがあり、スクリプト内で処理しています。

  • Slackでメンションが飛ばせること
  • 新しい端末を渡してから少なくとも5日経過していること
  • 端末のシリアルを通知できること

注意事項

本記事を書いている人はポンコツ情報システムとして仕事しています。
開発はメイン業務ではないため、スクリプトの書き方が下手くそっていうのは大目に見てください。
(リーダブルコード読めって話)

参考記事

  1. GASからSlackメンションする際の表記とそのIDの取得方法 - note
  2. SlackのIncoming Webhooksを使い倒す - Qiita
  3. Google Apps Scriptのトリガーについて - TD SYNNEX
  4. Slack API を使って email から User ID を取得 - Qiita
  5. Slack API attachmentsチートシート - Qiita
  6. SlackのIncoming Webhookについて(2021年2月版)
  7. APIの設定でInstall App からWebhook URLを取得できる
  8. Slack Incoming WebHooks を作成したユーザを解除した時の影響 - Qiita
  9. Google Apps ScriptでgetYear()関数の日付がおかしくなる問題の対応法

Updates

  • 2022/12/06 公開
  • 2022/12/08 スクリプトを改良

Discussion