コンテンツにスキップ
Zidooka
広告 Amazon Prime Day
スポンサーリンク

WordPressからXへ自動投稿する最強構成を自作した話 ― プラグインを捨てて、GAS+X APIで“壊れない配信基盤”を作る

※ この記事の内容について、業務・開発上お困りの場合は個別に対応できます(5,000円〜)。

WordPressからX(旧Twitter)への自動投稿。 これを実現するために、多くの人がプラグインを使っていると思います。

しかし、長年メディアを運用していると、必ずこの問題に直面します。

  • 「プラグインが突然動かなくなった」
  • 「API制限で投稿が失敗しているが、理由が分からない」
  • 「アップデートで設定が飛んだ」

楽をするためのプラグインが、実は最大の不安定要因になってしまうのです。

今回、私も「WP側のPOSTが働かなくなった」というトラブルに見舞われました。 そこで、既存のプラグインに頼るのをやめ、Google Apps Script (GAS) と X API v2 を組み合わせて、「絶対に壊れない(壊れても自分で直せる)配信基盤」 を自作しました。

これが、個人メディアにおける最強の構成だと確信しています。

設計思想:WordPressは「通知するだけ」

今回のツールの肝は、「WordPressに余計な仕事をさせない」 ことです。

従来のプラグイン構成

  • WordPressの中に「Xの認証情報」を持つ
  • WordPressが「Xへの投稿処理」を行う
  • WordPressが「エラーハンドリング」を行う

これだと、WPが重くなるし、セキュリティ的にも不安です(WPが乗っ取られたらXも乗っ取られます)。

今回の「最強構成」

  • WordPress:記事が公開されたら、GASに「JSON」を投げるだけ。
  • GAS:認証・投稿・ログ保存・再送処理を一手に引き受ける。
  • Spreadsheet:投稿ログを記録し、可視化する。
[WordPress]
    ↓ (POST: Title, URL)
[Google Apps Script] ← ここに秘密鍵を隔離
    ↓ (OAuth 1.0a)
[X API v2]
    ↓
[Spreadsheet] (Log: Success/Fail)

この構成なら、WordPressが侵害されてもXのアカウントは無事です。 また、XのAPI仕様が変わっても、GAS側を1箇所直すだけで済みます。

技術構成

  • WordPress: functions.php に数行のフックを追加
  • Google Apps Script: X API v2 への署名とPOST処理(ライブラリ不使用)
  • X API: Freeプランでも使える v2 API (/2/tweets)
  • 認証方式: OAuth 1.0a(GAS単体で署名を生成)

実装手順:完全ガイド

ここからは、実際にこのツールを構築する手順をステップバイステップで解説します。 プログラミングに詳しくない方でも、手順通りに進めれば完成します。

手順1:ログ保存用のスプレッドシートを作成

まず、Googleドライブで新しいスプレッドシートを作成します。 1行目に以下の見出しを入力してください。

  • A列: time
  • B列: post_id
  • C列: text
  • D列: tweet_id
  • E列: status
  • F列: error

