PLhery / node-twitter-api-v2

Strongly typed, full-featured, light, versatile yet powerful Twitter API v1.1 and v2 client for Node.js.
https://www.npmjs.com/package/twitter-api-v2
Apache License 2.0
1.29k stars 176 forks source link

Error in creating media ids from hosted image #451

Open ArunBohra12 opened 1 year ago

ArunBohra12 commented 1 year ago

Hi, I'm trying to create a media_id from the image that are hosted on s3 storage. The following is my code.

// socialAccountId in my code will give me the access token for twitter
async generateMediaIds(mediaUrls, socialAccountId) {
  // taken from "twitter-api-v2" types
  const EUploadMimeType =  {
    jpeg: "image/jpeg",
    jpg: "image/jpeg",
    mp4: "video/mp4",
    gif: "image/gif",
    png: "image/png",
    srt: "text/plain",
    webp: "image/webp"
  }

  var { v1Client } = await twitterClientGenerator(socialAccountId);
  const mediaIds = await Promise.all(mediaUrls.map(async url => {
    const mimeType = EUploadMimeType[url.split('.').at(-1)]

    const buffer = Buffer.from(url);
    return await v1Client.uploadMedia(buffer, { mimeType: mimeType });
  }));

  return mediaIds;
}

The problem is that by running the above code, I get the following error. Can't figure out if this error is something I'm getting from twitter API or is it this library that's causing this. Can anyone help me with this?

error message

sjunaiduk commented 1 year ago

Im getting this too

sjunaiduk commented 1 year ago

Hi, I'm trying to create a media_id from the image that are hosted on s3 storage. The following is my code.

// socialAccountId in my code will give me the access token for twitter
async generateMediaIds(mediaUrls, socialAccountId) {
  // taken from "twitter-api-v2" types
  const EUploadMimeType =  {
    jpeg: "image/jpeg",
    jpg: "image/jpeg",
    mp4: "video/mp4",
    gif: "image/gif",
    png: "image/png",
    srt: "text/plain",
    webp: "image/webp"
  }

  var { v1Client } = await twitterClientGenerator(socialAccountId);
  const mediaIds = await Promise.all(mediaUrls.map(async url => {
    const mimeType = EUploadMimeType[url.split('.').at(-1)]

    const buffer = Buffer.from(url);
    return await v1Client.uploadMedia(buffer, { mimeType: mimeType });
  }));

  return mediaIds;
}

The problem is that by running the above code, I get the following error. Can't figure out if this error is something I'm getting from twitter API or is it this library that's causing this. Can anyone help me with this?

error message

Don't convert it into a buffer. Do this instead:

await userClient.v1.uploadMedia(filePath);

Works for me.

ProCode47 commented 1 year ago

it still doesn't work for me tho, it keeps throwing error 403, any idea what could be the problem?

FilipByren commented 1 year ago

@ProCode47 Most likely you stumbled to the same issue as I had, which was setting up the user authentication using the oAuth2 option, this limits you to actually perform v1 api operations. Instead you should setup you client like the following case: https://github.com/PLhery/node-twitter-api-v2/blob/master/doc/auth.md. Twitters API setup is impressively complicated.

mutableideas commented 1 year ago

@ProCode47 Most likely you stumbled to the same issue as I had, which was setting up the user authentication using the oAuth2 option, this limits you to actually perform v1 api operations. Instead you should setup you client like the following case: https://github.com/PLhery/node-twitter-api-v2/blob/master/doc/auth.md. Twitters API setup is impressively complicated.

I'm authenticating using the OAuth2 option which has been working fine for Tweeting any status. I'm trying to upload using the v1 client and a buffer which tells me I'm not authorized to do so. What did you do to fix this? Are you saying that setting up the OAuth2 option limits the ability to call version 1 API operations?

commonpike commented 11 months ago

I'm authenticating using the OAuth2 option which has been working fine for Tweeting any status. I'm trying to upload using the v1 client and a buffer which tells me I'm not authorized to do so. What did you do to fix this? Are you saying that setting >up the OAuth2 option limits the ability to call version 1 API operations?

