n8n 上で X v2 API を使った画像付きポストを投稿する

•
4 min read

お久しぶりです。

n8n の X v2 API ノードを使って画像付きポストを投稿する方法を解説します。

前提

  • n8n のセットアップが完了していること
  • X アカウントがあり、API キーとシークレットを取得していること
    • Developer の設定は 過去記事 を参照してください。

n8n のバージョン

Version 1.104.1

セルフホスト版を使っています。

n8n 上で OAuth2 認証する

一番大事な工程です。

n8nで画像付き投稿をする上で、この工程が9割を占めると言っても過言ではありません。

New Credentials をクリックして、X OAuth2 API を選択します。

Client ID と Client Secret を入力します。

そして、 Connect my account をクリックします。

ここが重要なのですが、別ウィンドウでXの認証画面が表示されます。
※ この別ウィンドウは閉じないでください

URLをコピーするなどして取得します。

メモ帳などに、取得したURLを貼り付けます。

貼り付けたURLの末尾に +media.write を追加します。

つまり、以下のような形です。

修正前: https://twitter.com/i/flow/login?redirect_after_login=${略}read%2Blist.read%2Blist.write

修正後: https://twitter.com/i/flow/login?redirect_after_login=${略}read%2Blist.read%2Blist.write+media.write

修正したURLをブラウザで開きます。

OAuth2 の認証画面が表示されるので認証します。

認証されれば、晴れてn8n 上で X v2 API を使う準備が整いました。

作成する

ここまでできれば、あとはワークフローを作るだけです。

複数の画像をアップロードする場合、n8n がサポートしている X ワークフローは使えないため、HTTP Request ノードを使います。
※ 1枚の画像をアップロードする場合は、X ワークフローを使うことができます。使い方は参考記事を参照してください。

サンプルを次に示します。

このサンプルは、フォームから画像をアップロードし、X v2 APIを使って画像付きポストを投稿するワークフローです。

{
  "name": "My workflow 4",
  "nodes": [
    {
      "parameters": {
        "formTitle": "test",
        "formFields": {
          "values": [
            {
              "fieldLabel": "images",
              "fieldType": "file"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [
        0,
        0
      ],
      "id": "f5add65d-37a3-4c6a-b4e5-58b2899c3215",
      "name": "On form submission",
      "webhookId": "38a6e9d9-9741-4d9e-97f4-03975625bfd8"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        528,
        0
      ],
      "id": "83547b73-6513-4f90-8ede-b476f753da55",
      "name": "Loop Over Items"
    },
    {
      "parameters": {
        "jsCode": "const mediaIds = $input.all().map((media)=>{\n  return media.json.data.id\n});\n\nconst text = \"HogeHoge\";\n\nreturn {\n  mediaIds,\n  text\n}"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        784,
        -32
      ],
      "id": "51699128-1847-4aec-a668-f5663553fe1a",
      "name": "Pretreatment"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.x.com/2/media/upload",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "twitterOAuth2Api",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "name": "media_type",
              "value": "={{ $json.mimetype }}"
            },
            {
              "name": "media_category",
              "value": "tweet_image"
            },
            {
              "parameterType": "formBinaryData",
              "name": "media",
              "inputDataFieldName": "data"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        800,
        112
      ],
      "id": "8eda4f3c-a0c1-40af-a92c-e767ba170121",
      "name": "Upload Media to X",
      "credentials": {
        "twitterOAuth2Api": {
          "id": "mB3TR3y1LzKghmao",
          "name": "X account(Staging)"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.x.com/2/tweets",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "twitterOAuth2Api",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"text\": {{ $json.text.toJsonString() }},\n  \"media\": {\n    \"media_ids\": {{ $json.mediaIds.toJsonString() }}\n  }\n} ",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1008,
        -32
      ],
      "id": "e4bb9b72-17df-4f58-a6c1-cf9d9439777f",
      "name": "Publish Post",
      "credentials": {
        "twitterOAuth2Api": {
          "id": "mB3TR3y1LzKghmao",
          "name": "X account(Staging)"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const images = $input.first().json;\nconst binaryData = $input.first().binary;\n\nreturn Object.keys(binaryData).map((binaryKey, index)=>{\n  const image = images.images[index];\n  \n  return {\n    json: {\n      filename: image.filename,\n      mimetype: image.mimetype\n    },\n    binary: {data: binaryData[binaryKey]}\n  };\n});\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        208,
        0
      ],
      "id": "72c5083b-9a36-4d27-928f-4eae894abf79",
      "name": "Setup Loop"
    }
  ],
  "pinData": {},
  "connections": {
    "On form submission": {
      "main": [
        [
          {
            "node": "Setup Loop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Pretreatment",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Upload Media to X",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pretreatment": {
      "main": [
        [
          {
            "node": "Publish Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Media to X": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Setup Loop": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "9478d289-170f-4b87-8a4b-8082d4bdf57e",
  "meta": {
    "instanceId": "f1ea10e2477ea0ca0966ce19137d7a6d60d042c865a530a9bc6a8f603af2cc15"
  },
  "id": "aaNQmw3dwR0aPnn2",
  "tags": []
}

実行

実行するとフォーム画面が表示されるので、任意の画像をアップロードします。

しばらくすると X に画像付きポストが投稿されます。

まとめ

n8n を使って X v2 API を利用し、画像付きポストを投稿する方法を紹介しました。

この方法を使えば、画像のアップロードとポストの作成を自動化でき、X上でコンテンツを投稿できるようになります。

この情報は日本語ではあまり見かけないため、少しでも参考になれば幸いです。

参考