
【非エンジニアのためのClaude/ClaudeCodeシリーズ】日報をAIに書かせてみた〜Google Apps Script(GAS)×Googleカレンダー×Claude×Slackで自動化した話〜
はじめに
みなさん、日報書いてますか?
毎日19時に「さて日報を…」と思いながら、今日の予定を頭の中で振り返って、それをSlackにポチポチ打ち込む。そのルーティン、地味にしんどくないですか?
私はそう感じていました。
というわけで、Googleカレンダーの予定をClaudeに読み込ませて、日報を自動生成してSlackに投稿する仕組みを作ってみました。エンジニアじゃない私でも、なんとか動かせたのでその記録を残しておきます。
なぜGASを使うことになったのか
最初は「Claudeに日報を書いてもらえばいいか」と軽く考えていました。
毎日Claudeを開いて「今日の日報を書いて」と頼む、それだけのつもりでした。でもすぐに気づきます。Claudeは自分が立ち上げておかないと動いてくれない、当たり前ですが。
「自動化したい」のに「毎日Claudeを開く」では、結局手動です。日報を書く手間が、Claudeを開く手間に変わっただけ。それじゃ意味がない。
そこで調べた結果たどり着いたのが、Google Apps Script(GAS)をスケジューラーとして使うという方法です。GASはGoogleのサーバー上で動くので、PCの電源を切っていても、ブラウザを閉じていても、時間になれば自動で処理を実行してくれます。
「Claudeへの指示をGASに組み込んで、GASが毎日19時に動かす」という構造にすれば、完全自動化が実現できる。そういう発想で作ったのが今回のシステムです。
作ったものの全体像
毎日19時に、こんなことが自動で起きます。
- Googleカレンダーから当日の予定を取得
- Claudeが予定をもとに日報文章を生成
- 指定のSlackチャンネルのスレッドへ自動投稿
PCの電源を切っていても動きます。土日は自動スキップ。最高です。
実際の出力イメージ
セットアップの前に、まず「何ができるか」を見てもらった方がモチベーションが上がると思うので先に載せます。
こんな日報がSlackに自動投稿されます
山田 太郎の日報|2026年4月6日(月)
■ サマリー
本日は新規顧客との商談2件と、社内の週次MTGに参加しました。
午後は提案資料の修正に集中できた一日でした。
■ 業務内容
- 10:00〜11:00:株式会社○○様 初回商談(オンライン)
- 13:00〜14:00:週次営業MTG
- 14:00〜16:00:株式会社△△様 提案資料修正・送付
- 16:00〜17:00:株式会社□□様 フォローアップ商談
■ 案件対応
- 株式会社○○様:初回ヒアリング完了。先方のニーズは〜。次回は詳細提案を予定。
- 株式会社△△様:提案資料をv2に更新して送付済み。来週中に返答予定。
■ 所感
商談が続いた一日でしたが、どちらも前向きなリアクションをいただけました。
明日は資料作成に集中したいと思います。
カレンダーに登録された予定の情報だけから、ここまで書いてくれます。Googleカレンダーの予定説明欄にGoogle Docsのリンクを貼っておくと、議事録の内容まで反映してくれます。
必要なもの(全体像)
| 必要なもの | 取得先 | 備考 |
|---|---|---|
| Claude APIキー | console.anthropic.com | 管理者への連絡が必要な場合あり |
| Slack User Token(xoxp-) | api.slack.com/apps | 管理者承認が必要 |
| Googleアカウント | お手持ちのもの | カレンダーと同じアカウント |
| SlackチャンネルID | Slackのチャンネル詳細から | Cから始まる英数字 |
| スレッド検索キーワード | 週次スレッドの親メッセージから | 月曜に投稿される親メッセージ内の文字列 |
所要時間の目安は約1時間(管理者承認の待ち時間を除く)です。
セットアップ手順
STEP 1|Claude APIキーを取得する
console.anthropic.comにアクセスしてサインアップ
Anthropicのコンソールにアクセスし、アカウントを作成します。Googleアカウントでもサインアップできます。
-
左メニューの「API Keys」→「Create Key」をクリック
-
名前を入力してキーを発行
名前は何でもOKです(例:daily-report)。発行されたキーはsk-ant-から始まる文字列です。必ずこの画面でコピーしておいてください。画面を閉じると二度と表示されません。
コスト感:日報1回の生成コストは約2〜5円(Claude Sonnet使用時)。1ヶ月毎日使っても100円前後なので安心です。
ハマりポイント①:組織アカウントに紐づけようとしたら「追加の権限が必要です」と表示されました。その場合は管理者に「開発者アクセス」の付与を依頼する必要があります。個人アカウントで作れば不要です。
STEP 2|Slack Appを作成してUser Tokenを取得する
ここが一番手順が多いです。順番通りに進めれば大丈夫です。
2-1. Slack Appを新規作成する
api.slack.com/appsにアクセス- 右上の「Create New App」をクリック
- 「From scratch」を選択
- App Nameに
日報Botと入力、Workspaceに自分のワークスペース(Classmethod)を選択 - 「Create App」をクリック
2-2. Incoming Webhookを設定する
- 左メニューの「Incoming Webhooks」をクリック
- 「Activate Incoming Webhooks」をONに切り替える
- 下部の「Add New Webhook to Workspace」をクリック
- 日報を投稿したいチャンネルを選択して「許可する」
- 表示された
https://hooks.slack.com/services/...のURLをコピーして保存しておく
2-3. Bot Token Scopesを設定する
- 左メニューの「OAuth & Permissions」をクリック
- 「Scopes」のセクションまでスクロール
- 「Bot Token Scopes」の「Add an OAuth Scope」から以下の3つを追加する
| Scope | 用途 |
|---|---|
channels:history |
スレッドの検索 |
chat:write |
メッセージの投稿 |
groups:history |
プライベートチャンネル対応 |
2-4. User Token Scopesを設定する
同じページの「User Token Scopes」から以下の2つを追加する
| Scope | 用途 |
|---|---|
channels:history |
スレッドTS(タイムスタンプ)の自動検索 |
chat:write |
自分として投稿 |
2-5. ワークスペースにインストールする(管理者承認が必要)
- 「OAuth & Permissions」ページ上部の「Request to Workspace Install」をクリック
- Slack管理者に承認を依頼する
管理者への連絡文例
「日報自動化のSlack App『日報Bot』のインストール申請を出しました。承認をお願いできますか?チャンネルへのメッセージ投稿のみに使用します。」
- 承認後、
xoxp-から始まるUser OAuth Tokenをコピーして保存する
ハマりポイント②:管理者承認が下りるまで時間がかかる場合があります。STEP 2を終えたらすぐ申請を出して、承認を待ちながら次のSTEPを進めるのがおすすめです。
STEP 2.5|チャンネルIDとスレッド検索キーワードを確認する
チャンネルIDの確認方法
- Slackで日報を投稿したいチャンネルを開く
- チャンネル名をクリックしてチャンネル詳細を表示
- 詳細の一番下に表示される英数字(
Cから始まる)をコピー
例:C01ABCD2EFGのような文字列です。
ハマりポイント③:最初チャンネル名をそのまま入力してしまってエラーになりました。#generalなどのチャンネル名ではなく、チャンネルIDが必要です。必ず詳細から確認しましょう。
スレッド検索キーワードの確認方法
このシステムは、毎週月曜に投稿される週次スレッドの親メッセージを自動検索して、そこに返信する形で日報を投稿します。
- 日報を投稿したいスレッドの親メッセージを確認する(毎週月曜に投稿されているもの)
- そのメッセージに含まれる特徴的な文字列をメモする
特徴的な文字列の例:
- BotのID(
U0XXXXXXXのような形式) - 特定のユーザーグループへのメンション
- メッセージ冒頭の固定フレーズ(「今週の日報スレッド」など)
STEP 3|Google Apps Script(GAS)を設定する
3-1. GASプロジェクトを作成する
script.google.comにアクセス(Googleアカウントでログイン済みであればそのまま開けます)- 左上の「新しいプロジェクト」をクリック
- プロジェクト名を「日報自動化」に変更(画面上部のプロジェクト名をクリックすると変更できます)
3-2. コードを貼り付ける
- 開いたエディタ画面に最初から書かれているコードを全て削除する
- 以下のコードをコピーして貼り付ける
// ============================================================
// 日報自動生成・Slack投稿スクリプト
// ============================================================
// ① 設定エリア(★の項目を編集してください)
var MY_NAME = "氏名をここに入力"; // ★ 自分の名前
var MY_ROLE = "担当者"; // ★ 自分の職種(例:"営業担当者"、"エンジニア")
var CLAUDE_API_KEY = "sk-ant-ここにAPIキーを入力"; // ★ STEP1で取得
var SLACK_USER_TOKEN = "xoxp-ここにUser Tokenを入力"; // ★ STEP2で取得
var SLACK_CHANNEL_ID = "Cxxxxxxxxxx"; // ★ STEP2.5で取得したチャンネルID
var THREAD_SEARCH_KEYWORD = "ここに検索キーワードを入力"; // ★ STEP2.5で確認したキーワード
var CLAUDE_MODEL = "claude-sonnet-4-6";
var TIMEZONE = "Asia/Tokyo";
function runDailyReport() {
var day = new Date().getDay();
if (day === 0 || day === 6) { Logger.log("土日のためスキップ"); return; }
Logger.log("=== 日報自動生成開始 ===");
var events = getTodayEvents();
Logger.log("取得した予定数: " + events.length);
if (events.length === 0) { Logger.log("本日の予定がないため終了"); return; }
var memos = getEventMemos(events);
var reportText = generateReportWithClaude(events, memos);
var threadTs = findLatestMondayThread();
if (!threadTs) { Logger.log("対象スレッドが見つかりませんでした"); return; }
postToSlack(threadTs, reportText);
Logger.log("=== 日報投稿完了 ===");
}
function getTodayEvents() {
var today = new Date();
var startDay = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0);
var endDay = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59);
var calendar = CalendarApp.getDefaultCalendar();
var rawEvents = calendar.getEvents(startDay, endDay);
var results = [];
for (var i = 0; i < rawEvents.length; i++) {
var e = rawEvents[i];
results.push({
title: e.getTitle(),
startTime: Utilities.formatDate(e.getStartTime(), TIMEZONE, "HH:mm"),
endTime: Utilities.formatDate(e.getEndTime(), TIMEZONE, "HH:mm"),
location: e.getLocation() || "",
description: e.getDescription() || "",
attachmentIds: extractDocIds(e.getDescription() || ""),
});
}
return results;
}
function extractDocIds(description) {
var regex = /docs\.google\.com\/document\/d\/([a-zA-Z0-9_-]+)/g;
var ids = [];
var match;
while ((match = regex.exec(description)) !== null) { ids.push(match[1]); }
return ids;
}
function getEventMemos(events) {
var memos = [];
for (var i = 0; i < events.length; i++) {
var event = events[i];
if (!event.attachmentIds || event.attachmentIds.length === 0) continue;
for (var j = 0; j < event.attachmentIds.length; j++) {
var docId = event.attachmentIds[j];
try {
var doc = DocumentApp.openById(docId);
var content = doc.getBody().getText();
if (content.trim().length < 50) continue;
memos.push({ eventTitle: event.title, docTitle: doc.getName(), content: content.substring(0, 3000) });
} catch (err) { Logger.log("議事録取得エラー (" + docId + "): " + err.toString()); }
}
}
return memos;
}
function generateReportWithClaude(events, memos) {
var today = Utilities.formatDate(new Date(), TIMEZONE, "yyyy年MM月dd日(E)");
var eventsText = "";
for (var i = 0; i < events.length; i++) {
var e = events[i];
var loc = e.location ? "(" + e.location + ")" : "";
eventsText += "- " + e.startTime + "〜" + e.endTime + ":" + e.title + loc + "\n";
}
var memosText = "議事録なし";
if (memos.length > 0) {
memosText = "";
for (var j = 0; j < memos.length; j++) {
var m = memos[j];
memosText += "【" + m.eventTitle + "の議事録】\n" + m.content + "\n\n---\n\n";
}
}
var prompt = "あなたは" + MY_NAME + "(" + MY_ROLE + ")の日報作成アシスタントです。\n"
+ "以下の情報から、" + MY_NAME + "の視点でSlack投稿用の日報を作成してください。\n\n"
+ "## 本日の日付\n" + today + "\n\n"
+ "## 本日のカレンダー予定\n" + eventsText + "\n"
+ "## 議事録\n" + memosText + "\n"
+ "## 日報作成の指示\n"
+ "冒頭に「" + MY_NAME + "の日報」と日付を明記する\n"
+ "1. サマリー(2〜3文)\n2. 業務内容(時系列)\n3. 案件対応\n4. 所感\n\n"
+ "Slack markdown形式、500〜800文字程度";
var payload = JSON.stringify({
model: CLAUDE_MODEL, max_tokens: 2000,
messages: [{ role: "user", content: prompt }],
});
var options = {
method: "post", contentType: "application/json",
headers: { "x-api-key": CLAUDE_API_KEY, "anthropic-version": "2023-06-01" },
payload: payload, muteHttpExceptions: true,
};
var response = UrlFetchApp.fetch("https://api.anthropic.com/v1/messages", options);
var result = JSON.parse(response.getContentText());
if (result.error) throw new Error("Claude API エラー: " + JSON.stringify(result.error));
return result.content[0].text;
}
function findLatestMondayThread() {
var today = new Date();
var dayOfWeek = today.getDay();
var daysToMon = (dayOfWeek === 0) ? 6 : dayOfWeek - 1;
var lastMonday = new Date(today);
lastMonday.setDate(today.getDate() - daysToMon);
lastMonday.setHours(10, 0, 0, 0);
var oldest = String(Math.floor(lastMonday.getTime() / 1000) - 3600);
var latest = String(Math.floor(lastMonday.getTime() / 1000) + 3600);
var url = "https://slack.com/api/conversations.history"
+ "?channel=" + SLACK_CHANNEL_ID + "&oldest=" + oldest + "&latest=" + latest + "&limit=20";
var response = UrlFetchApp.fetch(url, {
headers: { "Authorization": "Bearer " + SLACK_USER_TOKEN },
muteHttpExceptions: true,
});
var result = JSON.parse(response.getContentText());
if (!result.ok) throw new Error("Slack API エラー: " + result.error);
var messages = result.messages || [];
for (var i = 0; i < messages.length; i++) {
if (messages[i].text && messages[i].text.indexOf(THREAD_SEARCH_KEYWORD) !== -1) return messages[i].ts;
}
return null;
}
function postToSlack(threadTs, message) {
var payload = JSON.stringify({
channel: SLACK_CHANNEL_ID, text: message, thread_ts: threadTs,
});
var response = UrlFetchApp.fetch("https://slack.com/api/chat.postMessage", {
method: "post", contentType: "application/json",
headers: { "Authorization": "Bearer " + SLACK_USER_TOKEN },
payload: payload, muteHttpExceptions: true,
});
var result = JSON.parse(response.getContentText());
if (!result.ok) throw new Error("Slack投稿エラー: " + result.error);
Logger.log("投稿成功: " + result.message.ts);
}
function setupTrigger() {
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
if (triggers[i].getHandlerFunction() === "runDailyReport") ScriptApp.deleteTrigger(triggers[i]);
}
ScriptApp.newTrigger("runDailyReport").timeBased().everyDays(1).atHour(19).create();
Logger.log("トリガー設定完了:毎日19時に runDailyReport が実行されます");
}
3-3. 設定値を書き換える
コードの上部にある★マークの6項目を自分の情報に書き換えます。
| 変数名 | 入力する値 |
|---|---|
MY_NAME |
自分の名前(例:「山田 太郎」) |
MY_ROLE |
自分の職種(例:「営業担当者」「エンジニア」「PM」) |
CLAUDE_API_KEY |
STEP1で取得したsk-ant-から始まるキー |
SLACK_USER_TOKEN |
STEP2で取得したxoxp-から始まるトークン |
SLACK_CHANNEL_ID |
STEP2.5で取得したCから始まるチャンネルID |
THREAD_SEARCH_KEYWORD |
STEP2.5で確認したキーワード |
書き換えたら、右上の「保存」ボタン(またはCmd+S / Ctrl+S)で保存します。
ハマりポイント④:コードを貼り付けた後、変数名をSLACK_WEBHOOK_URLのままにしていてエラーになりました(古いコードとの混在が原因)。コードを貼る前に既存コードを全選択して削除してから作業するのが安全です。
STEP 4|テスト実行とトリガー設定
4-1. 手動テスト実行
- エディタ上部のドロップダウンで「runDailyReport」を選択
- 「実行」ボタンをクリック
- 初回は「Googleアカウントの権限承認」画面が表示されるので「許可」をクリック(2回目以降は不要)
- 画面下部の「実行ログ」に
=== 日報投稿完了 ===と表示されれば成功!
Slackを確認して、指定のチャンネルに投稿されていればOKです。
4-2. 自動トリガーを設定する(毎日19時)
- ドロップダウンで「setupTrigger」を選択
- 「実行」ボタンをクリック
- 実行ログに「トリガー設定完了:毎日19時にrunDailyReportが実行されます」と表示されれば完了
これで設定完了です!以降は何もしなくてOK。PCを閉じていても毎日19時に自動投稿されます。
まとめ
- セットアップ時間:約1時間(管理者承認の待ち時間を除く)
- ランニングコスト:月100円前後
- 毎日の日報作業時間:ほぼゼロ
一番のハードルは管理者承認を待つことかもしれません(笑)。でも一度設定してしまえば、あとは完全放置で日報が飛んでいきます。
日報を毎日書いている方、ぜひ試してみてください。
【非エンジニアのためのClaude/ClaudeCodeシリーズ】の他の記事はこちら







