「自動投稿」は投稿ボタンを押さないだけじゃない――12言語SNS・メルマガを止めずに回す配信自動化の設計

読書機能の使い方

聴く:本文を読み上げます。速読:語句を順に表示し、速さを調整できます。語学練習:別の言語版と対訳を読み比べます。保存:このブラウザーにブックマークし、プレイヤーの保存済み一覧から開けます。

この記事をシェア
広告
広告

SNS自動化というと、「毎朝9時に投稿する」「AIに投稿文を書かせる」くらいの話になりがちだ。

でも本当に難しいのは投稿ボタンではない。

正しい記事だけを配る。二重投稿しない。1サービスが止まっても全停止しない。CAPTCHAを無理やり突破しない。人間にしかできない仕事だけ人間へ渡す。そして、配った後の結果から次の配り方を変える。

ここまでつながって、ようやく「配信が自動化された」と言える。

12言語、複数SNS、メルマガまで扱うなら、配信はもはや予約投稿機能ではない。小さな分散システムである。

1. 完成形は「完全無人」ではない。「人間が例外だけ処理する」

自動化の目標を「人間が一切触らない」にすると、設計がおかしくなる。

現実の外部サービスには、人間であることを確認するCAPTCHA、SMS認証、2FA、本人確認、利用規約への明示同意、開発者アプリの審査などがある。これは故障ではない。サービス側が意図的に「ここは本人を通す」と決めた境界だ。

だから完成形はこうなる。

記事生成 → 品質確認 → 各言語版 → 本番公開 → 本番HTMLの読み戻し → 配信候補 → 各言語の投稿文 → SNS・メルマガ → 実績回収 → 次回の配信判断

途中で人間しか処理できない箇所が来たら、その1件だけ人間へ渡す。

たとえば日本語XだけCAPTCHAなら、日本語Xだけ待つ。英語Bluesky、フランス語Facebook、メルマガ、アクセス分析、次の記事生成まで一緒に正座させる必要はない。

CAPTCHA 1個で12言語全部が喪に服す必要はない。

Cloudflare Workflowsのようなdurable workflow製品も、長時間の状態保持、失敗ステップの再試行、外部イベントや人間承認待ちを想定している。 大事なのは特定製品を使うことではなく、「人間待ちはシステム停止ではなく、状態の一つ」として持つことだ。

2. 「記事ができた」と「配ってよい」を分ける

配信レイヤーを記事生成と直結すると事故りやすい。

Markdownが保存された。翻訳が終わった。ビルドが通った。

どれも「読者が今そのURLを正しく読める」とは限らない。

だから配信可能な単位を、記事名ではなく、

articleId × locale × contentSha

で持つ。

さらにSNSでは、

articleId × locale × contentSha × platform × campaignType

まで細かくする。

ここで重要なのがcontentShaだ。同じURLでも本文が更新されれば別revisionである。古い本文に対して作った投稿文を、新しい本文の告知として流してはいけない。

そして配信へ渡す条件は「GitHubにある」ではなく、そのlocale、そのcontentShaの本番HTMLを実際に読み戻して確認済みであること。

投稿自動化の入口に必要なのは「原稿がある?」ではなく「読者が今開ける完成物がある?」だ。

3. タイムアウト=失敗、ではない。ここを間違えると双子投稿が生まれる

外部APIで怖いのは、明確なエラーより曖昧なエラーだ。

投稿APIへ送信した。こちらはタイムアウトした。返事がない。

このとき、

「失敗したっぽい。もう一回送ろう」

とやると、1回目が実は成功していた場合に同じ投稿が2つ生まれる。

APIが返事しなかったので念のため同じ記事を3回投稿しました、は自動化界の怪談である。

Cloudflare Queuesは既定でat-least-once deliveryを採用しており、まれに同じメッセージが複数回配送され得る。そのため公式ドキュメントも、一意IDやidempotency keyを使った重複排除を勧めている。

そこで配信ごとに決定的なキーを作る。

