はじめに

ブログを更新するたびに手動でSNSに投稿するのは面倒ですよね。今回、このブログに新しい記事を投稿すると自動的にX(旧Twitter)にも投稿される機能を実装しました。一見簡単そうに見えるこの機能ですが、実装には予想以上の困難がありました。本記事では、その実装過程で遭遇した問題と解決方法を詳しく共有します。

実装しようとしたこと

要件

  • ブログに新しい記事を追加したら、自動的にXに投稿
  • 投稿内容は記事のタイトル、説明、タグ、URLを含む
  • 重複投稿を防ぐ仕組み
  • ドラフト記事は投稿対象外
  • 必要に応じて自動投稿をスキップできる

使用したサービス・技術

  • GitHub Actions: 自動化のワークフロー実行環境
  • X API v1.1: ツイート投稿API(無料プラン)
  • twitter-api-v2: Node.js用のX APIクライアントライブラリ
  • Astro: ブログの静的サイトジェネレーター

遭遇した困難と解決方法

1. ESモジュールエラー

問題: 最初の実行で以下のエラーが発生

ReferenceError: require is not defined in ES module scope

原因: プロジェクトが"type": "module"を使用しているため、CommonJSのrequireが使えない

解決方法:

  • ファイル拡張子を.jsから.mjsに変更
  • requireimport文に書き換え

2. 認証エラー(401 Unauthorized)

問題: APIキーを正しく設定したはずなのに、401エラーが続く

原因の調査過程:

  1. GitHub Secretsの設定確認
  2. X Developer Portalでの権限確認
  3. Access Tokenの再生成
  4. API v2とv1.1の違いを調査

真の原因:

  • アプリが「Standalone Apps」として作成されていた
  • Standalone AppsはX API v2の書き込み権限がない
  • アプリ権限を「Read and write」に変更後、Access Tokenを再生成していなかった

解決方法:

  1. X Developer Portalで権限を確認・変更
  2. 重要: 権限変更後は必ずAccess Tokenを再生成
  3. v1.1 APIへのフォールバック処理を実装

3. ファイル変更検出の問題

問題: GitHub Actionsで新規ファイルが検出されない

原因:

  • fetch-depth: 2で履歴が不十分
  • 手動実行(workflow_dispatch)時の考慮不足

解決方法:

  • fetch-depth: 0に変更して全履歴を取得
  • 手動実行用の特別な処理を追加

4. URLの不一致

問題: ツイートに含まれるURLがsemiramisu.github.ioになっていた

原因: GitHub Pagesのデフォルトドメインを使用していた

解決方法: 実際のドメインsemiramisu.comに修正

実装のポイント

SHA256ハッシュによるURL生成

ブログではファイル名からSHA256ハッシュを生成してURLを作成しています:

const fileName = path.basename(file, '.md');
const slug = crypto.createHash('sha256').update(fileName).digest('hex').substring(0, 8);
const postUrl = `https://semiramisu.com/posts/${slug}`;

重複投稿防止

.github/tweeted/ディレクトリに投稿履歴を保存:

fs.writeFileSync(tweetedFile, JSON.stringify({
  tweetId: tweet.id_str,
  timestamp: new Date().toISOString(),
  file: file,
  title: postTitle
}, null, 2));

自動投稿のスキップ

コミットメッセージに[skip-tweet]を含めることで自動投稿をスキップ:

if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && !contains(github.event.head_commit.message, '[skip-tweet]'))

X API設定の注意点

必要な認証情報(OAuth 1.0a)

  1. API Key(Consumer Key)
  2. API Key Secret(Consumer Secret)
  3. Access Token
  4. Access Token Secret

重要な注意事項

  • Free tierではv1.1 APIのみ使用可能(Standalone Apps)
  • アプリ権限は「Read and write」が必須
  • 権限変更後は必ずAccess Tokenを再生成
  • 月間投稿数は1,500件まで(無料プラン)

この記事の作成過程

実は、この記事自体もAIアシスタント(Claude)との対話を通じて作成されました。興味深いのは、以下のような流れで記事が生まれたことです:

  1. 機能実装の要望: 「ブログ更新時にXに自動投稿したい」
  2. 共同デバッグ: エラーログを共有しながら問題を特定
  3. 段階的な解決: ESモジュール→認証→API版本と順に解決
  4. ドキュメント化: 実装過程を振り返り、記事として整理

特に印象的だったのは、401エラーの原因特定に時間がかかったことです。APIキーの形式確認、デバッグ出力の追加、X Developer Portalの画面情報の共有など、まるでペアプログラミングのような体験でした。

まとめ

X自動投稿機能の実装は、予想以上に多くの落とし穴がありました。特に以下の点が重要です:

  1. X APIのバージョンと制限を理解する(v1.1 vs v2)
  2. 権限変更後は必ずトークンを再生成
  3. エラーメッセージを丁寧に読み、段階的にデバッグ
  4. 公式ドキュメントと実際の動作の違いに注意

この経験が、同じような機能を実装しようとしている方の参考になれば幸いです。自動化によって、ブログ運営がより効率的になることを願っています!

参考リンク

ブログにX(Twitter)自動投稿機能を実装するまでの道のり

著者

semiramisu

公開日

2025 - 06 - 28

ライセンス CC BY-NC-SA 4.0

コメント