【GAS】#5 LINE(リッチメニュー)とスプレッドシートを利用し、勤怠記録を行う

1. 概要

Line to Spreadsheet

LINEのリッチメニューのボタン(出勤・退勤)をタップするとGASに通知される

  • 指定のスプレッドシートの月毎のシートにデータが記録される
    • 日付
    • 名前
    • 出勤時間
    • 退勤時間
  • LINEのグループチャットにも通知される
    • 「田中」さんが「出勤」しました。

本記事では「API連携」による通知について解説します。

2. LINE Messaging APIの準備

2-1. 「LINE Developers」にアクセス

  • 「ログイン」をクリック

2-2 LINEアカウントでログイン

2-3. 「LINE Developers Account」を作成

2-4. 「Provider」を作成

2-5. 「Channel」を作成

2-6. 「Channel」

2-7. 「Messaging API」タブにて「Webhook URL」を登録(↓3で発行されるWeb app URL)

2-8. 「QR code」より友達追加

2-9. 「グループ・複数人チャットへの参加を許可する」へ変更

3. GASをDeployし、Web app URLを取得

3-1. デプロイ

3-2. デプロイタイプを選択

3-3. デプロイ設定を行う

3-4. 「Web app URL」を取得する

4. リッチメニューの作成

リッチメニューの管理

4-1. 対象アカウントを選択

4-2. リッチメニューを作成

4-3. テンプレートを選択

4-4. コンテンツを設定

4-5. 公開

5. サンプルコード

