[비즈니스 개선] GAS를 사용한 슬랙의 질문 수 통계 자동화

결론


GAS를 사용하여 슬랙의 문제를 자동으로 통계하였다.
(실제로'GAS 기반 합계'와'스프레드시트 기반 함수 분석'을 했지만 스프레드시트 정보가 게재되지 않아 GAS에 대해서만 설명했다.)

배경.


업무 중에 슬랙을 통해 수강자의 질문에 대응한다.
이전에 이 문제에 대해 통계, 분석을 할 때 모든 슬랙의 질문 투고는 수동으로 전자 표에 복사하여 통계를 하였다.
많을 때는 일주일에 120개의 문제가 발생하기 때문에 슬랙에서 전자 표까지의 복사와 붙여넣기를 120회 반복해야 한다.
업무 효율화의 여지가 많다는 점을 고려해 GAS를 활용해 자동화했다.

지나간 일


GAS에 관해서는 전혀 접해본 적이 없는데, 다른 부서에서 GAS와 슬랙이 협력해 데이터를 얻었다고 들었는데, 이번 사례도 GAS를 사용하면 자동화할 수 있다는 점을 고려해 조사를 시작했다.
원래 GAS는 어디서부터 검색을 시작했는지 시간이 많이 걸렸지만, 구글 검색을 통해 거의 모든 지식을 포착할 수 있어 매번 체크하면서 진행됐다.
결국 우리는 하루에 한 번씩 시작하면 지정한 채널에 투고한 전날의 질문을 모두 받아서 전자 표로 옮길 때까지 자동으로 진행하는 프로그램을 만들었다.
만드는 데 8~10시간 정도 걸린다.
또한 프로그램을 만드는 과정에서 현재 수동적인 이유로 통계를 낼 수 없는 항목도 자동으로 얻을 수 있다는 것을 알게 되었다.
예를 들어 질문 기고에 기재된 수업의 URL을 집계함으로써 매달, 매주, 일요일, 매일, 시간대마다, 청강생마다 상당히 세분화된 데이터를 얻을 수 있다.

결실


기존의 수동 방법에서는 담당자가 매달 16시간 정도(문제가 많은 달에는 더 많은 시간이 걸린다)로 집계하기 때문에 그 시간을 줄일 수 있다.
또 지금까지는 담당자 집계 이후가 아니면 분석 결과를 확인할 수 없었으나 다음날 더 실시간으로 분석할 수 있었다.
또한 추가로 취득한 데이터에 따라 지금까지 통계·분석하지 못한 항목도 확보할 수 있어 교과계획과 서비스의 질 개선에 기반을 다졌다.

코드


아래에 실제 코드를 붙여넣습니다.
slack의 aptoken과 채널 정보 등의 값이 일부 바뀌었습니다.
GAS의 트리거 설정에서는 매일 오전 8시께mainFunction을 실행하도록 설정했다.
//-------------------------------------//
//-------------- 事前処理 --------------//
//-------------------------------------//
// slack用変数定義
const slack_token = '~~slack-apiのtoken~~';  // slack-apiで発行したtoken
const channel_id = '~~データ取得元のslackチャンネルID~~';  // チャンネルID(URLの末尾)
const slack_url = "https://slack.com/api/conversations.history";  // チャンネルから投稿を取得するための設定

// スプレッドシート用変数定義
const sheet_url = '~~集計結果を転記するシートURL~~'  // 質問数集計シートのURL
const sheet_name = "質問集計シート";                           // 転記するシート名
const spread_sheet = SpreadsheetApp.openByUrl(sheet_url);   // GoogleSpreadsheetのAPIを叩いて、質問数集計シートを開く
const sheet = spread_sheet.getSheetByName(sheet_name);      // 開いた質問集計シートのうち、「質問集計シート」という名前のシートを開く

// 日時用
const today = new Date();  // 実行した当日の日付を取得

//------------------------------------//
//-------------- 主処理 ---------------//
//------------------------------------//
const mainFunction = () => {
  const prev_date = getPrevDate();                       // ①前回日時を取得
  const slack_messages = getSlackHistories(prev_date);   // ②slackからデータを取得
  writeToSpreadsheet(slack_messages);                    // ③スプレッドシートに転記
};

//---------- ①前回日時を取得 ----------//
const getPrevDate = () => {
  prev_date = sheet.getRange('I1').getValue();  // I1セルの情報を取得
  return prev_date;
}