sha256(articleId + locale + contentSha + platform + campaignType)

受領テーブル側はこのキーをUNIQUEにする。

タイムアウトしたら即再投稿ではなく、

  1. 受領記録を確認する
  2. provider側で投稿済みか読み戻せるなら確認する
  3. 未投稿と確認できた場合だけ再試行する

という順にする。

Queueは「絶対1回だけ実行される魔法」ではない。重複しても結果を1回分に収束させるのが設計だ。

4. 障害は「システム全体」ではなく「最小scope」に閉じ込める

自動化で一番もったいないのは、1件のエラーを全体障害へ昇格させることだ。

最低でもfailure domainを、

platform × locale × account

に分ける。

日本語Xで認証切れなら、そこだけBLOCKED。

英語Blueskyが429なら、そこだけRETRY_WAIT。

あるFacebook Pageが本人確認待ちなら、そこだけHUMAN_ACTION_REQUIRED。

他は動く。

状態も「成功 / 失敗」の2値だけでは足りない。

  • READY
  • ACTIVE
  • DEGRADED_BUT_RUNNING
  • HUMAN_ACTION_REQUIRED
  • BLOCKED_PROVIDER
  • RETRY_WAIT
  • DISABLED_BY_POLICY

くらいに分けた方が現実を表現できる。

全15系統のうち14系統が動いて1系統だけ人間待ちなら、overallは「全停止」ではない。DEGRADED_BUT_RUNNINGに近い。

429に根性論は通じない。Retry-Afterがあるなら従う。5xxは上限付き指数バックオフ。401/403は正規のcredential refresh経路があるなら一度修復し、それでも駄目なら対象アカウントだけ止める。

CAPTCHAや人間確認は自動retry loopに入れない。100回叩いて人間に進化するAPIはない。

5. Human Handoff Queueは「助けて」ではなく作業指示書にする

人間へ渡す仕組みが雑だと、自動化の最後に巨大な手作業が残る。

悪い引き渡しはこれだ。

「SNSが止まっています。確認してください。」

何を? どこで? ログインだけ? 設定変更も必要? 終わったらどうする?

正しいhandoffは、1件ごとに最低限、

  • platform
  • locale
  • account
  • blockerType
  • 検出時刻
  • 再試行可能時刻
  • 開くURL
  • 人間がやること
  • やってはいけないこと
  • 完了後に自動系が何を再確認するか
  • このblockerが止めるscope
  • 他作業を継続できるか

を持つ。

たとえば、

「この言語の公式アカウントでログインし、表示中のCAPTCHAだけ完了してください。投稿設定やプロフィールは変更しないでください。完了後は次回巡回で認証状態を読み戻し、canary投稿から再開します。」

なら、一回で分かる。

そして重要なのは、CAPTCHAを解くことを自動化しないことだ。

solverを使う、challengeを偽装する、非公式経路で回避する。これは自動化ではなく、相手が設定した境界を破る方向に行っている。

人間は毎日の投稿担当から外し、機械が正当に越えられない境界だけを引き受ける。

パスワード、OAuth access/refresh token、session cookie、SMS/2FA code、recovery code、private API secretはGitHubや通常ログへ残さない。GitHubへ残すのはpublic handle、状態、sanitized error、receipt ID、public post URLなど、再開に必要な非秘密情報だけにする。

6. 自動投稿とengagement botを混ぜない

公式アカウントが自サイトの記事を自動投稿することと、いいね・フォロー・返信・DMまで自動化することは別問題だ。

Xの2026年4月版Automation Rulesは、ルールに従った有用・情報提供目的の自動投稿を認める一方、APIのrate limit回避、Webサイトをスクリプト操作する非API自動化、スパム的・重複的な投稿を禁じている。また自動いいねは禁止されている。

だから初期値は地味なくらいでよい。

  • AUTO_PUBLISH = true
  • AUTO_LIKE = false
  • AUTO_FOLLOW = false
  • AUTO_UNFOLLOW = false
  • AUTO_REPLY = false
  • AUTO_DM = false

