9
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ZOZOAdvent Calendar 2022

Day 16

GASでSlackのユーザーグループのメンバーを自動更新する

Last updated at Posted at 2022-12-15

はじめに

チーム内でシステムのアラート当番を回しており、何かしらのエラーを検知したら当番が対応するという運用をしています。
エラーの通知のメンション先はSlackの当番用のユーザーグループなので、当番が変わるタイミングで手動で更新していたのですが、地味に面倒でした。
そこでGASを使ってスプレッドシートで作成した当番表に従ってユーザーグループ定期的に自動更新するようにしました。

概要

Google Spreadsheet
以下のような当番表のシートを作成する。
image.png

GAS

  • 毎日決まった時間に動くようにトリガーを設定して定期的に実行する。
  • 現在日とスプレッドシートの当番開始日が一致するレコードがあったら
    その担当者のユーザIDを取得し、ユーザーグループを更新する。
  • 更新したらSlack通知でお知らせする。

使用するSlack API

Slack APIのusergroups.users.updateでユーザーグループ所属ユーザーの登録ができます。
このAPIにはユーザーグループのIDとユーザーのIDが必要であるため、
それぞれusergroups.listusers.lookupByEmailでIDを検索します。

これらのAPIは、SlackのアプリのTokenを使って実行します。

Slackの準備

Slack apiのYour AppsのページからSlackアプリを作成します。
各apiのドキュメントに必要な権限が記載されているので、それぞれ必要な権限を付与します。
今回の場合は以下になります。

  • usergroups:write
  • usergroups:read
  • users:read.email

作成したらWorkspaceにインストールします。
後で使うため、OAuth Access Token をCopyします。

スクリプト

main.gs
const TOKEN = 'TOKEN';
const SLACK_API_URL = "https://slack.com/api/";
const API_USERGROUPS_LIST = SLACK_API_URL + "usergroups.list";
const API_USERS_LOOKUPBYEMAIL = SLACK_API_URL + "users.lookupByEmail";
const API_USERGROUPS_USERS_UPDATE = SLACK_API_URL + "usergroups.users.update";
const USER_GROUP_NAME = "slackのユーザーグループ名";
const SPREADSHEET = SpreadsheetApp.getActiveSpreadsheet();
const SHEET_USERS = SPREADSHEET.getSheetByName('当番表'); // ここでは「当番表」というシート名から取得する

/** ここはコードでベタ書きしたが、別シートにまとめてそこから取得しても良いかも */
const ADDRESS_LIST = {
  "担当者名1":"member1@hogehoge.com",
  "担当者名2":"member2@hogehoge.com",
  "担当者名3":"member3@hogehoge.com",
  "担当者名4":"member4@hogehoge.com"
}

/** UserGroupIDを求める */
function getUserGroupID() {
  Logger.log("START getUserGroupID");
  let usergroup_id = '';

  // GroupIDがわかっている場合はこのまま終了
  if (usergroup_id != ""){
    return usergroup_id
  }
  // 全UserGroupを取得して、そこから該当のものを探す
  let params = {'method': 'post', 'payload': {'token': TOKEN}};
  let resjson = callApi(API_USERGROUPS_LIST, params);
  if (resjson == {}) {return "";}
  let usergroups = resjson.usergroups;

  let hit_usergroup = usergroups.find((v) => v.handle == USER_GROUP_NAME);
  if (hit_usergroup === undefined){
    throw new Error(":" + USER_GROUP_NAME + "は見つかりませんでした。登録済みか確認してください");
  }
  Logger.log(hit_usergroup);

  // 取得結果
  Logger.log('UserGroupID:' + hit_usergroup['id']);

  return hit_usergroup['id'];
}

/** カンマ区切りのUserIDリストを返す */
function getUserIDs(targerRow){
  Logger.log("START getUserIDs");
  let error_flg = false;
  let user_id_list = [];
  let name_1 = SHEET_USERS.getRange("C"+targerRow).getValues();
  let name_2 = SHEET_USERS.getRange("D"+targerRow).getValues();
  let email_1 = ADDRESS_LIST[name_1];
  let email_2 = ADDRESS_LIST[name_2];
  let emailList = [email_1, email_2];
  
  let user_id = '';
  let requests = [];
  let responses =[];
  let response = '';
  const options = {
	"headers": { 'Authorization': 'Bearer ' + TOKEN }
  };

  for(let i = 0; i < 2; i++){
    requests=API_USERS_LOOKUPBYEMAIL + "?&email=" +  emailList[i];
    
    responses = UrlFetchApp.fetch(requests,options);
    response = JSON.parse(responses.getContentText("UTF-8"));
    user_id =  response['user']['id'];
    user_id_list.push(user_id);
  }

  Logger.log('user_id_list:' + user_id_list);

  return user_id_list.join(',');
}

/** ユーザーグループの更新 */
function updateUserGroup(usergroup_id, user_id_list) {
  Logger.log("START updateUserGroup");

  let payload = {
    'token': TOKEN,
    'usergroup': usergroup_id,
    'users': user_id_list,
  }

  let params = {'method': 'post', 'contentType': 'application/x-www-form-urlencoded', 'payload': payload};
  let response = callApi(API_USERGROUPS_USERS_UPDATE, params);
  Logger.log(response);
}

/** SlackAPIの呼び出し */
function callApi(url, params){
  let response = UrlFetchApp.fetch(url, params);
  let resjson = JSON.parse(response);

  if (resjson.ok != true) {
    Logger.log('エラー:' + resjson['error']);
    throw new Error(resjson['error']);
  }
  return resjson;
}

function findRow(sheet,val,col){
  const dat = sheet.getDataRange().getValues(); //受け取ったシートのデータを二次元配列に取得
  for(let i=1;i<dat.length;i++){
    Logger.log('value:'+dat[i][col-1]);
    if(dat[i][col-1] === val){
      return i+1;
    }
  }
  return 0;
}

/** メイン処理 */
function main(){
  Logger.log("START");

  const today = new Date();
  todayFormatted = Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy-MM-dd');
  Logger.log('today:' + todayFormatted);

  const targerRow = findRow(SPREADSHEET,todayFormatted,1);

  if (targerRow == 0) {
    Logger.log('not exists target');
    return;
  }

  Logger.log('targerRow:'+targerRow);

  // UserGroupIDを求める
  const usergroup_id = getUserGroupID();
  // 各行のUserIDを求める
  const user_id_list = getUserIDs(targerRow);
  // Update実施
  updateUserGroup(usergroup_id, user_id_list);
  // slack通知
  sendNotice();
  Logger.log("END");
}

sendNotice.gs
function sendNotice() {
  const webhookURL = 'https://hooks.slack.com/services/~';

  const data = {
    'username' : '当番チームのメンバーを変更してくれるくん',
    'channel' : '#チャンネル名',
    'icon_emoji': ':smile:',
    'attachments': [{
      'color': '#008000',
      'text' : '当番交代しました!',
    }],
  };

  const payload = JSON.stringify(data);
  const options = {
    'method' : 'POST',
    'contentType' : 'application/json',
    'payload' : payload
  };

  UrlFetchApp.fetch(webhookURL, options);
}

トリガー設定

毎日決まった時間に動くようにGASの画面からトリガーを設定する。

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?