SlackからワンコマンドでGoogleカレンダー招待リンクを作成するBotを作った
はじめに
Slackでミーティングを設定する際、以下のような課題はありませんか?
- メンバーのメールアドレスを一人ずつコピペするのが面倒
- Googleカレンダーを開いてゲストを追加するのに時間がかかる
- チャンネル全員を招待したいのに、一人ずつ追加するのが大変
そこで、Slackのコマンド一つでGoogleカレンダーの招待リンクを自動生成するBotを作成しました。
デモ
# 個別メンション
/calendar @田中 @佐藤 @鈴木
# チャンネル全員
/calendar @channel
実行すると、自分だけに以下のメッセージが表示されます:
✅ カレンダーリンクを作成しました
件名: ミーティング
日時: 2025年12月25日 21:00-22:00
ゲスト:
・田中太郎 <tanaka@example.com>
・佐藤花子 <sato@example.com>
・...
[Googleカレンダーを開く]
ボタンをクリックすると、ゲストが全員追加された状態でGoogleカレンダーが開きます。
作成したもの
主な機能
✅ Slash Commandで簡単実行
-
/calendar @user1 @user2で個別メンション -
/calendar @channelでチャンネル全員 -
-
/calendar @hereでチャンネル全員
-
✅ 自動的にメールアドレスを取得
- Slackのプロフィールから自動取得
- 手動でのコピペ不要
✅ プライバシー保護
- メッセージは実行者だけに表示(ephemeral)
- 他のメンバーに通知が行かない
✅ スマートな時刻設定
- 実行時刻の次の正時に自動設定
- 例:14:34に実行 → 15:00-16:00
✅ 大人数対応
-
@channelで数十人のメンバーにも対応 - タイムアウトエラーなし(非同期処理)
技術スタック
- AWS Lambda - サーバーレス実行環境
- Python 3.14 - 実装言語
- Slack Bolt for Python - Slack Bot開発フレームワーク
- Slack API - Slash Command、Users API
- Google Calendar API - カレンダーリンク生成
アーキテクチャ
Slack → Lambda Function URL → Lambda (Python 3.14)
↓
Slack Bolt Framework
↓
┌──────────────┴──────────────┐
↓ ↓
Slack API Google Calendar
(User情報取得) (リンク生成)
プロジェクト構成
ディレクトリ/ファイル構造
slack-calendar-bot/
├── app.py # メインのアプリケーションコード
├── requirements.txt # Python依存パッケージ
├── README.md # プロジェクト説明(オプション)
├── .gitignore # Git除外設定(オプション)
├── package/ # デプロイ用パッケージディレクトリ
│ ├── app.py # app.pyのコピー
│ ├── slack_bolt/ # Slack Boltライブラリ
│ ├── slack_sdk/ # Slack SDKライブラリ
│ ├── requests/ # Requestsライブラリ
│ └── ... # その他の依存ライブラリ
└── lambda_function.zip # Lambda用デプロイパッケージ
各ファイルの役割
| ファイル | 役割 |
|---|---|
app.py |
メインコード(Slack Bolt、Lambda Handler) |
requirements.txt |
依存パッケージ一覧 |
package/ |
Lambdaにデプロイする全ファイル |
lambda_function.zip |
Lambda用のZIPファイル |
ローカル開発環境のセットアップ
前提条件
- Python 3.12以上
- pip(Pythonパッケージマネージャー)
- AWS CLI(オプション)
- Git(オプション)
1. プロジェクトディレクトリの作成
# プロジェクトディレクトリを作成
mkdir slack-calendar-bot
cd slack-calendar-bot
2. requirements.txtの作成
cat > requirements.txt << EOF
slack-bolt==1.18.0
requests
EOF
requirements.txtの内容:
slack-bolt==1.18.0
requests
3. app.pyの作成
app.pyを作成し、以下のコードを記述します:
(後述の「コード全体」セクション参照)
4. 依存パッケージのインストール
# packageディレクトリを作成
mkdir package
# 依存パッケージをpackageディレクトリにインストール
pip3 install -r requirements.txt -t ./package
# app.pyをpackageにコピー
cp app.py package/
実行結果の確認:
ls -la package/
# 以下のようなファイル/ディレクトリが表示されればOK
# app.py
# slack_bolt/
# slack_sdk/
# requests/
# certifi/
# urllib3/
# ...
5. デプロイパッケージの作成
# packageディレクトリ内でZIPを作成
cd package
zip -r ../lambda_function.zip .
cd ..
# ZIPファイルのサイズを確認
ls -lh lambda_function.zip
# 約2-3MBになっていればOK
6. ローカルでのテスト(オプション)
Lambda環境を再現してローカルテストする場合:
# AWS SAM CLIを使用
sam local start-api
# または、Docker + Lambda Runtime Emulatorを使用
docker run -p 9000:8080 \
-v $(pwd)/package:/var/task \
public.ecr.aws/lambda/python:3.14 \
app.lambda_handler
開発→デプロイの流れ
開発フロー
1. ローカルでコード編集
↓
2. packageにコピー
↓
3. ZIPファイル作成
↓
4. Lambdaにアップロード
↓
5. Slackでテスト
↓
6. CloudWatch Logsで確認
コード変更時の手順
# 1. app.pyを編集
vim app.py
# 2. packageに反映
cp app.py package/
# 3. ZIPを再作成
rm lambda_function.zip
cd package
zip -r ../lambda_function.zip .
cd ..
# 4. Lambdaにアップロード
# AWS Lambda コンソール → アップロード元 → .zipファイル
.gitignoreの設定(オプション)
Gitで管理する場合、以下を.gitignoreに追加:
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
# Virtual Environment
venv/
env/
ENV/
# Package
package/
lambda_function.zip
# Environment Variables
.env
# IDE
.vscode/
.idea/
*.swp
*.swo
実装のポイント
1. Slackの3秒ルール対策
Slackは、Slash Commandの応答を3秒以内に返さないとoperation_timeoutエラーになります。
解決策:Lazy Listener + 非同期処理
def calendar_ack(body, ack, logger):
"""即座に進捗メッセージを返す(3秒以内)"""
ack({
"response_type": "ephemeral",
"text": "⏳ カレンダーリンクを作成中...",
})
def handle_calendar_command(body, client, logger):
"""非同期で実際の処理を実行"""
# 重い処理(ユーザー情報取得、メール取得など)
# response_urlで結果を返す
pass
# Lazy Listenerとして登録
app.command("/calendar")(ack=calendar_ack, lazy=[handle_calendar_command])
2. チャンネルメンバー全員の取得
@channelや@hereを検出し、チャンネルメンバー全員を取得します。
def get_channel_members(client, channel_id):
"""チャンネルメンバーを取得(Bot除外)"""
# チャンネルメンバー取得
members_result = client.conversations_members(channel=channel_id)
member_ids = members_result.get("members", [])
# 全ユーザーリストを一度に取得(効率化)
users_result = client.users_list()
# Botユーザーを除外
bot_ids = {u["id"] for u in users_result["members"] if u.get("is_bot")}
human_members = [uid for uid in member_ids if uid not in bot_ids]
return human_members
重要:Botがチャンネルメンバーである必要がある
conversations.members APIを使うには、Botがそのチャンネルに追加されている必要があります。
# チャンネルにBotを追加
/invite @Calendar Link Bot
3. 実行時刻の次の正時に設定
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
# 日本時間で現在時刻を取得
jst = ZoneInfo('Asia/Tokyo')
now = datetime.now(jst)
# 次の正時(整時刻)に設定
if now.minute > 0 or now.second > 0:
start_dt = now.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
else:
start_dt = now.replace(minute=0, second=0, microsecond=0)
end_dt = start_dt + timedelta(hours=1)
# Google Calendar用のフォーマット(UTC)
start_time = start_dt.astimezone(ZoneInfo('UTC')).strftime("%Y%m%dT%H%M%SZ")
end_time = end_dt.astimezone(ZoneInfo('UTC')).strftime("%Y%m%dT%H%M%SZ")
動作例:
- 14:34に実行 → 15:00-16:00
- 20:05に実行 → 21:00-22:00
4. プライバシー保護(ephemeral)
すべてのメッセージをephemeral(自分だけに表示)に設定:
# ack()の応答
ack({
"response_type": "ephemeral", # 自分だけに表示
"text": "⏳ カレンダーリンクを作成中...",
})
# 最終結果も自分だけに表示
success_message = {
"response_type": "ephemeral",
"text": "✅ カレンダーリンクを作成しました",
}
プロジェクト構成
ディレクトリ/ファイル構造
slack-calendar-bot/
├── app.py # メインのアプリケーションコード
├── requirements.txt # Python依存パッケージ
├── README.md # プロジェクト説明(オプション)
├── .gitignore # Git除外設定(オプション)
├── package/ # デプロイ用パッケージディレクトリ
│ ├── app.py # app.pyのコピー
│ ├── slack_bolt/ # Slack Boltライブラリ
│ ├── slack_sdk/ # Slack SDKライブラリ
│ ├── requests/ # Requestsライブラリ
│ ├── urllib3/ # urllib3ライブラリ
│ ├── certifi/ # SSL証明書
│ └── ... # その他の依存ライブラリ
└── lambda_function.zip # Lambda用デプロイパッケージ
各ファイルの役割
| ファイル/フォルダ | 役割 | 作成方法 |
|---|---|---|
app.py |
メインコード(Slack Bolt、Lambda Handler) | 手動作成 |
requirements.txt |
依存パッケージ一覧 | 手動作成 |
package/ |
Lambdaにデプロイする全ファイル |
pip installで自動生成 |
lambda_function.zip |
Lambda用のZIPファイル |
zipコマンドで作成 |
ローカル開発環境のセットアップ
前提条件
以下がインストールされていることを確認:
-
Python 3.12以上
python3 --version # Python 3.12.0 以上であればOK -
pip(Pythonパッケージマネージャー)
pip3 --version -
zip(ZIPコマンド)
zip --version # Macには標準でインストール済み
ステップ1: プロジェクトディレクトリの作成
# プロジェクトディレクトリを作成
mkdir slack-calendar-bot
cd slack-calendar-bot
# 現在のディレクトリを確認
pwd
# /Users/your-name/slack-calendar-bot
ステップ2: requirements.txtの作成
依存パッケージを定義するファイルを作成:
cat > requirements.txt << 'EOF'
slack-bolt==1.18.0
requests
EOF
requirements.txtの内容:
slack-bolt==1.18.0
requests
各パッケージの役割:
-
slack-bolt- Slack Bot開発フレームワーク -
requests- HTTP通信ライブラリ(response_url用)
ステップ3: app.pyの作成
メインのアプリケーションコードを作成:
# エディタで開く(VSCode, vim, nanoなど)
code app.py # VSCodeの場合
# または
vim app.py # vimの場合
app.pyの内容:
(後述の「コード全体」セクションの完全版を参照)
ステップ4: 依存パッケージのインストール
# packageディレクトリを作成
mkdir package
# 依存パッケージをpackageディレクトリにインストール
pip3 install -r requirements.txt -t ./package
# インストールされたパッケージを確認
ls -la package/
実行結果の例:
drwxr-xr-x slack_bolt/
drwxr-xr-x slack_sdk/
drwxr-xr-x requests/
drwxr-xr-x urllib3/
drwxr-xr-x certifi/
...
サイズ確認:
du -sh package/
# 約10-15MBになる
ステップ5: app.pyをpackageにコピー
# app.pyをpackageディレクトリにコピー
cp app.py package/
# コピーされたか確認
ls package/app.py
# package/app.py
ステップ6: デプロイパッケージ(ZIP)の作成
# packageディレクトリに移動
cd package
# ZIPファイルを作成(カレントディレクトリの全ファイルを含める)
zip -r ../lambda_function.zip .
# 親ディレクトリに戻る
cd ..
# ZIPファイルのサイズを確認
ls -lh lambda_function.zip
# 約2-3MBになっていればOK
ZIPの内容を確認:
# ZIPの中身を確認
unzip -l lambda_function.zip | head -20
# app.pyが含まれているか確認
unzip -l lambda_function.zip | grep "app.py"
# 14591 12-25-2025 20:30 app.py
ステップ7: .gitignoreの設定(Git使用時)
Gitでバージョン管理する場合、以下のファイルを.gitignoreに追加:
cat > .gitignore << 'EOF'
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
# Virtual Environment
venv/
env/
ENV/
# Package(生成されるファイル)
package/
lambda_function.zip
# Environment Variables(機密情報)
.env
.env.local
# IDE
.vscode/
.idea/
*.swp
*.swo
# macOS
.DS_Store
EOF
最終的なディレクトリ構成
slack-calendar-bot/
├── app.py ✅ メインコード
├── requirements.txt ✅ 依存パッケージ
├── .gitignore ✅ Git除外設定
├── package/ ✅ デプロイ用(約10-15MB)
│ ├── app.py
│ ├── slack_bolt/
│ ├── slack_sdk/
│ ├── requests/
│ └── ...
└── lambda_function.zip ✅ デプロイパッケージ(約2-3MB)
開発→デプロイの流れ
初回セットアップ
1. プロジェクトディレクトリ作成
↓
2. requirements.txt作成
↓
3. app.py作成
↓
4. pip install(packageディレクトリに)
↓
5. app.pyをpackageにコピー
↓
6. ZIPファイル作成
↓
7. Lambdaにアップロード
コード変更時の手順
app.pyを修正した場合:
# 1. app.pyを編集
vim app.py
# 2. packageに反映
cp app.py package/
# 3. 古いZIPを削除
rm lambda_function.zip
# 4. 新しいZIPを作成
cd package
zip -r ../lambda_function.zip .
cd ..
# 5. サイズ確認
ls -lh lambda_function.zip
# 6. AWS Lambda コンソールでアップロード
# (ブラウザで手動アップロード)
もっと簡単に:ワンライナー
# すべてを一気に実行
cp app.py package/ && \
rm -f lambda_function.zip && \
cd package && zip -r ../lambda_function.zip . && cd .. && \
ls -lh lambda_function.zip
依存パッケージを追加した場合
requirements.txtに新しいパッケージを追加した場合:
# 1. packageディレクトリを削除
rm -rf package/
# 2. 再作成
mkdir package
# 3. 依存パッケージを再インストール
pip3 install -r requirements.txt -t ./package
# 4. app.pyをコピー
cp app.py package/
# 5. ZIP作成
cd package && zip -r ../lambda_function.zip . && cd ..
デバッグの流れ
1. Slackでコマンド実行
↓
2. エラー発生
↓
3. CloudWatch Logsで確認
↓
4. ローカルでapp.pyを修正
↓
5. packageにコピー
↓
6. ZIP作成
↓
7. Lambdaに再アップロード
↓
8. 再テスト
セットアップ手順
1. Slack Appの作成
- Slack APIにアクセス
- 「Create New App」 → 「From scratch」
- App Nameに「Calendar Link Bot」を入力
- ワークスペースを選択
2. 権限の設定
OAuth & Permissions → Bot Token Scopesに以下を追加:
-
commands- Slash Commandの実行 -
users:read- ユーザー情報の取得 -
users:read.email- メールアドレスの取得 -
chat:write- メッセージの送信 -
channels:read- パブリックチャンネル情報の取得 -
groups:read- プライベートチャンネル情報の取得
3. Slash Commandの作成
Slash Commands → Create New Command
-
Command:
/calendar - Request URL: (後でLambda Function URLを設定)
-
Short Description:
Googleカレンダーリンクを作成
4. Lambda関数の作成
4.1 Lambda関数を作成
AWS Lambda コンソールで新しい関数を作成:
関数名: slack-calendar-bot
ランタイム: Python 3.14
アーキテクチャ: x86_64
4.2 環境変数を設定
設定 → 環境変数 → 編集 で以下を追加:
-
SLACK_BOT_TOKEN:xoxb-...(Bot User OAuth Token) -
SLACK_SIGNING_SECRET: Slack AppのSigning Secret
4.3 Function URLを作成
設定 → Function URL → Create function URL
- 認証タイプ: NONE
- 追加のCORS設定: 無効
生成されたURLをコピー → Slash CommandのRequest URLに設定
4.4 デプロイパッケージのアップロード
前述の「ローカル開発環境のセットアップ」で作成したlambda_function.zipをアップロード:
AWS Lambda コンソール → コード → アップロード元 → .zip ファイル → lambda_function.zipを選択
※ ローカルでのZIP作成手順は「ローカル開発環境のセットアップ」セクションを参照
5. Slack Appをインストール
Install App → Install to Workspace → 許可する
6. テスト
使いたいチャンネルで:
# Botを追加(@channel使用時に必要)
/invite @Calendar Link Bot
# テスト実行
/calendar @channel
コード全体
app.py(抜粋)
クリックして展開
import os
import re
import json
import requests
from urllib.parse import urlencode
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler
# Slack App初期化
app = App(
token=os.environ.get("SLACK_BOT_TOKEN"),
signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
process_before_response=True
)
def has_channel_mention(text):
"""@channelまたは@hereが含まれているか判定"""
return "@channel" in text or "@here" in text
def get_channel_members(client, channel_id):
"""チャンネルメンバーを取得(Bot除外)"""
members_result = client.conversations_members(channel=channel_id)
member_ids = members_result.get("members", [])
users_result = client.users_list()
bot_ids = {u["id"] for u in users_result["members"] if u.get("is_bot")}
return [uid for uid in member_ids if uid not in bot_ids]
def calendar_ack(body, ack, logger):
"""即座に応答(3秒ルール対策)"""
ack({
"response_type": "ephemeral",
"text": "⏳ カレンダーリンクを作成中...",
})
def handle_calendar_command(body, client, logger):
"""カレンダーリンク作成処理"""
command = body
text = command.get("text", "").strip()
channel_id = command.get("channel_id")
response_url = command.get("response_url")
# メンション処理
if has_channel_mention(text):
mentioned_user_ids = get_channel_members(client, channel_id)
else:
mentioned_user_ids = extract_user_mentions(text)
# メールアドレス取得
users = []
for uid in mentioned_user_ids:
email, real_name = fetch_user_email(client, uid)
if email:
users.append({"email": email, "real_name": real_name})
# 日時設定(次の正時)
jst = ZoneInfo('Asia/Tokyo')
now = datetime.now(jst)
if now.minute > 0 or now.second > 0:
start_dt = now.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
else:
start_dt = now.replace(minute=0, second=0, microsecond=0)
end_dt = start_dt + timedelta(hours=1)
# カレンダーリンク生成
link = build_google_calendar_link("ミーティング", start_dt, end_dt, users)
# response_urlで結果を返す
send_response(response_url, {
"response_type": "ephemeral",
"text": "✅ カレンダーリンクを作成しました",
# ... blocks
})
# Lazy Listenerとして登録
app.command("/calendar")(ack=calendar_ack, lazy=[handle_calendar_command])
# Lambda用ハンドラー
def lambda_handler(event, context):
slack_handler = SlackRequestHandler(app=app)
return slack_handler.handle(event, context)
トラブルシューティング
operation_timeout エラー
原因: 3秒以内に応答できていない
解決策:
- Lazy Listener + 非同期処理を使用
-
ack()で即座に進捗メッセージを返す - 重い処理は
response_urlで返す
channel_not_found エラー
原因: Botがチャンネルメンバーでない
解決策:
/invite @Calendar Link Bot
メールアドレスが取得できない
原因: Slackプロフィールにメールアドレスが設定されていない
解決策:
- Slackプロフィールにメールアドレスを追加
- または、
users:read.email権限を確認
運用コスト
AWS Lambda
無料枠(永続):
- リクエスト数: 月100万リクエストまで無料
- 実行時間: 月40万GB-秒まで無料
このBotの実質コスト(月3,000回実行):
- Lambda実行料金: $0(無料枠内)
- CloudWatch Logs: 約$0.001(約0.1円/月)
ほぼ無料で運用可能!
使用例
定例ミーティング
/calendar @channel
→ チーム全員のカレンダーリンクを作成
→ 件名を「週次ミーティング」に変更
→ 日時を毎週月曜10:00に設定
急な打ち合わせ
/calendar @田中 @佐藤
→ 次の正時のミーティングリンクを作成
→ すぐにGoogle Meetを追加
プロジェクトキックオフ
/calendar @channel
→ 件名を「プロジェクトキックオフ」に変更
→ アジェンダを説明欄に追加
メリット
時間短縮
従来の方法:
- Googleカレンダーを開く
- 新規イベントを作成
- メンバーのメールアドレスをコピー
- ゲストを一人ずつ追加
- 日時を設定
Calendar Link Bot:
-
/calendar @channelを実行 - ボタンをクリック
- 日時を調整(必要に応じて)
プライバシー保護
- メッセージは実行者だけに表示
- チャンネルが汚れない
- 他のメンバーに通知が行かない
柔軟性
- 個別メンションも
@channelにも対応 - 日時は後から自由に調整可能
- Googleカレンダーのすべての機能が使える
今後の展望
現在、以下の機能追加を検討しています:
1. 参加メンバーの空いている時間をレコメンド
構想:
- Google Calendar APIを使って参加者のカレンダーを取得
- 全員が空いている時間帯を自動検出
- 最適な時間をレコメンド
例:
/calendar @田中 @佐藤 @鈴木
↓
✅ 以下の時間帯が全員空いています:
• 明日 14:00-15:00
• 明後日 10:00-11:00
• 金曜日 15:00-16:00
[明日 14:00-15:00で作成]
技術的な課題:
- Google Calendar APIの認証(OAuth 2.0)
- 複数人のカレンダーを並行取得
- プライバシー考慮(カレンダー情報へのアクセス許可)
2. AIの導入
構想1: 自然言語での時刻指定
/calendar @田中 @佐藤 明日の午後に1時間
→ 明日の14:00-15:00でリンク作成
/calendar @channel 来週の月曜日の朝
→ 来週月曜日の10:00-11:00でリンク作成
構想2: ミーティング内容の自動提案
Slackの過去のメッセージを分析して:
- 件名を自動提案
- アジェンダを生成
- 必要な参加者を推薦
構想3: スマートなリマインダー
AIが以下を判断:
- ミーティング前の最適なリマインダー時刻
- 準備が必要な場合は早めに通知
- 場所の移動時間を考慮
技術スタック候補:
- OpenAI API(自然言語処理)
- Claude API(文脈理解)
- LangChain(LLMアプリケーション開発)
まとめ
Slack Calendar Link Botにより、カレンダー招待の作成時間を60-70%削減できました。
主な特徴:
今後はAIを活用して、さらに使いやすく進化させていく予定です。