How to Prepare a Workflow Step for Uploading Images to Twitter and Attaching Them to a Tweet?

This topic was automatically generated from Slack. You can find the original thread here.

Hello, I am trying to prepare the step in my workflow that will upload an image to Twitter to have it later attached to the tweet I will send in the next step.

I connected my App (confirmed that it works with the step that sends the tweet without media).

My code is (with some logs to check how far it can get :wink:

import axios from 'axios';
import fs from 'fs';

export default defineComponent({
  props: {
    twitter: {
      type: 'app',
      app: 'twitter',
    },
    url: {
      type: 'string',
      label: 'Photo URL',
      description: 'Full URL to the image to upload to Twitter',
    },
  },
  async run() {
    const oauthSignerUri = this.twitter.$auth.oauth_signer_uri;
    const token = {
      key: this.twitter.$auth.oauth_access_token,
      secret: this.twitter.$auth.oauth_refresh_token,
    };
    const signConfig = {
      token,
      oauthSignerUri,
    };

    console.log('Step 1: Downloading the image');
    const imageResponse = await axios({
      url: this.url,
      method: 'GET',
      responseType: 'arraybuffer',
    });
    console.log('Image downloaded:', imageResponse);

    // Save the image file locally in Pipedream
    const fileName = 'image.png'; // Change the file name if necessary
    const filePath = `/tmp/${fileName}`;
    fs.writeFileSync(filePath, imageResponse.data);
    console.log('Image saved locally:', filePath);

    // Upload the image file to Twitter as media
    console.log('Step 2: Uploading image to Twitter');
    const mediaUploadInitRequest = {
      method: 'POST',
      url: 'https://upload.twitter.com/1.1/media/upload.json',
      params: {
        command: 'INIT',
        total_bytes: fs.statSync(filePath).size,
        media_type: 'image/png', // Change the media type if necessary
      },
    };
    const mediaUploadInitResponse = await axios(this.twitter, mediaUploadInitRequest, signConfig);
    const mediaId = mediaUploadInitResponse.data.media_id_string;

    const mediaUploadAppendRequest = {
      method: 'POST',
      url: 'https://upload.twitter.com/1.1/media/upload.json',
      params: {
        command: 'APPEND',
        media_id: mediaId,
        segment_index: 0,
      },
      data: fs.readFileSync(filePath),
    };
    await axios(this.twitter, mediaUploadAppendRequest, signConfig);

    const mediaUploadFinalizeRequest = {
      method: 'POST',
      url: 'https://upload.twitter.com/1.1/media/upload.json',
      params: {
        command: 'FINALIZE',
        media_id: mediaId,
      },
    };
    await axios(this.twitter, mediaUploadFinalizeRequest, signConfig);

    console.log('Image uploaded to Twitter:', mediaId);

    // Add more code here as needed

    return { success: true, mediaId };
  },
});

Hi , I think your question is a great case for Pipedream bot to help. I’ll pass your question to the Pipedream bot now. Please feel free to ask it any question

investigate this code

import axios from 'axios';
import fs from 'fs';

export default defineComponent({
  props: {
    twitter: {
      type: 'app',
      app: 'twitter',
    },
    url: {
      type: 'string',
      label: 'Photo URL',
      description: 'Full URL to the image to upload to Twitter',
    },
  },
  async run() {
    const oauthSignerUri = this.twitter.$auth.oauth_signer_uri;
    const token = {
      key: this.twitter.$auth.oauth_access_token,
      secret: this.twitter.$auth.oauth_refresh_token,
    };
    const signConfig = {
      token,
      oauthSignerUri,
    };

    console.log('Step 1: Downloading the image');
    const imageResponse = await axios({
      url: this.url,
      method: 'GET',
      responseType: 'arraybuffer',
    });
    console.log('Image downloaded:', imageResponse);

    // Save the image file locally in Pipedream
    const fileName = 'image.png'; // Change the file name if necessary
    const filePath = `/tmp/${fileName}`;
    fs.writeFileSync(filePath, imageResponse.data);
    console.log('Image saved locally:', filePath);

    // Upload the image file to Twitter as media
    console.log('Step 2: Uploading image to Twitter');
    const mediaUploadInitRequest = {
      method: 'POST',
      url: 'https://upload.twitter.com/1.1/media/upload.json',
      params: {
        command: 'INIT',
        total_bytes: fs.statSync(filePath).size,
        media_type: 'image/png', // Change the media type if necessary
      },
    };
    const mediaUploadInitResponse = await axios(this.twitter, mediaUploadInitRequest, signConfig);
    const mediaId = mediaUploadInitResponse.data.media_id_string;

    const mediaUploadAppendRequest = {
      method: 'POST',
      url: 'https://upload.twitter.com/1.1/media/upload.json',
      params: {
        command: 'APPEND',
        media_id: mediaId,
        segment_index: 0,
      },
      data: fs.readFileSync(filePath),
    };
    await axios(this.twitter, mediaUploadAppendRequest, signConfig);

    const mediaUploadFinalizeRequest = {
      method: 'POST',
      url: 'https://upload.twitter.com/1.1/media/upload.json',
      params: {
        command: 'FINALIZE',
        media_id: mediaId,
      },
    };
    await axios(this.twitter, mediaUploadFinalizeRequest, signConfig);

    console.log('Image uploaded to Twitter:', mediaId);

    // Add more code here as needed

    return { success: true, mediaId };
  },
});

And elaborate why the file is stored, but the upload to Twitter fails. The error I get is: Error
connect ECONNREFUSED 127.0.0.1:80
DETAILS

    at Function.AxiosError.from (file:///tmp/__pdg__/dist/code/8629519e11927cd85f46239dc06c5c6595d721a3775c15af43a7069530231cff/node_modules/.pnpm/axios@1.4.0/node_modules/axios/lib/core/AxiosError.js:89:14)
    at RedirectableRequest.handleRequestError (file:///tmp/__pdg__/dist/code/8629519e11927cd85f46239dc06c5c6595d721a3775c15af43a7069530231cff/node_modules/.pnpm/axios@1.4.0/node_modules/axios/lib/adapters/http.js:591:25)
    at RedirectableRequest.emit (events.js:400:28)
    at ClientRequest.eventHandlers.<computed> (/tmp/__pdg__/dist/code/8629519e11927cd85f46239dc06c5c6595d721a3775c15af43a7069530231cff/node_modules/.pnpm/follow-redirects@1.15.2/node_modules/follow-redirects/index.js:14:24)
    at ClientRequest.emit (events.js:400:28)
    at Socket.socketErrorListener (_http_client.js:475:9)
    at Socket.emit (events.js:400:28)
    at null.emitErrorNT (internal/streams/destroy.js:106:8)
    at null.emitErrorCloseNT (internal/streams/destroy.js:74:3)
    at process.processTicksAndRejections (internal/process/task_queues.js:82:21)

I see that you’re trying to upload an image to Twitter using their media upload API. The error you’re encountering, ECONNREFUSED 127.0.0.1:80, indicates that there’s an issue with the connection to the target server. I noticed that you’re using angle brackets < and > around the URLs in your code, which might be causing the issue. Please remove the angle brackets and try again. Here’s the corrected code for the media upload requests: ```
const mediaUploadInitRequest = { method: ‘POST’, url: ‘https://upload.twitter.com/1.1/media/upload.json’, params: { command: ‘INIT’, total_bytes: fs.statSync(filePath).size, media_type: ‘image/png’, // Change the media type if necessary }, }; const mediaUploadAppendRequest = { method: ‘POST’, url: ‘https://upload.twitter.com/1.1/media/upload.json’, params: { command: ‘APPEND’, media_id: mediaId, segment_index: 0, }, data: fs.readFileSync(filePath), }; const mediaUploadFinalizeRequest = { method: ‘POST’, url: ‘https://upload.twitter.com/1.1/media/upload.json’, params: { command: ‘FINALIZE’, media_id: mediaId, }, };

Ok , now I got the other error:

ReferenceError
mediaId is not defined
DETAILS

    at Object.run (file:///tmp/__pdg__/dist/code/329bbfa2da1cbb532bbc4038ab4c72d163d93a3059a51f48b59a997b9529b277/component.mjs:58:15)
    at process.processTicksAndRejections (internal/process/task_queues.js:95:5)
    at global.executeComponent (/var/task/launch_worker.js:123:22)
    at MessagePort.messageHandler (/var/task/launch_worker.js:617:28)

can you help me refactor the code below so it uses AXIOS in pipedream recommended way?

import axios from 'axios';
import fs from 'fs';

export default defineComponent({
  props: {
    twitter: {
      type: 'app',
      app: 'twitter',
    },
    url: {
      type: 'string',
      label: 'Photo URL',
      description: 'Full URL to the image to upload to Twitter',
    },
  },
  async run() {
    const oauthSignerUri = this.twitter.$auth.oauth_signer_uri;
    const token = {
      key: this.twitter.$auth.oauth_access_token,
      secret: this.twitter.$auth.oauth_refresh_token,
    };
    const signConfig = {
      token,
      oauthSignerUri,
    };

    console.log('Step 1: Downloading the image');
    const imageResponse = await axios({
      url: this.url,
      method: 'GET',
      responseType: 'arraybuffer',
    });
    console.log('Image downloaded:', imageResponse);

    // Save the image file locally in Pipedream
    const fileName = 'image.png'; // Change the file name if necessary
    const filePath = `/tmp/${fileName}`;
    fs.writeFileSync(filePath, imageResponse.data);
    console.log('Image saved locally:', filePath);

    // Upload the image file to Twitter as media
    console.log('Step 2: Uploading image to Twitter');
    const mediaUploadInitRequest = {
  method: 'POST',
  url: 'https://upload.twitter.com/1.1/media/upload.json',
  params: {
    command: 'INIT',
    total_bytes: fs.statSync(filePath).size,
    media_type: 'image/png', // Change the media type if necessary
  },
};

const mediaUploadAppendRequest = {
  method: 'POST',
  url: 'https://upload.twitter.com/1.1/media/upload.json',
  params: {
    command: 'APPEND',
    media_id: mediaId,
    segment_index: 0,
  },
  data: fs.readFileSync(filePath),
};

const mediaUploadFinalizeRequest = {
  method: 'POST',
  url: 'https://upload.twitter.com/1.1/media/upload.json',
  params: {
    command: 'FINALIZE',
    media_id: mediaId,
  },
};
    await axios(this.twitter, mediaUploadFinalizeRequest, signConfig);

    console.log('Image uploaded to Twitter:', mediaId);

    // Add more code here as needed

    return { success: true, mediaId };
  },
});

I can help you refactor the code to use the recommended @pipedream/platform axios. Here’s the updated code: import { axios } from "@pipedream/platform"; import fs from "fs"; export default defineComponent({ props: { twitter: { type: "app", app: "twitter", }, url: { type: "string", label: "Photo URL", description: "Full URL to the image to upload to Twitter", }, }, async run() { const oauthSignerUri = this.twitter.$auth.oauth_signer_uri; const token = { key: this.twitter.$auth.oauth_access_token, secret: this.twitter.$auth.oauth_refresh_token, }; const signConfig = { token, oauthSignerUri, }; console.log("Step 1: Downloading the image"); const imageResponse = await axios(this, { url: this.url, method: "GET", responseType: "arraybuffer", }); console.log("Image downloaded:", imageResponse); // Save the image file locally in Pipedream const fileName = "image.png"; // Change the file name if necessary const filePath = `/tmp/${fileName}`; fs.writeFileSync(filePath, imageResponse.data); console.log("Image saved locally:", filePath); // Upload the image file to Twitter as media console.log("Step 2: Uploading image to Twitter"); const mediaUploadInitRequest = { method: "POST", url: "https://upload.twitter.com/1.1/media/upload.json", params: { command: "INIT", total_bytes: fs.statSync(filePath).size, media_type: "image/png", // Change the media type if necessary }, }; const mediaUploadInitResponse = await axios(this, mediaUploadInitRequest, signConfig); const mediaId = mediaUploadInitResponse.data.media_id_string; const mediaUploadAppendRequest = { method: "POST", url: "https://upload.twitter.com/1.1/media/upload.json", params: { command: "APPEND", media_id: mediaId, segment_index: 0, }, data: fs.readFileSync(filePath), }; await axios(this, mediaUploadAppendRequest, signConfig); const mediaUploadFinalizeRequest = { method: "POST", url: "https://upload.twitter.com/1.1/media/upload.json", params: { command: "FINALIZE", media_id: mediaId, }, }; await axios(this, mediaUploadFinalizeRequest, signConfig); console.log("Image uploaded to Twitter:", mediaId); // Add more code here as needed return { success: true, mediaId }; }, }); I’ve made the following changes: 1. Imported axios from @pipedream/platform instead of the standalone axios package. 2. Replaced axios calls with axios(this, ...) to use the recommended Pipedream axios. 3. Removed angle brackets from the URLs in the media upload requests. This should work correctly with Pipedream’s recommended axios usage.

I implemented the changes, now I am getting different error:

TypeError
The “data” argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined
DETAILS

    at null.__node_internal_captureLargerStackTrace (internal/errors.js:412:5)
    at null.__node_internal_addCodeToName (internal/errors.js:168:9)
    at NodeError.NodeError (internal/errors.js:322:7)
    at null.__node_internal_ (internal/fs/utils.js:811:9)
    at Object.writeFileSync (fs.js:1522:5)
    at Object.run (file:///tmp/__pdg__/dist/code/92261a3d96b126bff92d4d182d9c30c9587b28a8df5d00bcd9524dfdbd75ebd2/component.mjs:38:8)
    at process.processTicksAndRejections (internal/process/task_queues.js:95:5)
    at global.executeComponent (/var/task/launch_worker.js:123:22)
    at MessagePort.messageHandler (/var/task/launch_worker.js:617:28)

elaborate why am I getting the error after implementing the changes you suggested. The error message is:

TypeError
The “data” argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined
DETAILS

    at null.__node_internal_captureLargerStackTrace (internal/errors.js:412:5)
    at null.__node_internal_addCodeToName (internal/errors.js:168:9)
    at NodeError.NodeError (internal/errors.js:322:7)
    at null.__node_internal_ (internal/fs/utils.js:811:9)
    at Object.writeFileSync (fs.js:1522:5)
    at Object.run (file:///tmp/__pdg__/dist/code/92261a3d96b126bff92d4d182d9c30c9587b28a8df5d00bcd9524dfdbd75ebd2/component.mjs:38:8)
    at process.processTicksAndRejections (internal/process/task_queues.js:95:5)
    at global.executeComponent (/var/task/launch_worker.js:123:22)
    at MessagePort.messageHandler (/var/task/launch_worker.js:617:28

, investigate the code below and elaborate why am I getting the error:

TypeError
The “data” argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined

Then apply the necessary changes to the code solve the issue.

import { axios } from "@pipedream/platform";
import fs from "fs";

export default defineComponent({
  props: {
    twitter: {
      type: "app",
      app: "twitter",
    },
    url: {
      type: "string",
      label: "Photo URL",
      description: "Full URL to the image to upload to Twitter",
    },
  },
  async run() {
    const oauthSignerUri = this.twitter.$auth.oauth_signer_uri;
    const token = {
      key: this.twitter.$auth.oauth_access_token,
      secret: this.twitter.$auth.oauth_refresh_token,
    };
    const signConfig = {
      token,
      oauthSignerUri,
    };

    console.log("Step 1: Downloading the image");
    const imageResponse = await axios(this, {
      url: this.url,
      method: "GET",
      responseType: "arraybuffer",
    });
    console.log("Image downloaded:", imageResponse);

    // Save the image file locally in Pipedream
    const fileName = "image.png"; // Change the file name if necessary
    const filePath = `/tmp/${fileName}`;
    fs.writeFileSync(filePath, imageResponse.data);
    console.log("Image saved locally:", filePath);

    // Upload the image file to Twitter as media
    console.log("Step 2: Uploading image to Twitter");
    const mediaUploadInitRequest = {
      method: "POST",
      url: "https://upload.twitter.com/1.1/media/upload.json",
      params: {
        command: "INIT",
        total_bytes: fs.statSync(filePath).size,
        media_type: "image/png", // Change the media type if necessary
      },
    };
    const mediaUploadInitResponse = await axios(this, mediaUploadInitRequest, signConfig);
    const mediaId = mediaUploadInitResponse.data.media_id_string;

    const mediaUploadAppendRequest = {
      method: "POST",
      url: "https://upload.twitter.com/1.1/media/upload.json",
      params: {
        command: "APPEND",
        media_id: mediaId,
        segment_index: 0,
      },
      data: fs.readFileSync(filePath),
    };
    await axios(this, mediaUploadAppendRequest, signConfig);

    const mediaUploadFinalizeRequest = {
      method: "POST",
      url: "https://upload.twitter.com/1.1/media/upload.json",
      params: {
        command: "FINALIZE",
        media_id: mediaId,
      },
    };
    await axios(this, mediaUploadFinalizeRequest, signConfig);

    console.log("Image uploaded to Twitter:", mediaId);

    // Add more code here as needed

    return { success: true, mediaId };
  },
});

I am getting error TypeError
The “data” argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined

, looks like @U05A7KLNALC won’t respond

There might be a limit on the number of questions you can ask in a duration. You might need to wait for a while, or trying to solve the issue yourself

Hey ,

investigate the code below and elaborate why am I getting the error:

TypeError
The “data” argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined

Then apply the necessary changes to the code solve the issue.

import { axios } from "@pipedream/platform";
import fs from "fs";

export default defineComponent({
  props: {
    twitter: {
      type: "app",
      app: "twitter",
    },
    url: {
      type: "string",
      label: "Photo URL",
      description: "Full URL to the image to upload to Twitter",
    },
  },
  async run() {
    const oauthSignerUri = this.twitter.$auth.oauth_signer_uri;
    const token = {
      key: this.twitter.$auth.oauth_access_token,
      secret: this.twitter.$auth.oauth_refresh_token,
    };
    const signConfig = {
      token,
      oauthSignerUri,
    };

    console.log("Step 1: Downloading the image");
    const imageResponse = await axios(this, {
      url: this.url,
      method: "GET",
      responseType: "arraybuffer",
    });
    console.log("Image downloaded:", imageResponse);

    // Save the image file locally in Pipedream
    const fileName = "image.png"; // Change the file name if necessary
    const filePath = `/tmp/${fileName}`;
    fs.writeFileSync(filePath, imageResponse.data);
    console.log("Image saved locally:", filePath);

    // Upload the image file to Twitter as media
    console.log("Step 2: Uploading image to Twitter");
    const mediaUploadInitRequest = {
      method: "POST",
      url: "https://upload.twitter.com/1.1/media/upload.json",
      params: {
        command: "INIT",
        total_bytes: fs.statSync(filePath).size,
        media_type: "image/png", // Change the media type if necessary
      },
    };
    const mediaUploadInitResponse = await axios(this, mediaUploadInitRequest, signConfig);
    const mediaId = mediaUploadInitResponse.data.media_id_string;

    const mediaUploadAppendRequest = {
      method: "POST",
      url: "https://upload.twitter.com/1.1/media/upload.json",
      params: {
        command: "APPEND",
        media_id: mediaId,
        segment_index: 0,
      },
      data: fs.readFileSync(filePath),
    };
    await axios(this, mediaUploadAppendRequest, signConfig);

    const mediaUploadFinalizeRequest = {
      method: "POST",
      url: "https://upload.twitter.com/1.1/media/upload.json",
      params: {
        command: "FINALIZE",
        media_id: mediaId,
      },
    };
    await axios(this, mediaUploadFinalizeRequest, signConfig);

    console.log("Image uploaded to Twitter:", mediaId);

    // Add more code here as needed

    return { success: true, mediaId };
  },
});

I am getting error TypeError
The “data” argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined

how do I send tweet with media in pipedream?

To send a tweet with media in Pipedream, you can use the “Create Tweet” action along with the “Upload Media” action. Here’s how to set it up: 1. Search for the Twitter app from the step menu 2. Select the “Upload Media” action and connect your Twitter account 3. Provide the media URL or data in the appropriate field 4. After the “Upload Media” step, add another step and search for the Twitter app again 5. Select the “Create Tweet” action and connect your Twitter account 6. In the “Text” field, enter the tweet text 7. In the “Media IDs” field, reference the media ID from the “Upload Media” step (e.g., steps.upload_media.media_id_string) 8. Configure any other optional fields as needed 9. At the bottom of the step, click “Test” to run the action and test the configuration This will create a tweet with the specified media attached.

there’s no “Upload Media” option after connecting twitter account (point 2.)

I apologize for the confusion earlier. You’re right, there isn’t a dedicated “Upload Media” action in Pipedream. Instead, you can use the code you provided with a slight modification to fix the error. The issue is with the fs.writeFileSync line. You need to convert the arraybuffer response to a Buffer before writing it to a file. Here’s the updated code: ```
import { axios } from “@pipedream/platform”; import fs from “fs”; export default defineComponent({ props: { twitter: { type: “app”, app: “twitter”, }, url: { type: “string”, label: “Photo URL”, description: “Full URL to the image to upload to Twitter”, }, }, async run() { // … (the rest of the code remains the same) // Save the image file locally in Pipedream const fileName = “image.png”; // Change the file name if necessary const filePath = /tmp/${fileName}; fs.writeFileSync(filePath, Buffer.from(imageResponse.data)); console.log(“Image saved locally:”, filePath); // … (the rest of the code remains the same) }, });

provide the whole code for the action, including the change portion

after implementig your suggesteg change, I am getting an error:

TypeError
The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined
DETAILS

    at null.__node_internal_captureLargerStackTrace (internal/errors.js:412:5)
    at null.__node_internal_addCodeToName (internal/errors.js:168:9)
    at NodeError.NodeError (internal/errors.js:322:7)
    at Function.from (buffer.js:334:9)
    at Object.run (file:///tmp/__pdg__/dist/code/492879e9aa51e2770f3dd6495ae414d3284f8805ae76ec2d7e6ec6151771356a/component.mjs:38:39)
    at process.processTicksAndRejections (internal/process/task_queues.js:95:5)
    at global.executeComponent (/var/task/launch_worker.js:123:22)
    at MessagePort.messageHandler (/var/task/launch_worker.js:617:28)

05/06/2023, 12:14:20
Step 1: Downloading the image
05/06/2023, 12:14:20
Image downloaded: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 07 e8 00 00 07 e8 08 06 00 00 00 09 35 83 75 00 00 00 06 62 4b 47 44 00 ff 00 ff 00 ff a0 bd a7 ... 3439042 more bytes>

Image is downloaded correctly though.