会社の出勤退勤管理を少し楽にした話
日々のルーティン業務である出退勤記録を効率化する仕組みを作成しました。本記事では、Google Apps ScriptとDiscord Botを活用して、自動化を実現する手順をお話しします。
馴れ初め
私の職場では、以下のような手順で出退勤を記録しています:
-
出勤時
- Discordに「おはようございます、出社してます」と投稿
- スプレッドシートに出社時間を記入
-
退勤時
- Discordに「お先に失礼します、お疲れ様でした」と投稿
- スプレッドシートに退勤時間を記入
しかし、二度手間感があってめんどくさいと感じたので、これらを一元化し、自動化する仕組みを作りました。
作戦概要
以下の技術を活用して自動化を実現します:
-
Google Apps Script
- Googleスプレッドシートを操作するAPIを作成します。
-
Discord Bot
- Discordで特定のメッセージを受信したときに、Google Apps ScriptのAPIを呼び出します。
実装手順
1. Google Apps ScriptでスプレッドシートAPIを作成
ステップ1: スプレッドシートの準備
以下のレイアウトでスプレッドシート用意しました。
A列: No.
B列: 日付
C列: 曜日
D列: 出勤モード (“出勤” or “在宅”)
E列: 出勤時刻
F列: 退勤時刻
G列: 休憩時間
ステップ2: 新しいApps Scriptプロジェクトを作成
Google Apps Scriptを使って、出退勤時刻を受け取るお皿のようなものを作成します。
-
スプレッドシートのメニューから 拡張機能 > Apps Script を選択
-
別タブでコードエディタが開くので、デフォルトの Code.gs を以下のように置き換えます。
// POSTリクエストから「出勤(oha1/oha2)」「退勤(otu)」を判別し
// スプレッドシートに時刻を書き込むメイン関数
function doPost(e) {
try {
// リクエストのJSONを解析
const params = JSON.parse(e.postData.contents);
const action = params.action; // 'oha1', 'oha2', 'otu' のいずれか
// アクションごとに対応する関数を呼び出し
if (action === 'oha1' || action === 'oha2') {
// mode: '出勤' または '在宅'
const mode = action === 'oha1' ? '出勤' : '在宅';
recordCheckin(mode);
return ContentService
.createTextOutput(JSON.stringify({ status: 'success', message: `${mode}を記録しました。` }))
.setMimeType(ContentService.MimeType.JSON);
} else if (action === 'otu') {
recordCheckout();
return ContentService
.createTextOutput(JSON.stringify({ status: 'success', message: '退勤を記録しました。' }))
.setMimeType(ContentService.MimeType.JSON);
} else {
throw new Error('無効なアクションです: ' + action);
}
} catch (error) {
// エラー時はJSONで返す
return ContentService
.createTextOutput(JSON.stringify({ status: 'error', message: error.message }))
.setMimeType(ContentService.MimeType.JSON);
}
}
ステップ3: 出勤/退勤を記録する関数を作る
// 出勤時刻を記入する関数
function recordCheckin(mode) {
const sheet = getCurrentSheet();
if (!sheet) throw new Error('シートが見つかりません');
const todayStr = formatDate(new Date());
const lastRow = sheet.getLastRow();
for (let i = 2; i <= lastRow; i++) {
const cellDate = sheet.getRange(i, 2).getValue();
if (!(cellDate instanceof Date)) continue;
if (formatDate(cellDate) !== todayStr) continue;
// D列に mode、E列に時刻をセット
sheet.getRange(i, 4).setValue(mode);
sheet.getRange(i, 5).setValue(formatTime(new Date()));
return;
}
}
// 退勤時刻と休憩時間を記入する関数
function recordCheckout() {
const sheet = getCurrentSheet();
if (!sheet) throw new Error('シートが見つかりません');
const todayStr = formatDate(new Date());
const lastRow = sheet.getLastRow();
for (let i = 2; i <= lastRow; i++) {
const cellDate = sheet.getRange(i, 2).getValue();
if (!(cellDate instanceof Date)) continue;
if (formatDate(cellDate) !== todayStr) continue;
// F列に退勤時刻、G列に休憩時間(固定1:00)をセット
sheet.getRange(i, 6).setValue(formatTime(new Date()));
sheet.getRange(i, 7).setValue('1:00');
return;
}
}
ステップ4: 補助関数を追加
// 現在の集計シートを選ぶ
function getCurrentSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const today = new Date();
const year = today.getFullYear().toString().slice(-2); // 例: '24'
const month = today.getMonth() + 1; // 1~12
const day = today.getDate();
// 16日以降なら翌月、1日~15日なら今月シートを参照
const targetMonth = day > 15 ? month + 1 : month;
const sheetName = `${year}年${targetMonth}月`;
return ss.getSheetByName(sheetName);
}
// 日付を「M/d」形式の文字列に変換
function formatDate(date) {
return Utilities.formatDate(date, Session.getScriptTimeZone(), 'M/d');
}
// 時刻を「H:mm」形式の文字列に変換
function formatTime(date) {
return Utilities.formatDate(date, Session.getScriptTimeZone(), 'H:mm');
}
ステップ3〜5で以下のようなコードになるはずです
https://github.com/hitto-hub/DiscordWorkLogger/blob/main/code.gs
ステップ5: ウェブアプリとしてデプロイ
1.エディタ右上の デプロイ > 新しいデプロイをクリック
2.「種類」を ウェブアプリ に設定
- 説明:例)「勤怠記録API」
- 実行ユーザー:自分
- アクセス権限:**全員**、必要に応じて「自分のみ」または「組織内」に
3.「デプロイ」をクリック → 発行された URL をコピー
発行されたURLは大切にメモしておきましょう
2. Discord Botの作成
Discord Botを使い、特定のメッセージに反応して先ほど作成した関数を呼び出す仕組みを作ります。
手順
DiscordBotを動かすにはサーバーが必要です。
私は自前のサーバーで動かしていますが、無料のサーバーを借りてDiscordBotを動かすこともできる(未検証)
コード
https://github.com/hitto-hub/DiscordWorkLogger/blob/main/main.py
-
Botを作成
- Discordの開発者ポータルで新しいBotを作成します。
その際、Discord Bot のトークンをしっかりとメモしておきましょう
参考
- Discordの開発者ポータルで新しいBotを作成します。
-
Botコードの実装
メッセージをトリガーにGoogle Apps Script APIを呼び出すDiscordBotを実装します。
-
.env
ファイルを作成
.env.exampleを参考に適当に設定して下さい
```env
DISCORD_TOKEN=YOUR_DISCORD_BOT_TOKEN
TARGET_GUILD_ID=YOUR_GUILD_ID
TARGET_CHANNEL_ID=YOUR_CHANNEL_ID
```
DISCORD_TOKEN
しっかりとメモしておいたトークン
TARGET_GUILD_ID
TARGET_CHANNEL_ID(お疲れ様でしたと報告するチャンネル)
- ユーザー ID と GAS URL のマッピング
user_gas_mapping.json ファイルを作成し、以下のようにユーザー ID と GAS の Web App URL をマッピングします:
左側にDiscordのユーザーID, 右側に大切にメモしておいたgoogle apps scriptのURLを書いていく
{
"123456789012345678": "https://script.google.com/macros/s/abc1234567890/exec",
"876543210987654321": "https://script.google.com/macros/s/def0987654321/exec"
}
- 起動させる
DockerでDiscord Bot の起動
-
Docker イメージをビルド
docker compose build
-
Docker コンテナを起動
docker compose up -d
Discord Bot の起動
-
Python スクリプトを実行
python main.py
結果
実際に動かすと、以下のような流れで自動記録が行われます。
1.出勤時の挙動
-
Discord で
おはようございます、出社してます
と投稿 -
Bot が反応してリアクションで反応
-
当日該当行の D 列に 出勤、E 列に HH:mm(例: 9:05)が自動入力される
2.在宅出勤時の挙動
-
Discord で
おはようございます、始めます
と投稿 -
Bot が反応してリアクションで反応
-
当日該当行の D 列に 在宅、E 列に HH:mm が自動入力される
3.退勤時の挙動
-
Discord で お先に失礼します、お疲れ様でした と投稿
-
Bot が反応してリアクションで反応
-
当日該当行の F 列に HH:mm(例: 18:30)、G 列に休憩時間 1:00 が自動入力される
実際に仕組みを導入すると、以下のような効果を得られました:
- 手作業の削減で業務効率アップ
- 記録漏れの防止
- 幸せになれる
感想
この仕組みを作成してみて、一番感じたのは「思っていたよりも簡単に実現できた!」ということです。Google Apps ScriptやDiscord Botは過去に触ったことがあったので2時間くらいであまり苦戦せずに作成できました。
また、200行程度のコードで日々の業務を効率化できると考えると、プログラミングの力は偉大だなと恐怖すら覚えました。
最後に、
今回の取り組みで、普段の業務を少しだけ楽にすることができました。同じように感じている方はぜひ試してみてください!
詳細なコードはこちらのGitHubにアップしています:https://github.com/hitto-hub/DiscordWorkLogger
おまけ
この記事の存在意義あるか?
Discussion