記事が公開されたらAIが書いた文章を飛んできたGmailで確認し承認するだけでFacebookに投稿される仕組みをn8nで作った話

ブログを公開すると、X(旧Twitter)には自動で投稿文やハッシュタグ、ブログカードが届くようにして、本文確認、投稿ボタンを押すだけのリンクがGmailに送られるようになっていました。届いたメールのリンクをタップするだけで投稿画面に自動で入力されてあとは投稿を押すだけ、というやつです。全自動投稿のAPIは有料だったので、n8nをつかって無料でできる範囲では最適解かなっておもってます。

このX投稿フローは前に作った仕組みなんですが、実はFacebookページの方は長らく手動のままでした。投稿ページに行って、コピペしたり文章考えて・・・

正確には「一応自動化しようとした」んですが、何回もつまづいてちょと放置してたんです。でもFACEBOOKでもページ作って投稿していきたいっておもって、CLAUDE君(Claude Code)と一緒に作業しながら、「よし直った!投稿できるかも」→「あれ、また投稿されてない…」を5回くらい繰り返して、始めた時はもっと簡単に設定できると思ってました( ゚д゚)

今回はその全記録を書きました。なんかいも失敗した部分のつまりポイントが、参考になればと思います。

スポンサーリンク

Before → After

Before(つらかったこと)

  • ブログを書き終えたあと、Facebook用の投稿文をまた考える気力が残っていない
  • X用とFacebook用で文字数も文体も変えたいのに、毎回両方書くのが二度手間
  • 忘れたまま数日経ってしまうこともある

After(できるようになったこと)

  • ブログを公開すると自動でメールが2通届く
  • Xは届いたリンクをタップして、投稿画面に自動入力されて確認して投稿ボタン押すだけ
  • FacebookはAIが書いた投稿文をGmail上で確認して「✅ 投稿する」ボタンを押すだけ

…という状態に、ようやく先週たどり着きました。ようやく、というのがポイントです。ここまで長かった。。。。

そもそもの始まり:トークンを直書きしていたら切れて詰まった話

最初、FacebookのPage Access Token(投稿権限を持つ認証情報)をn8nのワークフローに直接貼り付けて運用しようとしてました。「テスト投稿でうまくできたし多分これで動くようになるだろう」で放置してたら、ほんと1週間くらいでFacebookに何も投稿されなくなっていることに気づいたんです。

「あれ、なんで投稿されてないの?」ってなって調べたら、トークンには有効期限があって、それがとっくに切れていました。しかも直書きだと切れたことに気づく手段もない。

これは、何回もトークンきれたら再発行して、また書いて、また発行して・・・めんどくさーーーーーい(● ˃̶͈̀ロ˂̶͈́)੭ꠥ⁾⁾ なと思って、n8nのCredentials(認証情報の保存場所)にちゃんと登録し直しました。ワークフローの中に直接書くのではなく、Credentialsとして一元管理する形です。これで少なくとも「どこに何のトークンがあるか」は把握できるようになりました。

でも、これはまだ序章に過ぎませんでした。

次の野望:AIに文章を書かせて、確認してから投稿したい

作ったn8nのフローは当初X投稿だけのフローでしたが、条件分岐させてFacebookの投稿も同時でできるようにしようと。WPのメタフィールドにFacebookの投稿文用の文章を用意してそれを投稿にもっていくっていう感じで自動投稿する仕組みを最初作ったんですが、正直自分で書く文章が短すぎることに気づきました。Xは140文字制限があるから短くて当然なんですが、Facebookはもっと長く書けるし、興味がある人のフィードに出るようなキーワードもちゃんと入れたいなぁって。 

なので方針を変えることにしました。

  • 記事を公開したら、AIが記事の内容を前取得して、GeminiでFacebook向けの投稿文(200〜400文字)を自動生成する 文字数は調整可能。
  • ただしAIに丸投げで即投稿はさすがに怖いので、メールで内容を確認してから、ボタンを押して初めて投稿される半自動フローにする 24時間ボタンが押されなかったらフローは中断するようにもした。

CLAUDE君に「兄貴やっちゃいましょうよ!投稿文はXよりは長く、興味ある人のFacebookフィードに出るようなキーワードをちゃんと抑えるように長文でフローとコード作っちゃってください」と伝えて、実装が始まりました。

仕組みの全体像

n8n(ノーコードで自動化フローが作れるツール)で、こんな流れを作りました。