まず「公式記事を正しく届ける」だけに責任範囲を絞る。

Blueskyも公式APIで投稿を作成でき、投稿にはlanguage情報を持たせられる。 つまり、多言語運用では「英語投稿文を全部へ投げる」より、localeごとに本文とlanguage metadataを合わせる設計が自然だ。

ブラウザ自動操作はアカウント初期設定など、公式APIがない・人間操作が許されている局面の補助に限定する。定常配信は、可能な限り公式APIと公式認証へ寄せる。

初期チャネルは、たとえば ja→X、en→X/Bluesky、ko→X、zh-Hans→Weibo、zh-Hant→Facebook/Threads、es・pt-BR・id・th・vi・fr→Facebook、de→Facebook/X のようにlocaleごとに決める。Instagram、TikTok、Reelsのような画像・動画中心の面は、カード生成とmedia QAが安定してから第二段階へ回す。プラットフォームのAPI・Automation Policy・利用条件は実装時点で必ず再確認する。

7. 12言語運用は「日本語投稿の翻訳大会」にしない

多言語サイトでありがちな事故がある。

日本語記事を12言語へ翻訳した。 日本語のSNS文を1個作った。 それも11言語へ翻訳した。 完成。

これでは本文を12言語化した意味が薄い。

投稿文はそのlocaleの実際の公開本文を読んで、その言語のサイト公式アカウントが一言漏らすように作る。

基本形は短い。

これ、地味に一番困るやつ。🫠 URL

あるいは、

そこ自動化できるんだ。👀 URL

長い要約や「必見」「衝撃」「今すぐチェック」は要らない。サイト自身の投稿なのに、第三者が偶然見つけて感動したようなfake testimonialを作るのもおかしい。

ブランド人格は共通にしつつ、言語ごとに自然な文章にする。別人が運営しているように偽装しない。

さらにmedical、legal、investment、大きなお金、災害、犯罪、死亡、自傷、暴力、性被害、未成年、安全、政治・選挙、強い対立はserious modeへ切り替える。

その場合は原則emojiなし、煽りなし、記事以上の断定なし。政治記事ならendorsementを自動で足さない。

「ネタ全開」は万能設定ではない。救急車の記事に🫠を置く必要はない。

8. メルマガは「SNSのメール版」ではなく、同じ配信思想を別adapterへ載せる

メルマガも本文生成から直接sendしてはいけない。

最低限、

  • explicit opt-in
  • locale
  • topics
  • consentAt
  • status
  • unsubscribeAt
  • createdAt

を持ち、同じ記事を何度も送らない。

問い合わせ用メールアドレスと大量配信の仕組みも論理分離する。

読者からの返信先は既存窓口を使えても、送信基盤は後から専用providerへ交換できるadapterにしておく。

Gmailは大量送信者に対し、送信認証、迷惑・未承諾メールの回避、解除しやすい仕組みを要求しており、subscription messageではunsubscribeを重要な要件としている。

購読解除は「次回バッチでたぶん止まります」ではなく、即時に配信対象から外す。

メルマガの価値はアドレスを集めることではない。読者が頼んだ情報を、頼んだ頻度で、やめたい時にすぐやめられることだ。

9. KPIを「いいね」にすると、いいねを取りに行く機械が完成する

自動改善を入れるなら、何を目的関数にするかが重要だ。

SNSのいいね数だけを最大化すると、刺激の強いタイトル、極端な言い切り、炎上寄りのネタが勝ちやすくなる。

でも記事サイトで本当に欲しいのは別だ。

優先順位をたとえば、

  1. site visit
  2. meaningful reading
  3. next article
  4. return visit
  5. newsletter signup

とする。

「その投稿でサイトへ来たか」「ちゃんと読んだか」「次の記事へ進んだか」「また来たか」を重く見る。

配信campaignも、

  • NEW
  • UPDATED
  • TRENDING
  • POPULAR
  • EVERGREEN

