趣味の短歌とか俳句とかと、ゲームジャムの情報とかを眺めている。ほか、天気など。
Claude Code(claude.ai)の ルーチン機能 で、毎朝6時にいい感じに情報を集めてDiscordに自動投稿してくれるようにしました。
外部サービス(Cloudflare Workers / n8n / Zapier 等)は使わず、GitHub と claude.ai だけで完結する構成です。
経緯
もともと立ち上げっぱなしのMacBookを使用して、OpenClawでやってたんですが、いまいち安全ではないな、と思っていたところでClaude公式から機能が追加されたのでこちらに移行しました。月額料金の範囲内で使用できるのが良い点ですね。
ルーチン機能について
Claude Code には、定期実行用の仕組みが2種類あるようです。いつのまにか増えてました。
| Routines(ルーチン) | Desktop Scheduled Tasks | |
|---|---|---|
| 実行場所 | Anthropic のクラウド | ローカルPC |
| PC起動 | 不要 | 必要 |
| ローカルファイルアクセス | 不可 | 可 |
| トリガー | cron / 一回限り / API / GitHub webhook | スケジュール / 手動 |
今回は「PCを開いてなくても毎朝動いてほしい」ので クラウド側のルーチン を使います。
逆に、ローカルファイルを触る定期処理(例:手元のリポジトリをビルドして FTP アップロード)は Desktop Scheduled Tasks のほうが向いています。
参考: Routines / Desktop Scheduled Tasks(公式ドキュメント)
claude.ai のルーチンタブ。登録済みのルーチンが並ぶ。
全体の流れ
ルーチン側は Markdown を git push しています。GitHub Actions が拾ってDiscordに流します。
claude.ai のサンドボックスから直接 Discord は叩けない(egress allowlist 外)ので、間に GitHub Actions を一段挟みます。
やったこと
1. 空の private リポジトリを作る
GitHub の Web UI で <owner>/daily-brief のような空 repo を作ります。
2. Claude.ai の GitHub App にそのリポジトリを追加する
github.com/settings/installations → Claude → Repository access で、作った repo を選択。
これを忘れると、ルーチン側からの clone が通りません。
3. Discord webhook を Actions Secret に登録する
gh secret set DISCORD_WEBHOOK_URL \
--repo <owner>/daily-brief \
--body "https://discord.com/api/webhooks/..."
--body "VALUE" で直接渡します。
4. Workflow を commit する
.github/workflows/post-to-discord.yml:
name: post-to-discord
on:
push:
paths: ['posts/**.md']
jobs:
post:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 2 }
- name: Send to Discord
env:
WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: |
set -euo pipefail
if [ -z "${WEBHOOK:-}" ]; then echo 'WEBHOOK secret not set'; exit 1; fi
if git rev-parse HEAD~1 >/dev/null 2>&1; then
FILES=$(git diff --name-only --diff-filter=AM HEAD~1 HEAD -- 'posts/*.md' || true)
else
FILES=$(git ls-files 'posts/*.md')
fi
[ -z "$FILES" ] && { echo 'no post files'; exit 0; }
for f in $FILES; do
python3 - "$f" <<'PY'
import sys, os, json, time, urllib.request
path = sys.argv[1]
webhook = os.environ['WEBHOOK']
UA = 'daily-brief-bot/1.0'
text = open(path, encoding='utf-8').read().rstrip() + '\n'
paras = text.split('\n\n')
chunks, cur = [], ''
for p in paras:
candidate = (cur + '\n\n' + p) if cur else p
if len(candidate) <= 1900:
cur = candidate
else:
if cur: chunks.append(cur)
cur = p if len(p) <= 1900 else p[:1900]
if cur: chunks.append(cur)
for i, c in enumerate(chunks):
body = json.dumps({'content': c, 'allowed_mentions': {'parse': []}}).encode()
req = urllib.request.Request(webhook, data=body, method='POST',
headers={'Content-Type': 'application/json', 'User-Agent': UA})
with urllib.request.urlopen(req) as r:
print(f'{path} [{i+1}/{len(chunks)}] -> {r.status}')
if i < len(chunks) - 1:
time.sleep(1.2)
PY
done
ポイント
paths: ['posts/**.md']でposts/配下の.md変更だけをトリガにする- User-Agent ヘッダを必ずセットする(urllib のデフォルト UA は Cloudflare に bot 判定されて 403 になる)
- Discord の2000字制限に引っかからないよう、段落境界で分割。チャンク間に1.2秒 sleep
fetch-depth: 2で直前 commit を確保し、diff で「今回追加された投稿」だけを送る
5. claude.ai のルーチンを作る
cron_expression: "0 21 * * *"(06:00 JST = 21:00 UTC)session_context.sources: [{git_repository: {url: "https://github.com/<owner>/daily-brief"}}]- プロンプトは「harness が clone 済みの前提で、WebSearch → Markdown 生成 →
posts/$DATE.mdに write → git add/commit/push origin main」
6. 完成
翌朝 06:00 JST に Discord に流れてきます。
確かめたければ実行ボタンを押せばいいですね。