GASでX(Twitter)にポストする例|実際に私が利用しているコード 2025/12~

この記事の内容について、業務や開発でお困りの場合は個別に対応できます。

はじめに

WordPressで記事を公開したタイミングで、X(旧Twitter)にも自動投稿したい、という要望はかなり多いです。プラグインでも実現できますが、API制限や将来的な仕様変更を考えると、外部連携を自前で持っておく価値は高いです。

この記事では、WordPress → Google Apps Script(GAS)Web App → X API v2 という構成で、投稿内容をXに自動投稿する仕組みを実装し、そのコードを一つずつ解説します。実運用を想定し、エラーハンドリングやログ保存まで含めた構成です。

ただ動かしたい人は次の記事に行ってください。こっちはコードの解説バージョンで難しいです。
WordPressからXへ自動投稿する最強構成を自作した話 ― プラグインを捨てて、GAS+X APIで“壊れない配信基盤”を作る


全体構成の概要

流れは非常にシンプルです。

  1. WordPress側からHTTP POSTでJSONを送信
  2. GASのdoPost(e)が受信
  3. X API v2(OAuth 1.0a)を使ってポスト
  4. 結果をスプレッドシートにログ保存
  5. JSONでレスポンスを返却

WordPress側は「投稿ID」と「本文テキスト」を送るだけで済むため、REST APIやフックとの相性も良い構成です。


エントリーポイント:doPost(e)

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() });
  }
}

役割

  • GAS Web AppのHTTP POST受付口
  • WordPressから送られたJSONを検証
  • 投稿処理・ログ処理・レスポンス生成を統括

ポイント

  • e.postData.contents が空の場合は即エラー
  • JSON.parse失敗もcatchでまとめて処理
  • 成功・失敗いずれもログを残す
  • WordPress側が扱いやすいJSONレスポンスを返却

Web Appを「APIサーバ」として扱う前提の、堅実な構造です。


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'
  };

認証方式について

X API v2でも「ツイート投稿」は OAuth 1.0a が必要です。Bearer Tokenでは投稿できません。この点で詰まる人が非常に多いです。

ここでは以下をScriptPropertiesに保存して使用しています。

  • X_API_KEY
  • X_API_SECRET
  • X_ACCESS_TOKEN
  • X_ACCESS_SECRET

署名ベース文字列の生成

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

OAuth 1.0aでは、

  • HTTPメソッド
  • URL
  • 全OAuthパラメータ(アルファベット順)

を結合した「署名ベース文字列」を作成します。この順序が一つでも違うと認証エラーになります。


署名生成とAuthorizationヘッダ

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);

GAS標準のcomputeHmacSignatureを使えば、外部ライブラリ不要で署名を生成できます。

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

最終的にこのAuthorizationヘッダをUrlFetchApp.fetchで送信します。


投稿リクエストとレスポンス検証

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;
  • muteHttpExceptions: true でエラー本文を取得
  • 投稿成功時は 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
  ]);
}

なぜログを残すのか

  • WordPress側の再送防止
  • 投稿失敗の原因追跡
  • API制限・凍結時の証跡

自動化するほど「失敗したときの可視性」が重要になります。


JSONレスポンスの共通化

function json(obj) {
  return ContentService
    .createTextOutput(JSON.stringify(obj))
    .setMimeType(ContentService.MimeType.JSON);
}

WordPress側で fetchwp_remote_post を使う場合、JSONで返すのが最も扱いやすいです。


単体テスト用関数

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

Web Appを叩かなくても、

  • 認証
  • 署名
  • 投稿

が正しく動くかを即座に確認できます。


まとめ

この構成の強みは以下です。

  • WordPressとXを疎結合にできる
  • プラグイン依存がない
  • 投稿ログを確実に残せる
  • 将来BlueskyやThreadsへも横展開しやすい

「自動投稿」は小さな仕組みに見えて、運用フェーズで差が出ます。GASを中継点にする構成は、個人運用から業務利用まで十分に耐える選択肢です。


Zidooka!では、X(旧Twitter)への自動投稿を含む業務自動化の開発も承っております。
本記事で紹介している構成をそのままお渡しし、セットアップまで含める場合は目安として1万円前後です。
運用状況に応じた例外処理やログ設計、WordPress側との連携調整など、細かなチューニングをご希望の場合は内容に応じてお見積りいたします。

ご興味がありましたら、
main@zidooka.com(山口)
まで、お気軽にご連絡ください。

ZIDOOKA!

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

この記事に関連する技術トラブルや開発上の問題について個別対応を行っています。

個別対応は3,000円〜 内容・工数により事前にお見積りします
最後までお読みいただきありがとうございました

コメントを残す

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

AI活用に関するポリシー

当サイトでは、記事の執筆補助にAIを活用する場合がありますが、全面的な委任は行いません。