に分ける。

誤字を1文字直しただけで「大型アップデート!」と再投稿しない。UPDATEDはmaterial updateだけ。

POPULARやTRENDINGも、既存の実アクセス計測があるならそれを再利用する。配信用に別の人気ランキングを作り、数字が二つになって戦争を始める必要はない。

配信時間も「日本語だから20時」のような固定観念ではなく、実際の locale × country × platform × weekday × hour を見て探索する。最初は複数slotを試し、データがたまってから寄せる。

10. 実際の使い方――「投稿する」ではなく、状態を次へ進める

実運用は次の一連の状態遷移として考えると分かりやすい。

  1. 対象localeの記事が本番公開される。
  2. exact contentShaの本番HTMLを読み戻し、PRODUCTION_VERIFIEDにする。
  3. articleId × locale × contentSha × platform × campaignTypeで配信候補を作る。
  4. そのlocaleの本文を読んで短い投稿文を生成する。
  5. hype禁止、serious mode、長さ、URL、言語を検査する。
  6. idempotency key付きでoutboxへ入れる。
  7. providerの公式APIから送る。
  8. API receiptだけでなく、可能ならpublic postを読み戻す。
  9. サイト流入・読了・次記事・再訪を回収する。
  10. NEW / UPDATED / TRENDING / POPULAR / EVERGREENの次回判断へ使う。

途中でCAPTCHAが出たら、そのscopeだけHUMAN_ACTION_REQUIREDへ。

429ならRETRY_WAITへ。

providerが落ちていればBLOCKED_PROVIDERへ。

他は進む。

最初から15アカウントへ一斉射撃もしない。adapterごとに、

auth readback → dry run → 実記事1件のcanary → receipt → public readback → locale確認 → duplicate suppression確認 → analytics attribution確認

まで通してから広げる。

そして運用者が見る入口は、一つのcurrent-statusにまとめる。

実装上も配信用に第二の記事工場・第二schedulerを生やすより、PRODUCTION_VERIFIED後のpost-publication childとして既存publication ownerへ接続し、既存のdurable runtime/state storeを再利用する方が状態の正本を増やしにくい。

「今どうなってる?」に対して、15個のJSONとログを人間に発掘させたら、その時点で自動化が人間へ仕事を返している。

自動配信で一番大切なのは、「何も失敗しないこと」ではない。

失敗しても、どこが止まったか分かり、止まる範囲が小さく、人間にしかできない部分だけ人間へ渡り、残りは勝手に進み、復旧後は二重実行せず続きから戻れること。

投稿ボタンを消すのは序章だ。

本当の自動化は、失敗した日の運用まで自動化することである。


PRこのテーマの本を探す

この記事には広告(アフィリエイトリンク)が含まれます。 広告について

今日これ読んで

この記事を読んだ人の次の疑問に、それぞれ答える記事です。

すべての記事から探す「テクノロジー」の記事をもっと見る

広告

他の記事を探す

すべての記事

めんどいちゃん

この記事を書いた人

めんどいちゃん

仕事や日常の「めんどい」を構造化して、次に動きやすくする記事を書いています。

このサイトについて
広告

新着記事

  1. 1『氷の城壁』『氷の城壁』小雪は冷たいんじゃないの。中で味噌が発酵してるのよ――「分かるわお姉さん」と読む反芻・脳内予想・境界線
  2. 2面接で「圧が強い人がいても大丈夫?」を3回聞かれたら、もう会社説明になってない?
  3. 350年住宅ローンは「家を安くする魔法」ではない――借金ゲーム・金利上昇・金融庁の監視を小学生でも分かるように解剖する
  4. 4『ラヴ上等』『ラヴ上等2』、恋愛に24時間SLAを持ち込むな――Awichは全部知ってる、たいちゃんの耳はHPゲージ、最後はモモンガが「よこせッ!」
  5. 5『ちいかわ』装備:モモンガ、属性:六角堂帰り――人類は他人の「設定資料集」を勝手に書く
広告