Running into the same, I finally got it working when authenticating via the OAuth1 method and uploading with that client, then authenticating via OAuth2 and tweeting with that client. I had to make sure the OAuth1 credentials were set to read & write, and only get the OAuth2 credentials after OAuth1 was settled (I think).

So basicly, this example https://github.com/PLhery/node-twitter-api-v2/blob/master/doc/examples.md#post-a-new-tweet-with-multiple-images did not work. I ended up doing

// First, post all your images to Twitter
const client1 = new TwitterApi({
      appKey: TWITTER_O1_API_KEY,
      appSecret: TWITTER_O1_API_KEY_SECRET,
      accessToken: TWITTER_O1_ACCESS_TOKEN,
      accessSecret: TWITTER_O1_ACCESS_SECRET,
 });
const mediaIds = await Promise.all([
  // file path
  client1.v1.uploadMedia('./my-image.jpg'),
  // from a buffer, for example obtained with an image modifier package
  client1.v1.uploadMedia(Buffer.from(rotatedImage), { type: 'png' }),
]);

// mediaIds is a string[], can be given to .tweet
const client2 = new TwitterApi(TWITTER_ACCESS_TOKEN);
await client2.v2.tweet({
  text: 'My tweet text with two images!',
  media: { media_ids: mediaIds }
});

403 may suggest that there is some scope missing in my OAuth2 access token, but ... I don't think there is.

genu commented 8 months ago

What i'm not clear about is, what does the user authorize with?

  1. If you authorize using v2 flow, you cannot upload media, but you can tweet?
  2. If you authorize using v1 flow, you can upload media, but not create tweet?

Do we need to authenticate with both v1 and v2 flows to really get the full experience? Does that mean the user has to go through 2 authorization screens separately?

In this hybrid approach above, What is the client2 initialized with, what access token is that?

commonpike commented 8 months ago

Sorry if that was unclear.

Just speaking for my own installation and OTOH,

  1. The OAuth v1 credentials I use didn't require the user to authenticate. On https://developer.twitter.com/, on your apps OAuth1 settings screen, you can generate an Api Key and Api Secret,and also generate an Access Token and Secret ( make sure it is read and write). They are apparently tied to the App, not to a user, but these 4 allow me to upload media. I don't know if they would allow me to tweet f.e. to the apps owner timeline. I haven't tried.
  2. For the Oauth2 credentials, on https://developer.twitter.com/, on your apps OAuth2 settings screen, you can pick up Client ID and Client Secret. You can use these to get an access token for one particular user by going through the auth flow (code -> token).

I ended up doing it like this because the first thing I tried was OAuth v2, and when that failed I was hoping it would work in the future and this was just a temporary workaround.

genu commented 8 months ago

@commonpike

Thanks, that seems to be the key. In order to do media uploads, you must use appKey, appSecret, accessToken and accessSecret

all coming from dashboard.

const v1Client = new TwitterApi({
  appKey: "API_KEY"
  appSecret: "API_SECRET,
  accessToken: "ACCESS_TOKEN",
  accessSecret: "ACCESS_SECRET,
});

const mediaId = v1Client.v1.uploadMedia("./sample.png");
// mediaId = 834579578343
genu commented 8 months ago

Also, worth mentioning:

When uploading media in this way, you must set the the additionalOwners option and include the user which would be posting the tweet. Otherwise, the tweet creation step would fail.

so basically, you would need to:

const v1Client = new TwitterApi({
  appKey: "API_KEY"
  appSecret: "API_SECRET,
  accessToken: "ACCESS_TOKEN",
  accessSecret: "ACCESS_SECRET,
});

const mediaId = v1Client.v1.uploadMedia("./sample.png", { additionalOwner: ["ID OF THE USER"]});
// mediaId = 834579578343

await client.v2.tweet({text: "My tweet", { media: { media_ids: ['834579578343'] } } );