自動投稿システムの処理フロー図

プロジェクト概要

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 の失敗や遅延に強い運用設計を学ぶため

使用技術

プログラミング言語

Python

バックエンド / 外部サービス

GitHub Actions Gemini API Threads API note RSS Google Gen AI SDK python-dotenv feedparser requests

主な機能

  • 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 がスケジュールまたは手動で起動
Python 3.11 環境をセットアップ
必要なライブラリをインストール
note RSS から記事一覧を取得
ランダムに 1 件の記事を選択
リンクあり / なしをランダムに決定
Gemini API で Threads 向け本文を生成
Threads API で親投稿コンテナを作成
コンテナ状態が FINISHED になるまで確認
親投稿を publish
リンクありの場合はリプライコンテナを作成
リプライコンテナ状態を確認
リプライを 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 解析