n8nを自然言語でVScodeで操作できるようになってからほんと楽になりました。

せんせ〜これつくって〜 でぱぱぱぱぱぱぱぴぴぴぴぴ  ってできちゃうんですもん。

あ、ちなみにWebhook(ウェブフック)というのは「あるサービスで何かが起きたら、それをきっかけに別のサービスに自動で通知を飛ばす仕組み」のことです。今回で言うと「WordPressで記事を公開した」がきっかけで、n8nに「公開されたよ」という信号が飛ぶ、というイメージです。

記事を公開(WordPress)
  ↓
n8nのWebhookが起動
  ↓
├─ X用メール送信(クリックするだけで投稿)← 並行して動く
└─ WordPressから記事の抜粋を取得
     ↓
   Geminiに渡してFacebook向けの投稿文を生成
     ↓
   確認メールが届く(生成された投稿文つき)
     ↓
   「✅ この内容でFacebookに投稿する」ボタンを押す
     ↓
   Facebookに自動投稿

実際のフロー画面はこちら

以前は一つ一つのノードを開いて、手で設定、コードをコピペしてました。でもn8nのAPIを繋いだら会話で全部つくってくれます。 

ここまでは順調でした。問題はここからです。

詰まりポイント全記録

①Geminiのモデルが廃止されていた

ある日、Xの確認メールは届くのにFacebookだけ届かなくなりました。n8nの実行ログを見たら、こんなエラーが出ていました。

[GoogleGenerativeAI Error]: models/gemini-1.5-flash is not found for API version v1beta, or is not supported for generateContent.

gemini-1.5-flashというモデルを使っていたんですが、いつの間にか廃止されていたようです。え、そんなことある?ってなりました( ゚д゚)

②新しいモデルに変えたら今度は429エラー

じゃあ最新版だろうとgemini-2.0-flashに変更したら、今度は無料枠がなくて429エラー(リクエスト過多)。あっちを立てればこっちが立たず、という状態でした。

③実績のあるモデルでようやく解決

CLAUDE君に「他のワークフローで実際に動いているGeminiモデルはどれ?」と調べてもらったら、Ankiカード作成用の別ワークフローでmodels/gemini-2.5-flashが問題なく動いていることが判明。これに変えたら無事解決しました。同じAPIキーを使っているはずなのに、モデルによって挙動が全然違うんですね。

④メール本文が空欄になる問題

モデルの問題が解決したと思ったら、今度は確認メールは届くのに投稿文が空欄という新しい問題が発生しました。

これはn8nのAIノード(Basic LLM Chain)の出力キーがoutputだと思い込んでメールテンプレートを書いていたのが原因でした。実際はtextというキーで返ってきていて、存在しないキーを参照していたから空欄になっていたという、地味だけど気づきにくいやつでした。

修正前:{{ $json.output }}
修正後:{{ $json.text }}

この1文字も直さない置き換えだけで直るというのが、逆に見つけるまでが大変でした。。。。

⑤Google CalendarとGmailも巻き添えで止まっていた

Facebookの件を調べている途中、まったく別のワークフロー(Camblyのチケット消え忘れ防止メール)も止まっていることに気づきました。原因は前に別記事で書いたのと同じ、GoogleのOAuth認証がテストモードのままで7日失効していたというやつです。この件は詳しく書くと長くなるので、詳細は関連記事に譲りますが、Google Cloud Consoleで「アプリを公開」して本番モードに切り替えることで解決しました。

あ、ちなみにOAuth(オーオース)というのは「パスワードを直接渡さずに、限定的な権限だけをアプリに許可する認証の仕組み」のことです。Googleの場合、これを審査前の「テストモード」のまま使っていると、許可の有効期限が7日しか持たないという罠があるんですよね。

一つのトラブルを調べていたら、別の場所でも同じ根っこの問題が起きていた、というのはあるあるっぽいです(;´Д`A

⑥Facebookトークンがまた切れた→無期限化に挑戦

Gemini・Google認証と立て続けに直して、いよいよFacebookも直そう、というタイミングで、また同じエラーが出ました。

Error validating access token: Session has expired

さすがにもう「またか」という気持ちでした(ノД`)。ここで根本対策として、トークンを無期限化することにしました。手順はこうです。

  1. Graph API Explorerで、投稿に必要な権限(pages_manage_postspages_read_engagementpages_show_list)付きの短期User Tokenを取得
  2. アクセストークンデバッガーで「アクセストークンを延長」を押すと、60日間有効な長期User Tokenになる
  3. Graph API Explorerに戻り、その長期トークンをセットした状態でme/accountsというクエリを実行すると、対象ページのaccess_token(Page Access Token)が取得できる
  4. これをn8nのCredentialsに設定

