複数Googleアカウントのカレンダーを一元管理する方法【AIアシスタント連携】
はじめに
仕事用とプライベート用で複数のGoogleアカウントを使い分けている人は多いと思います。しかし、カレンダーを複数開いて確認するのは面倒ですよね。
そこで今回、複数のGoogleカレンダーを自動監視して、新しい予定をリアルタイムで通知するツールを開発しました。個人用AIアシスタントと連携して、Telegramに自動通知します。
開発の背景
私は以下の2つのGoogleアカウントを使い分けています:
- 仕事用アカウント
- 会社の会議・MTG
-
顧客との打ち合わせ
-
副業用アカウント
- 体験会・研修
- リーダーMTG
これらを常に2つのカレンダーを開いて確認するのが非効率だったため、一元管理できるシステムを構築しました。
システム構成
アーキテクチャ
Google Calendar API
↓
Python監視スクリプト(30分ごとに実行)
↓
AIアシスタント
↓
Telegram通知
技術スタック
- Python 3.11
- Google Calendar API v3
- OAuth 2.0認証
- Cron – 定期実行
実装のポイント
1. Google Calendar API の認証
Google Calendar APIを使うには、OAuth 2.0認証が必要です。
credentials.json の取得
- Google Cloud Console{:target=”_blank” rel=”noopener noreferrer”}にアクセス
- プロジェクトを作成
- 「APIとサービス」→「OAuth同意画面」で設定
- 「認証情報」→「OAuth 2.0 クライアントID」を作成
credentials.jsonをダウンロード
認証フロー
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
SCOPES = [
'https://www.googleapis.com/auth/calendar.readonly', # カレンダー読み取り
'https://www.googleapis.com/auth/calendar.events' # イベント作成・編集
]
def authenticate(account: str):
"""
Googleアカウントを認証してトークンを保存
Args:
account: 'work' or 'side_job'
"""
token_path = f'token_{account}.json'
creds = None
# 既存のトークンを読み込み
if os.path.exists(token_path):
creds = Credentials.from_authorized_user_file(token_path, SCOPES)
# トークンが無効または存在しない場合、再認証
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
# トークンをリフレッシュ
creds.refresh(Request())
else:
# 新規認証(ブラウザが開く)
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# トークンを保存
with open(token_path, 'w') as token:
token.write(creds.to_json())
return build('calendar', 'v3', credentials=creds)
ポイント:
– 各アカウントごとに別々のトークンファイル(token_work.json、token_side_job.json)を保存
– トークンの有効期限が切れたら自動でリフレッシュ
2. カレンダーイベントの監視
30分ごとに新しいイベントをチェックして、通知します。
import json
from datetime import datetime, timedelta
STATE_FILE = 'calendar_monitor_state.json'
def load_state():
"""前回のチェック状態を読み込む"""
if os.path.exists(STATE_FILE):
with open(STATE_FILE, 'r') as f:
return json.load(f)
return {
'last_check': None,
'seen_events': {} # {account: [event_id, ...]}
}
def check_new_events(account: str, state: dict):
"""
新しいイベントをチェック
Returns:
新しいイベントのリスト
"""
# カレンダーサービスを取得
service = authenticate(account)
# 前回チェック時刻以降のイベントを取得
last_check = state.get('last_check')
if last_check:
time_min = datetime.fromisoformat(last_check).isoformat() + 'Z'
else:
# 初回は現在時刻から
time_min = datetime.utcnow().isoformat() + 'Z'
time_max = (datetime.utcnow() + timedelta(days=30)).isoformat() + 'Z'
# イベント取得
events_result = service.events().list(
calendarId='primary',
timeMin=time_min,
timeMax=time_max,
singleEvents=True,
orderBy='startTime'
).execute()
events = events_result.get('items', [])
# 既に見たイベントを除外
seen_ids = set(state.get('seen_events', {}).get(account, []))
new_events = [
event for event in events
if event['id'] not in seen_ids
]
return new_events
def monitor_calendars():
"""全カレンダーを監視"""
state = load_state()
accounts = ['work', 'side_job']
all_new_events = []
for account in accounts:
print(f"Checking {account}...")
try:
new_events = check_new_events(account, state)
if new_events:
print(f" {len(new_events)} new event(s)")
# 見たイベントIDを記録
if account not in state['seen_events']:
state['seen_events'][account] = []
for event in new_events:
state['seen_events'][account].append(event['id'])
all_new_events.append({
'account': account,
'event': event
})
else:
print(f" No new events")
except Exception as e:
print(f" Error: {e}")
# 状態を更新
state['last_check'] = datetime.utcnow().isoformat()
save_state(state)
# 通知
if all_new_events:
notify_new_events(all_new_events)
else:
print("\nNo new events to notify")
def save_state(state: dict):
"""状態を保存"""
with open(STATE_FILE, 'w') as f:
json.dump(state, f, indent=2)
ポイント:
– 前回チェック時刻を保存して、重複通知を防ぐ
– 既に見たイベントIDを記録(seen_events)
– 複数アカウントを順次チェック
3. Telegram通知の実装
Telegram Bot APIを使って通知を送信します。
def notify_new_events(events: list):
"""
新しいイベントをTelegramに通知
Args:
events: [{account: str, event: dict}, ...]
"""
message = "新しいカレンダーイベントが追加されました!\n\n"
for item in events:
account = item['account']
event = item['event']
# 日本語のアカウント名
account_name = {
'work': '仕事用',
'side_job': '副業用'
}.get(account, account)
# イベント情報を整形
summary = event.get('summary', '(タイトルなし)')
start = event['start'].get('dateTime', event['start'].get('date'))
# 日時をパース
if 'T' in start:
# 時刻あり
dt = datetime.fromisoformat(start.replace('Z', '+00:00'))
time_str = dt.strftime('%m/%d %H:%M')
else:
# 終日イベント
dt = datetime.fromisoformat(start)
time_str = dt.strftime('%m/%d')
message += f" {time_str} - {summary}\n"
message += f" ({account_name})\n\n"
# Telegram Bot APIで送信
import requests
bot_token = os.getenv('TELEGRAM_BOT_TOKEN')
chat_id = os.getenv('TELEGRAM_CHAT_ID')
response = requests.post(
f'https://api.telegram.org/bot{bot_token}/sendMessage',
json={
'chat_id': chat_id,
'text': message,
'parse_mode': 'Markdown'
}
)
if response.status_code == 200:
print("Notification sent to Telegram")
else:
print(f"Failed to notify: {response.status_code}")
4. Cronによる定期実行
30分ごとに監視スクリプトを実行:
#!/bin/bash
# run_monitor.sh
cd "$(dirname "$0")"
python3 calendar_monitor.py
# crontab -e で設定
*/30 * * * * /path/to/run_monitor.sh >> /path/to/logs/calendar_monitor.log 2>&1
実際の通知例
Telegramに以下のような通知が届きます:
新しいカレンダーイベントが追加されました!
03/10 09:30 - 週次定例MTG
(仕事用)
03/12 14:00 - リーダーMTG
(副業用)
03/15 13:00 - 取引先との打ち合わせ
(仕事用)
追加機能
1. 会議前リマインダー
30分前、1時間前に自動リマインド:
def check_upcoming_events():
"""30分以内に始まる予定をチェック"""
now = datetime.now()
threshold = now + timedelta(minutes=30)
for account in ['work', 'side_job']:
service = authenticate(account)
events_result = service.events().list(
calendarId='primary',
timeMin=now.isoformat() + 'Z',
timeMax=threshold.isoformat() + 'Z',
singleEvents=True,
orderBy='startTime'
).execute()
events = events_result.get('items', [])
for event in events:
start = event['start'].get('dateTime')
if start:
start_dt = datetime.fromisoformat(start.replace('Z', '+00:00'))
minutes_until = (start_dt - now).total_seconds() / 60
if 25 <= minutes_until <= 30:
notify_reminder(event, int(minutes_until))
def notify_reminder(event: dict, minutes: int):
"""リマインダー通知"""
summary = event.get('summary', '(タイトルなし)')
message = f"まもなく予定があります!({minutes}分後)\n\n"
message += f"{summary}"
# Telegramに通知
# ... (省略)
2. 予定のコンフリクト検出
同じ時間に複数の予定が入っている場合に警告:
def detect_conflicts():
"""予定の重複を検出"""
all_events = []
# 全アカウントのイベントを取得
for account in ['work', 'side_job']:
service = authenticate(account)
events = get_events(service)
all_events.extend([(account, e) for e in events])
# 時間でソート
all_events.sort(key=lambda x: x[1]['start'].get('dateTime', ''))
conflicts = []
for i in range(len(all_events) - 1):
event1 = all_events[i][1]
event2 = all_events[i + 1][1]
end1 = event1['end'].get('dateTime')
start2 = event2['start'].get('dateTime')
if end1 and start2:
end1_dt = datetime.fromisoformat(end1)
start2_dt = datetime.fromisoformat(start2)
if end1_dt > start2_dt:
conflicts.append((event1, event2))
if conflicts:
notify_conflicts(conflicts)
セキュリティ対策
1. トークンの安全な保存
# トークンファイルのパーミッションを制限
chmod 600 token_*.json
2. 環境変数での管理
# .env ファイルを使用
TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=your_chat_id_here
from dotenv import load_dotenv
load_dotenv()
bot_token = os.getenv('TELEGRAM_BOT_TOKEN')
chat_id = os.getenv('TELEGRAM_CHAT_ID')
3. .gitignoreで秘密情報を除外
# Google Calendar認証
credentials.json
token_*.json
# 環境変数
.env
# 状態ファイル
calendar_monitor_state.json
トラブルシューティング
エラー: invalid_grant
原因:トークンの有効期限切れ
解決策:
# トークンを削除して再認証
rm token_*.json
python server.py
# → ブラウザで認証
エラー: API has not been used
原因:Google Calendar APIが有効化されていない
解決策:
1. Google Cloud Consoleで「APIとサービス」→「有効なAPIとサービス」
2. 「+ APIとサービスの有効化」をクリック
3. 「Google Calendar API」を検索して有効化
まとめ
複数Googleアカウントのカレンダー統合で得られた効果:
✅ カレンダーを切り替える手間がゼロに
→ 全ての予定を一箇所で確認
✅ 新しい予定の見逃しが激減
→ 自動通知で即座に気づく
✅ 会議前のリマインダーで遅刻防止
→ 30分前通知で準備できる
✅ 予定の重複を事前に検出
→ ダブルブッキングを防ぐ
Google Calendar APIは非常に強力で、今回紹介した機能以外にも:
– イベントの自動作成
– 定期予定の管理
– 参加者への招待送信
など、様々なことが可能です。業務効率化の第一歩として、ぜひ試してみてください!
技術スタック: Python, Google Calendar API, OAuth 2.0, Telegram Bot API
開発期間: 約1週間
コード行数: 約1,000行
対応サービス: Google Calendar