1. 概要

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)とは
- https://workspace.google.co.jp/intl/ja/products/apps-script/
- https://developers.google.com/apps-script?hl=ja
7-2. 2種類の方式
- コンテナバインド型(スプレッドシートやフォームに紐づくタイプ)
- スタンドアロン型(独立タイプ)
7-3. Web APIとは
7-4. 「UrlFetchApp.fetch()」について
投稿者プロフィール

-
開発好きなシステムエンジニアです。
卓球にハマってます。
最新の投稿
【Next.js】2025年3月8日【NextJS】Cropping a portion of an image with React Cropper
【Next.js】2025年2月9日【NextJS】View and Download PDF
【AWS】2025年2月1日【AWS】Github ActionsやAWS SAMを使ってAWS S3・CloudFrontにウェブコンテンツをデプロイし、サブドメインにアクセスできるようにする
【AWS】2025年1月25日【AWS】Deploy Serverless NextJS app with AWS Lambda Web Adapter using AWS SAM