デバッガーで確認すると「この長期アクセストークンは期限切れになりません」と表示されて、「よっしゃーーー!!」ってなりました。これでもう安心だと思っていました。

⑦無期限化したはずなのに直っていない、というオチ

数日後、また同じ「Session has expired」エラーが出ました。しかも表示されている失効日時が、前回見たのとまったく同じ。

「あれ、無期限にしたはずでは?」と首をひねって、n8nのCredentials一覧を確認したら、そこには紛らわしい2つのFacebook関連Credentialが並んでいました。

  • 「Facebook Page Token」(Facebook Graph API方式)← 最初にワークフローを組んだ時の古いCredential
  • 「Facebook Page Token Query」(HTTP Query Auth方式)← ワークフローが実際に参照しているのはこっち

なんと、私は毎回見た目に馴染みのある①の方を更新し続けていて、実際にワークフローが使っている②はずっと古いままだったんです。せっかく無期限トークンを取得していたのに、間違った場所に貼っていたので何度やっても直らなかった、という完全に自分のミスでした( ̄∇ ̄)

昔、小学校の時校庭で遊んでたら3回の窓をあけて学年で1、2をあらそうキュートガールが
「おぉ〜〜〜い」って手を振ってて、 え! え!! ってはぁ〜いって手を振ったら後ろの女の子に振ってたっていう・・・

あっ  そっちね・・・・

ていう情景を思い出しちゃったじゃないか・・ƪ(˘⌣˘)ʃ

②の正しい方に貼り直して、ようやく本当に解決しました。

似た名前のCredentialが複数ある場合、どちらが実際にワークフローで使われているかを必ず確認してから更新するようにしないとです。

見た目や慣れだけで判断すると、今回のように何度作業しても直らない沼にハマります。

手を振られたら振り返って確認しましょう!笑

⑧Webhookシークレットの扱いに気づいた話

動作確認のために、Claude君にいって、Webhookを手動で叩いて再送信しようとしたことがあったんですが、その過程でn8nの実行ログに含まれる認証用のシークレット文字列がチャットにそのまま見えてしまうことに気づきました。

シークレットは一度でも見えたら再発行するのが鉄則なので、CLAUDE君と相談して「Webhookをテストしたい時はシークレットを直接扱わず、WordPress側で記事を一旦下書きに戻してからまた公開する」という方法に切り替えました。WordPressの公開処理をトリガーにすれば、Webhookは自然に再送信されるので、シークレットを一切触らずに済みます。

最終的に動いた設定

長々と書きましたが、最終的にちゃんと動いた設定をまとめておきます。同じ沼にハマった人はここだけコピペしてもらえれば大丈夫です。

Geminiのモデル設定

modelName: "models/gemini-2.5-flash"

確認メールの投稿文表示部分

{{ $json.text }}

Facebookトークンの取得手順

  1. Graph API Explorerで権限付き短期User Token取得
  2. アクセストークンデバッガーで延長(無期限化)
  3. me/accountsクエリでPage Access Tokenを取得
  4. n8nの正しいCredential(実際にワークフローが参照している方)に設定

Webhook手動テストの方法

シークレットを直接扱わず、WordPress側で記事を「下書き→再公開」するとWebhookが自然に再送信される

まとめ

振り返ってみると、詰まった原因はだいたい3パターンに分けられる気がします。

サービス側の仕様がいつの間にか変わっていたパターン(Geminiモデル廃止・出力キーの仕様)、認証の期限が知らないうちに切れていたパターン(OAuth7日ルール・Facebookトークンの短期失効)、そして一番情けないのが自分の設定ミスパターン(違うCredentialをずっと更新し続けていた)です。

特に最後の「違う方を更新していた」は完全に自分のうっかりなんですよね。無期限化の手順自体は最初からずっと正しかったのに、気づくのに何日もかかりました。似たような名前のものが並んでいるときは要注意ですよ、本当に要注意(;´Д`A

今はブログを公開すると、Xはリンクをタップして投稿画面で投稿ボタン、Facebookは確認して「✅」を押すだけの状態に持ってくことができました。

手動で毎回文章を考えていた頃を思うと、地味にすごく楽になりました。同じような沼にハマっている人の助けになれば嬉しいです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です