Note Auto Post
プロジェクト概要
Note Auto Post は、自分の note 記事をもとに Threads 投稿を自動生成・自動投稿するために開発したツールです。note の RSS から記事を取得し、ランダムに 1 件を選択したうえで、Gemini API により SNS 向けの文章へ要約します。生成された本文は Threads API を通じて投稿され、必要に応じて記事リンクをリプライとして追加します。 定期実行には GitHub Actions を利用しており、毎日決まった時間帯に自動で投稿される構成です。外部 API の一時的な混雑や、Threads API 側の反映遅延にも対応できるよう、再試行処理や状態確認処理を組み込んでいます。
開発目的
- note 記事の再活用を自動化するため
- SNS 投稿作成の手間を減らすため
- GitHub Actions を使った定期実行の仕組みを実践するため
- Gemini API と Threads API を組み合わせた自動投稿フローを構築するため
- 外部 API の失敗や遅延に強い運用設計を学ぶため
使用技術
プログラミング言語
バックエンド / 外部サービス
主な機能
- note RSS からの記事取得
- 記事のランダム選択
- Gemini による Threads 向け要約生成
- Gemini の複数モデルフォールバック
- Gemini 一時エラー時の指数バックオフ再試行
- Threads への親投稿作成
- リンク付きモード時のリプライ投稿
- Threads コンテナ状態確認後の publish
- Threads publish 失敗時の再試行
- GitHub Actions による定期実行
- 手動実行用の workflow_dispatch
- 失敗時に Actions をエラー終了させるログ設計
システム構成
GitHub Actions を実行基盤とし、Python スクリプトが note RSS、Gemini API、Threads API と連携する構成です。処理はサーバーを常時稼働させるのではなく、スケジュール実行時だけ GitHub Actions 上で起動します。 Gemini API では複数モデルを順番に試すフォールバック方式を採用し、特定モデルの高負荷による失敗を吸収します。Threads API では、投稿コンテナ作成後すぐに公開すると `Media Not Found` が発生することがあるため、コンテナ状態の確認と publish の再試行を行います。
データフロー
工夫した点
- GitHub Actions の混雑を避けるため、毎時 0 分ではなく 17 分に実行するようにした
- Gemini の 503 や 429 などの一時エラーに対して指数バックオフで再試行するようにした
- 複数の Gemini モデルを設定できるようにし、失敗時は次のモデルへフォールバックする設計にした
- Threads API のコンテナ状態を確認してから publish するようにした
- Threads publish 時の `Media Not Found` に対して再試行処理を追加した
- 投稿失敗時に GitHub Actions が成功扱いにならないよう、終了コードを明確にした
- ログから RSS、Gemini、Threads のどこで失敗したか分かるようにした
- リンクあり投稿では本文に誘導文を入れ、リンクはリプライに分離する構成にした
技術的ハイライト
Gemini モデルフォールバック
GEMINI_MODELS に複数モデルを指定し、上位モデルから順に試行します。特定モデルが高負荷の場合でも、次のモデルへ切り替えて投稿処理を継続できるようにしました。
DEFAULT_GEMINI_MODELS = [
"gemini-2.5-flash",
"gemini-2.5-flash-lite",
"gemini-2.0-flash",
]
def get_gemini_models():
raw_models = os.getenv("GEMINI_MODELS", "")
models = [model.strip() for model in raw_models.split(",") if model.strip()]
return models or DEFAULT_GEMINI_MODELS.copy()
Gemini 一時エラーへの再試行
503 UNAVAILABLE や 429 RESOURCE_EXHAUSTED など、外部 API の一時的な混雑で起きるエラーは、すぐに失敗扱いにせず指数バックオフで再試行します。
wait_seconds = base_wait_seconds * (2 ** (attempt - 1)) + random.randint(0, 2)
print(f"Retrying Gemini after {wait_seconds} seconds.")
time.sleep(wait_seconds)
Threads publish の安定化
Threads API では、投稿コンテナが FINISHED でも publish 時に Media Not Found が返ることがあります。そのため、publish 処理自体にも再試行を入れ、Meta 側の反映遅延に対応しました。
def is_retryable_threads_publish_error(response_body):
error = response_body.get("error", {})
if error.get("code") != 24:
return False
return error.get("error_subcode") == 4279009
開発情報
開発形態
個人開発
役割
企画、設計、実装、GitHub Actions 設定、API 連携、ログ改善、運用改善
制作期間
約1週間
開発時の注意事項
本プロジェクトは、自分の note 記事を活用した自動投稿の仕組みとして開発しました。外部サービスと連携するため、API の仕様、実行頻度、認証情報の管理、失敗時の挙動に注意して設計しています。
外部サービス利用時の配慮
- note RSS へのアクセスは GitHub Actions の定期実行に限定
- Threads API の仕様に合わせてコンテナ作成と publish を分離
- GitHub Actions の schedule は遅延する可能性がある前提で運用
- API の一時エラーを想定し、再試行処理を実装
認証情報の管理
- API キーやアクセストークンは `.env` または GitHub Secrets で管理
- `.env` はコミット対象にしない
- GitHub Actions では Secrets と Variables を使って値を注入
- ログに機密情報が出ないように注意
投稿品質と運用
- 投稿内容は Gemini による生成のため、プロンプトで文体と構成を制御
- リンクあり / なしをランダムに切り替えて投稿パターンを分散
- 失敗時は Actions のログから原因を確認できるようにした
- 同一記事が再投稿される可能性があるため、必要に応じて履歴管理の追加を検討
謝辞
- note RSS: 記事情報の取得
- Google Gemini API: 投稿文の要約生成
- Meta Threads API: Threads への投稿
- GitHub Actions: 定期実行基盤
- Python ライブラリ各種: API 連携と RSS 解析