Back to tech

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

4 min read
Table of Contents

お久しぶりです。

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

前提

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

n8n のバージョン

Version 1.104.1

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

n8n 上で OAuth2 認証する

一番大事な工程です。

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

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

Client IDClient 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上でコンテンツを投稿できるようになります。

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

参考