5-1. ファイル構成(GitHubで管理

  • notification/slack/attendance_record_from_line.gs
    • メインコード
  • notification/slack/gas_properties.gsheet
    • 「WebHook URL」を含め、コード内に書かない方が良いデータ(ID、PASSWORD、KEY等)をプロパティとして保存

5-2. スプレッドシートIDの取得

5-3. GAS Editorの開け方

※「+新規」をクリック

5-4. コード & 解説

※複数のプログラムよりプロパティファイルを共有する為、敢えてスタンドアロン型(*7-2)を採用する

// プロパティ情報が記載されているスプレッドシートのID
const bookId = '1j2z-S●●●●●●●●●●●●●●●●●●●●●●●●●●●●cQk';

// LINEからのイベント(タップ)をキャッチ
const doPost = (e) => {
  recordAttendance(e);
}

// Mainメソッド
// LINEボットより出勤・退勤ボタンがタップされた場合
// ①指定のスプレッドシートに出・退勤時間を記録する
// ②LINEの勤怠記録用グループに通知する
const recordAttendance = (e) => {
  // アクセストークン
  const accessToken = getValueOfProperty('D17');

  // 出勤・退勤ボタンをタップしたユーザーを取得
  const userId = JSON.parse(e.postData.contents).events[0].source.userId;
  const userName = getUserName(accessToken, userId);
  // 出勤なのか退勤なのか?
  const msg = JSON.parse(e.postData.contents).events[0].message.text;

  // 今月の勤怠記録用シート(月毎)
  const sheetName = Utilities.formatDate(new Date(), 'JST', 'YYYY-MM');
  const sheet = getTargetSheet(sheetName);

  const date = Utilities.formatDate(new Date(), 'JST', 'YYYY-MM-dd');
  const time = Utilities.formatDate(new Date(), 'JST', 'HH:mm:ss');
  // 特に退勤時の記録の為に、行番号を取得
  const rowNumber = findRowNumber(sheet, date, userName);

  if (rowNumber > 0) {
    // 退勤時間を記録
    let colIdx = 4;
    if (msg == '出勤') {
      colIdx = 3;
    }
    sheet.getRange(rowNumber, colIdx).setValue(time);
  } else {
    // 出勤時間
    let msgs = [date, userName, time, null];
    if (msg == '退勤') {
      // 退勤時間
      msgs = [date, userName, null, time];
    }
    sheet.appendRow(msgs);
  }
  if (msg == '出勤' || msg == '退勤') {
    // LINEの勤怠記録用のグループに共有用のメッセージを送信(例:「田中」さんが「出勤」しました。)
    const groupId = getValueOfProperty('E17');
    sendToLine(accessToken, groupId, `「${userName}」さんが「${msg}」しました。`);
  }
}

// 勤怠記録用スプレッドシート
const getTargetSheet = (sheetName) => {
  const spreadsheetId = getValueOfProperty('F17');
  const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  return createSheetIfNotExist(spreadsheet, sheetName);
}

// 対象シートが存在するかチェックし、存在しない場合は新規で作成する
const createSheetIfNotExist = (spreadsheet, sheetName) => {
  let sheet = spreadsheet.getSheetByName(sheetName);
  if (sheet != null) {
    return sheet;
  }
  // 新規で作成したシートを先頭に移動
  sheet =  spreadsheet.insertSheet(sheetName).activate();
  spreadsheet.moveActiveSheet(1);
  // シートにタイトルを記載
  const title = ['日付', '名前', '出勤時間', '退勤時間', '休憩時間', '稼働時間'];
  sheet.getRange(1, 1, 1, title.length).setValues([title]);
  return sheet;
}

// 日付やユーザー名で検索し、行番号を返還
const findRowNumber = (sheet, date, userName) => {
  const data = sheet.getRange('A:B').getValues();
  const rowNumber = data.findIndex(
    row => {
      return (Utilities.formatDate(new Date(row[0]), 'JST', 'YYYY-MM-dd') == date && row[1] == userName);
    }
  );
  return (rowNumber + 1);
}

// ユーザー名の取得
const getUserName = (accessToken, userId) => {
  const profileUrl = getValueOfProperty('G17');
  const targetUrl = profileUrl + userId;

  const headers = {
    'Content-type': 'application/json; charset=UTF-8',
    'Authorization': 'Bearer ' + accessToken,
  };

  const userProfile = UrlFetchApp.fetch(targetUrl, { 'headers': headers, },);
  return JSON.parse(userProfile).displayName;
}

// 指定のスプレッドシートに記載されているプロパティの値を取得
const getValueOfProperty = (cell) => {
  const sheet = SpreadsheetApp.openById(bookId).getSheetByName('Properties');
  return sheet.getRange(cell).getValue();
}

// メッセージをLineへ通知
const sendToLine = (accessToken, to, msg) => {
  // Message Push API Url
  const targetUrl = getValueOfProperty('C17');

  const headers = {
    'Content-type': 'application/json; charset=UTF-8',
    'Authorization': 'Bearer ' + accessToken,
  };

  const param = {
    'to': to,
    'messages': [
      {
        'type': 'text',
        'text': msg,
      },
    ],
  };

  const payload = JSON.stringify(param);

  const options = {
    'method': 'post',
    'headers': headers,
    'payload': payload,
  };

  UrlFetchApp.fetch(targetUrl, options);
}

6. 実行結果例

6-1. 出・退勤bot

6-2. 勤怠記録シート

  • 休憩時間
    • 手動
  • 稼働時間
    • 数式

6-3. グループチャットへの通知

7. 参考

7-1. GAS(Google Apps Script)とは

7-2. 2種類の方式

7-3. Web APIとは

7-4. 「UrlFetchApp.fetch()」について

関連記事

  1. 【GAS】#7 GoogleForms入力からスプレッドシートへ記載と…

  2. 【GAS】#1 Gmailに届いた新着メールをSlackに通知する

  3. 【GAS】#8 TwitterのPublic Metricsデータを取…

  4. 【GAS】#2 Google Calendarに登録されている予定をS…

  5. 【GAS】#4 OCRにて画像からテキストを起こし、Lineへ通知する…

  6. 【GAS】#11 Twitterの話題を検索し、スプレッドシートに記載…