このスプレッドシートのURLに含まれる ID 部分(/d//edit の間の文字列)を後で使うので、メモしておきます。

手順2:GASプロジェクトを開く

スプレッドシートのメニューから 「拡張機能」→「Apps Script」 をクリックします。 これで、このシートに紐付いたGASエディタが開きます。

手順3:X Developer Portal でキーを取得

X Developer Portal にアクセスし、プロジェクトを作成します(FreeプランでOK)。 「Keys and tokens」 タブから、以下の4つの情報を取得します。

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

※ Access Token を生成する際は、必ず 「Read and Write」 権限が付与されていることを確認してください。

手順4:スクリプトプロパティの設定

GASのエディタに戻り、左側の歯車アイコン(プロジェクトの設定)をクリックします。 一番下にある 「スクリプト プロパティ」 に、先ほど取得したキーと、手順1でメモしたシートIDを設定します。

プロパティ名
X_API_KEY (取得したAPI Key)
X_API_SECRET (取得したAPI Key Secret)
X_ACCESS_TOKEN (取得したAccess Token)
X_ACCESS_SECRET (取得したAccess Token Secret)
LOG_SHEET_ID (スプレッドシートのID)
スクリプトプロパティの設定

手順5:コードの貼り付け

左側の「エディタ(<>アイコン)」に戻り、Code.gs の中身を以下のコードにすべて書き換えます。 外部ライブラリは一切不要です。

/**
 * WordPress → GAS Web App 入口
 */
function doPost(e) {
  try {
    if (!e || !e.postData || !e.postData.contents) {
      throw new Error('No postData');
    }

    const data = JSON.parse(e.postData.contents);

    if (!data.text) {
      throw new Error('No text');
    }

    const tweetId = postToX(data.text);

    logPost({
      post_id: data.post_id || '',
      text: data.text,
      tweet_id: tweetId,
      status: 'success',
      error: ''
    });

    return json({ ok: true, tweet_id: tweetId });

  } catch (err) {

    logPost({
      post_id: '',
      text: '',
      tweet_id: '',
      status: 'error',
      error: err.toString()
    });

    return json({ ok: false, error: err.toString() });
  }
}

/**
 * X(Twitter)へ投稿
 */
function postToX(text) {
  const props = PropertiesService.getScriptProperties();

  const url = 'https://api.twitter.com/2/tweets';
  const method = 'POST';

  const oauth = {
    oauth_consumer_key: props.getProperty('X_API_KEY'),
    oauth_token: props.getProperty('X_ACCESS_TOKEN'),
    oauth_nonce: Utilities.getUuid(),
    oauth_timestamp: Math.floor(Date.now() / 1000),
    oauth_signature_method: 'HMAC-SHA1',
    oauth_version: '1.0'
  };

  const baseString =
    method + '&' +
    encodeURIComponent(url) + '&' +
    encodeURIComponent(
      Object.keys(oauth)
        .sort()
        .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(oauth[k])}`)
        .join('&')
    );

  const signingKey =
    encodeURIComponent(props.getProperty('X_API_SECRET')) + '&' +
    encodeURIComponent(props.getProperty('X_ACCESS_SECRET'));

  const signatureBytes = Utilities.computeHmacSignature(
    Utilities.MacAlgorithm.HMAC_SHA_1,
    baseString,
    signingKey
  );

  oauth.oauth_signature = Utilities.base64Encode(signatureBytes);

  const authHeader = 'OAuth ' + Object.keys(oauth)
    .sort()
    .map(k => `${encodeURIComponent(k)}="${encodeURIComponent(oauth[k])}"`)
    .join(', ');

  const res = UrlFetchApp.fetch(url, {
    method: 'post',
    headers: {
      Authorization: authHeader,
      'Content-Type': 'application/json'
    },
    payload: JSON.stringify({ text }),
    muteHttpExceptions: true
  });

  const body = JSON.parse(res.getContentText());

  if (!body.data || !body.data.id) {
    throw new Error(res.getContentText());
  }

  return body.data.id;
}

/**
 * スプレッドシートにログ
 */
function logPost(row) {
  const sheetId = PropertiesService
    .getScriptProperties()
    .getProperty('LOG_SHEET_ID');

  if (!sheetId) return;

  const sheet = SpreadsheetApp
    .openById(sheetId)
    .getSheets()[0];

  sheet.appendRow([
    new Date(),
    row.post_id,
    row.text,
    row.tweet_id,
    row.status,
    row.error
  ]);
}

/**
 * JSONレスポンス
 */
function json(obj) {
  return ContentService
    .createTextOutput(JSON.stringify(obj))
    .setMimeType(ContentService.MimeType.JSON);
}

/**
 * 単体テスト用
 */
function testPost() {
  postToX('GASからのテスト投稿です');
}

手順6:デプロイ(Webアプリとして公開)

右上の 「デプロイ」→「新しいデプロイ」 をクリックします。

  1. 種類の選択: 歯車アイコンから 「ウェブアプリ」 を選択。
  2. 説明: 適当に入力(例: v1)。
  3. 次のユーザーとして実行: 自分 (Me)。
  4. アクセスできるユーザー: 全員 (Anyone) ← ここ重要!
Spreadsheet Preparation

「デプロイ」ボタンを押すと、URL(ウェブアプリURL)が発行されます。 このURLをコピーしておきます(WordPressの設定で使います)。


実装手順:WordPress側の設定

WordPress側は、functions.php に以下のコードを追加するだけです。 記事が「公開 (publish)」された瞬間だけフックし、GASにデータを投げます。

/**<br> * WordPress → GAS → X 自動投稿<br> */<br>add_action('transition_post_status', function ($new_status, $old_status, $post) {<br><br>  // publish になった瞬間のみ実行<br>  if ($old_status === 'publish' || $new_status !== 'publish') return;<br><br>  // 投稿タイプ制限(通常の投稿のみ)<br>  if ($post->post_type !== 'post') return;<br><br>  // 自動保存・リビジョン除外<br>  if (wp_is_post_autosave($post->ID) || wp_is_post_revision($post->ID)) return;<br><br>  // 二重投稿防止(メタデータで管理)<br>  if (get_post_meta($post->ID, '_x_posted', true)) return;<br><br>  // タイトルとURLを取得<br>  $title = trim(wp_strip_all_tags($post->post_title));<br>  if ($title === '') return;<br><br>  $title = mb_substr($title, 0, 100); // 文字数制限<br>  $url   = get_permalink($post->ID);<br><br>  // 投稿文の作成<br>  $text = "【新記事】\n{$title}\n\n{$url}";<br><br>  // GAS Web App URL(先ほど発行したもの)<br>  $gas_url = 'https://script.google.com/macros/s/YOUR_GAS_ID/exec';<br><br>  // GAS に POST<br>  $response = wp_remote_post(<br>    $gas_url,<br>    [<br>      'timeout' => 5,<br>      'headers' => ['Content-Type' => 'application/json'],<br>      'body' => wp_json_encode([<br>        'post_id' => $post->ID,<br>        'text'    => $text<br>      ])<br>    ]<br>  );<br><br>  // 成功したらフラグ保存<br>  if (!is_wp_error($response)) {<br>    update_post_meta($post->ID, '_x_posted', 1);<br>  }<br><br>}, 10, 3);

注意点:functions.php の罠

実装時、functions.php の途中に <?php を二重に書いてしまい、フックが動かないというミスがありました。 functions.php はファイルの先頭に <?php があれば十分です。途中に書くと構文エラーの原因になるので注意しましょう。

運用してわかったメリット

実際にこの構成で運用を始めましたが、安定感が段違いです。

  1. 失敗が見える: スプレッドシートに全てのログが残るため、「いつ」「なぜ」失敗したかが一目瞭然です。
  2. 再送が簡単: 失敗しても、GASを叩き直すだけで再送できます。
  3. 拡張性が高い: 「カテゴリによってハッシュタグを変える」「Blueskyにも同時に投げる」といった拡張が、GAS側だけで完結します。

↑投稿できました!

まとめ

「プラグインを入れる」のは簡単ですが、それは「ブラックボックスを抱える」ことでもあります。 長期的にメディアを運営するなら、「壊れたときに自分で直せる」 構成にしておくことが、結果的に一番の近道になります。

この「WordPress × GAS × X API」の構成は、個人開発者やブロガーにとっての最強の武器になるはずです。 ぜひ試してみてください。

スポンサーリンク

ZIDOOKA!

この記事の内容について、対応できます

ZIDOOKA!では、この記事で扱っている内容に関連する技術トラブルや開発上の問題について、フリーランスとして個別対応を行っています。
「調べても解決しない」「業務で詰まっている」「社内で対応が難しい」といった場合にご相談ください。

個別対応:5,000円〜 (内容・工数により事前にお見積りします)

AI活用に関するポリシー

当サイトでは、記事の執筆支援にAIを活用する場合があります。ただし、AIに記事作成を全面的に委任することはなく、あくまでライティングの補助として利用しています。「AIを活用することで生産性が向上し、より伝わりやすいコンテンツになるならば積極的に利用すべき」という方針のもと運営しています。

One comment on “WordPressからXへ自動投稿する最強構成を自作した話 ― プラグインを捨てて、GAS+X APIで“壊れない配信基盤”を作る”

コメントを残す

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