//------- ②slackからデータを取得 -------//
const getSlackHistories = (prev_date) => {
  // スプレッドシートの前回日付の00:00:00からのデータを取得
  const oldest = getUnixTime(prev_date);

  // 前日の23:59:59までのデータを取得
  const today_date = today.getDate();
  const yesterday = new Date();
  yesterday.setDate(today_date - 1);
  yesterday.setHours(23);
  yesterday.setMinutes(59);
  yesterday.setSeconds(59);
  const latest = getUnixTime(yesterday);

  // データ取得用の設定
  const payload = {
    "token": slack_token,   //Slack AppのToken
    "channel": channel_id,  //ChannelのID
    "oldest": oldest,       //この日時から
    "latest": latest,       //この日時まで
    "inclusive": true,      //oldestとlatestを含めるか true, false
    "count": 1000           //取得する件数 1〜1000(とりあえず1000件にしておけばこれ以上くることはないだろうという想定)
  };

  const options = {
    'method': 'post',
    'payload': payload
  };
  
  // 投稿データを取得
  const response = UrlFetchApp.fetch(slack_url, options);   // slackのAPIを叩いて投稿データを取得
  const data = JSON.parse(response);                        // JSON形式に変換
  const messages = data['messages'];                        // 投稿データのみに絞る
  return messages;
}

// Unix時間に変換
const getUnixTime = (dateTime) => {
  const date = new Date(dateTime);
  const milsec = date.getTime();
  const sec = milsec / 1000;
  const time = sec.toString();
  return time;
}

//------- ③スプレッドシートへ転記 -------//
const writeToSpreadsheet = (slack_messages) => {
  const messages_number = slack_messages.length;   // 要素の数を取得(要素数分ループ処理をする)
  let last_row = sheet.getRange(1, 1).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();    // 現在埋まっているセルの最終行

  // 古い順になるようにソート 
  const sorted_slack_messages = slack_messages.sort(function(a, b) {
    if (a.ts < b.ts) {
        return -1;
    } else {
        return 1;
    }
  });

  // 1レコードずつ転記
  let array_data = '';    // ループ時に使用する変数を先に定義
  for (let i = 0; i < messages_number; i++) {
    array_data = sorted_slack_messages[i];

    // 質問以外は除外する
    let topic = array_data['text'].search('チャンネルのトピックを設定しました');  // チャンネルのトピックを設定した場合は除外
    let question = array_data['text'].search('■カリキュラムURL')             // 「■カリキュラムURL」という文字列がある場合は質問だと判断 
    if (topic !== -1 || question === -1){
      continue;
    };

    // UnixTimeから日時に変換
    let date_time = getDateTime(array_data['ts']);
    let date = date_time.slice(0, 10);
    let time = date_time.slice(11, 19);

    // 質問内容からカリキュラムだけを抜き出す
    let curriculum = getCurriculumNumber(array_data['text']);

    // 転記するセルを取得
    last_row += 1;
    let user_cell = 'A' + `${last_row}`;
    let date_cell = 'B' + `${last_row}`;
    let time_cell = 'C' + `${last_row}`;
    let text_cell = 'D' + `${last_row}`;
    let curriculum_cell = 'E' + `${last_row}`;

    // セルに転記
    sheet.getRange(user_cell).setValue(array_data['user']);
    sheet.getRange(date_cell).setValue(date);
    sheet.getRange(time_cell).setValue(time);
    sheet.getRange(text_cell).setValue(array_data['text']);
    sheet.getRange(curriculum_cell).setValue(curriculum);
  }

  // 最終更新日付を更新
  const today_year = today.getFullYear();
  const today_month = today.getMonth() + 1;
  const today_date = today.getDate();
  const write_date = `${today_year}` + `/` + `${today_month}` + `/` + `${today_date}`;
  sheet.getRange('I1').setValue(write_date);
};

// 標準時間に変換
const getDateTime = (unixtime) => {
  const date = new Date(unixtime * 1000);
  return Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd/HH:mm:ss');
};

// カリキュラムの末尾4桁を取得
const getCurriculumNumber = (message) => {
  let curriculum_num = '';
  let curriculum = message.match(/curriculums...../);   // 正規表現でカリキュラムのURLを抽出

  if (curriculum != null) {
    curriculum_num = curriculum[0].slice(12);    // 末尾4桁を取得(12文字目以降を取得)
  };

  return curriculum_num;
};

좋은 웹페이지 즐겨찾기