Giter Site home page Giter Site logo

draftbit / twitter-lite Goto Github PK

View Code? Open in Web Editor NEW
794.0 10.0 97.0 1.15 MB

A tiny, full-featured, flexible client / server library for the Twitter API

Home Page: https://npm.im/twitter-lite

License: MIT License

JavaScript 99.55% Shell 0.45%
twitter node-twitter twitter-api twitter-rest twitter-streams twitter-lite node

twitter-lite's Introduction

Twitter Lite

A tiny, full-featured, modern client / server library for the Twitter API.

npm travis

Features

  • Promise driven via Async / Await
  • REST and Stream support
  • Typescript support
  • Works in Node.js
  • Rate limiting support
  • Under 1kb
  • Minimal dependencies
  • Test suite

Why

We have built this library because existing ones have not been recently maintained, or depend on outdated libraries.

Installation

yarn add twitter-lite
npm install twitter-lite

Then you can include the following at the top of your code:

import Twitter from 'twitter-lite';

const client = new Twitter({
  ...
})

client.get(...)
client.post(...)

Usage

  • Create an app on https://apps.twitter.com/
  • Grab the Consumer Key (API Key) and Consumer Secret (API Secret) from Keys and Access Tokens
  • Make sure you set the right access level for your app
  • If you want to use user-based authentication, grab the access token key and secret as well

App vs. User authentication

Twitter has two different authentication options:

  • App: higher rate limits. Great for building your own Twitter App.
  • User: lower rate limits. Great for making requests on behalf of a User.

User authentication requires:

  • consumer_key
  • consumer_secret
  • access_token_key
  • access_token_secret

App authentication requires:

  • bearer_token

App authentication is a simple header behind the scenes:

headers: {
  Authorization: `Bearer ${bearer_token}`;
}

You can get the bearer token by calling .getBearerToken().

Verifying credentials example (user auth)

const client = new Twitter({
  subdomain: "api", // "api" is the default (change for other subdomains)
  version: "1.1", // version "1.1" is the default (change for other subdomains)
  consumer_key: "abc", // from Twitter.
  consumer_secret: "def", // from Twitter.
  access_token_key: "uvw", // from your User (oauth_token)
  access_token_secret: "xyz" // from your User (oauth_token_secret)
});

client
  .get("account/verify_credentials")
  .then(results => {
    console.log("results", results);
  })
  .catch(console.error);

App authentication example

const user = new Twitter({
  consumer_key: "abc",
  consumer_secret: "def"
});

const response = await user.getBearerToken();
const app = new Twitter({
  bearer_token: response.access_token
});

Oauth authentication

According to the docs this helps you get access token from your users.

const client = new Twitter({
  consumer_key: "xyz",
  consumer_secret: "xyz"
});

client
  .getRequestToken("http://callbackurl.com")
  .then(res =>
    console.log({
      reqTkn: res.oauth_token,
      reqTknSecret: res.oauth_token_secret
    })
  )
  .catch(console.error);

Then you redirect your user to https://api.twitter.com/oauth/authenticate?oauth_token=xyz123abc, and once you get the verifier and the token, you pass them on to the next stage of the authentication.

const client = new Twitter({
  consumer_key: "xyz",
  consumer_secret: "xyz"
});

client
  .getAccessToken({
    oauth_verifier: oauthVerifier,
    oauth_token: oauthToken
  })
  .then(res =>
    console.log({
      accTkn: res.oauth_token,
      accTknSecret: res.oauth_token_secret,
      userId: res.user_id,
      screenName: res.screen_name
    })
  )
  .catch(console.error);

And this will return you your access_token and access_token_secret.

Tweeting a thread

const client = new Twitter({
  consumer_key: "xyz",
  consumer_secret: "xyz",
  access_token_key: "xyz",
  access_token_secret: "xyz"
});

async function tweetThread(thread) {
  let lastTweetID = "";
  for (const status of thread) {
    const tweet = await client.post("statuses/update", {
      status: status,
      in_reply_to_status_id: lastTweetID,
      auto_populate_reply_metadata: true
    });
    lastTweetID = tweet.id_str;
  }
}

const thread = ["First tweet", "Second tweet", "Third tweet"];
tweetThread(thread).catch(console.error);

Streams

To learn more about the streaming API visit the Twitter Docs. The streaming API works only in Node.

const client = new Twitter({
  consumer_key: "xyz" // from Twitter.
  consumer_secret: "xyz" // from Twitter.
  access_token_key: "abc" // from your User (oauth_token)
  access_token_secret: "abc" // from your User (oauth_token_secret)
});

const parameters = {
  track: "#bitcoin,#litecoin,#monero",
  follow: "422297024,873788249839370240",  // @OrchardAI, @tylerbuchea
  locations: "-122.75,36.8,-121.75,37.8",  // Bounding box -	San Francisco
};

const stream = client.stream("statuses/filter", parameters)
  .on("start", response => console.log("start"))
  .on("data", tweet => console.log("data", tweet.text))
  .on("ping", () => console.log("ping"))
  .on("error", error => console.log("error", error))
  .on("end", response => console.log("end"));

// To stop the stream:
process.nextTick(() => stream.destroy());  // emits "end" and "error" events

To stop a stream, call stream.destroy(). That might take a while though, if the stream receives a lot of traffic. Also, if you attempt to destroy a stream from an on handler, you may get an error about writing to a destroyed stream. In that case, try to defer the destroy() call:

process.nextTick(() => stream.destroy());

After calling stream.destroy(), you can recreate the stream, if you wait long enough - see the "should reuse stream N times" test. Note that Twitter may return a "420 Enhance your calm" error if you switch streams too fast. There are no response headers specifying how long to wait, and the error, as well as streaming limits in general, are poorly documented. Trial and error has shown that for tracked keywords, waiting 20 to 30 seconds between re-creating streams was enough. Remember to also set up the .on() handlers again for the new stream.

Support for Twitter API v2

The new Twitter API v2 no longer requires the .json extension on its endpoints. In order to use v2, set version: '2' and extension: false.

const client = new Twitter({
  version: "2", // version "1.1" is the default (change for v2)
  extension: false, // true is the default (this must be set to false for v2 endpoints)
  consumer_key: "abc", // from Twitter.
  consumer_secret: "def", // from Twitter.
  access_token_key: "uvw", // from your User (oauth_token)
  access_token_secret: "xyz" // from your User (oauth_token_secret)
});

Methods

.get(endpoint, parameters)

Returns a Promise resolving to the API response object, or rejecting on error. The response and error objects also contain the HTTP response code and headers, under the _headers key. These are useful to check for rate limit information.

const client = new Twitter({
  consumer_key: "xyz",
  consumer_secret: "xyz",
  access_token_key: "abc",
  access_token_secret: "abc"
});

const rateLimits = await client.get("statuses/show", {
  id: "1016078154497048576"
});

.post(endpoint, parameters)

Same return as .get().

Use the .post method for actions that change state, or when the total size of the parameters might be too long for a GET request. For example, to follow a user:

const client = new Twitter({
  consumer_key: "xyz",
  consumer_secret: "xyz",
  access_token_key: "abc",
  access_token_secret: "abc"
});

await client.post("friendships/create", {
  screen_name: "dandv"
});

The second use case for POST is when you need to pass more parameters than suitable for the length of a URL, such as when looking up a larger number of user ids or screen names:

const users = await client.post("users/lookup", {
  screen_name: "longScreenName1,longerScreeName2,...,veryLongScreenName100"
});

.put(endpoint, query_parameters, request_body)

Same return as .get() and .post().

Use the .put method for actions that update state. For example, to update a welcome message.

const client = new Twitter({
  consumer_key: "xyz",
  consumer_secret: "xyz",
  access_token_key: "abc",
  access_token_secret: "abc"
});

const welcomeMessageID = "abc";

await client.put(
  "direct_messages/welcome_messages/update",
  {
    id: welcomeMessageID
  },
  {
    message_data: {
      text: "Welcome!!!"
    }
  }
);

.getBearerToken()

See the app authentication example.

.getRequestToken(twitterCallbackUrl)

See the OAuth example.

.getAccessToken(options)

See the OAuth example.

Examples

You can find many more examples for various resources/endpoints in the tests.

Troubleshooting

Headers on success

const tweets = await client.get("statuses/home_timeline");
console.log(`Rate: ${tweets._headers.get('x-rate-limit-remaining')} / ${tweets._headers.get('x-rate-limit-limit')}`);
const delta = (tweets._headers.get('x-rate-limit-reset') * 1000) - Date.now()
console.log(`Reset: ${Math.ceil(delta / 1000 / 60)} minutes`);

API errors

.get and .post reject on error, so you can use try/catch to handle errors. The error object contains an errors property with the error code and message, and a _headers property with the the HTTP response code and Headers object returned by the Twitter API.

try {
  const response = await client.get("some/endpoint");
  // ... use response here ...
} catch (e) {
  if ('errors' in e) {
    // Twitter API error
    if (e.errors[0].code === 88)
      // rate limit exceeded
      console.log("Rate limit will reset on", new Date(e._headers.get("x-rate-limit-reset") * 1000));
    else
      // some other kind of error, e.g. read-only API trying to POST
  } else {
    // non-API error, e.g. network problem or invalid JSON in response
  }
}

Rate limiting

A particular case of errors is exceeding the rate limits. See the example immediately above for detecting rate limit errors, and read Twitter's documentation on rate limiting.

Numeric vs. string IDs

Twitter uses numeric IDs that in practice can be up to 18 characters long. Due to rounding errors, it's unsafe to use numeric IDs in JavaScript. Always set stringify_ids: true when possible, so that Twitter will return strings instead of numbers, and rely on the id_str field, rather than on the id field.

Contributing

With the library nearing v1.0, contributions are welcome! Areas especially in need of help involve multimedia (see #33 for example), and adding tests (see these for reference).

Development

  1. Fork/clone the repo
  2. yarn/npm install
  3. Go to https://apps.twitter.com and create an app for testing this module. Make sure it has read/write permissions.
  4. Grab the consumer key/secret, and the access token/secret and place them in a .env file in the project's root directory, under the following variables:
    TWITTER_CONSUMER_KEY=...
    TWITTER_CONSUMER_SECRET=...
    ACCESS_TOKEN=...
    ACCESS_TOKEN_SECRET=...
    
  5. yarn/npm test and make sure all tests pass
  6. Add your contribution, along with test case(s). Note: feel free to skip the "should DM user" test during development by changing that it() call to it.skip(), but remember to revert that change before committing. This will prevent your account from being flagged as abusing the API to send too many DMs.
  7. Make sure all tests pass. NOTE: tests will take over 10 minutes to finish.
  8. Commit using a descriptive message (please squash commits into one per fix/improvement!)
  9. git push and submit your PR!

Credits

Authors:

Over the years, thanks to:

twitter-lite's People

Contributors

ciffelia avatar dandv avatar dependabot[bot] avatar dylanirlbeck avatar fdebijl avatar felipegcastro avatar italodeandra avatar jeremydaly avatar kmaid avatar lcswillems avatar leesiongchan avatar maybeanerd avatar niciusb avatar nick-gottschlich avatar ongspxm avatar paulmars avatar peterpme avatar seldszar avatar tylerbuchea avatar wesbos avatar zgotsch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

twitter-lite's Issues

Can someone please provide an example where I can get home timeline stream?

I'm trying to get my homepage stream (getting all the tweets as people tweet, a stream), but I couldn't.

I don't want to filter out anything, I just want to see the stream.

My code

const Twitter = require('twitter-lite')

// removed auth info
const client = new Twitter({
  consumer_key: '',
  consumer_secret: '',
  access_token_key: '',
  access_token_secret: ''
})

let stream = client.stream('statuses/home_timeline', {})
  .on('start', response => console.log('start'))
  .on('data', tweet => console.log('data', tweet.text))
  .on('ping', () => console.log('ping'))
  .on('error', error => console.log('error', error))
  .on('end', response => console.log('end'))

Error

error Response {
  size: 0,
  timeout: 0,
  _headers:
   [Object: null prototype] {
     connection: [ 'close' ],
     'content-length': [ '0' ],
     date: [ 'Sun, 01 Mar 2020 15:10:14 GMT' ],
     server: [ 'tsa_b' ],
     'set-cookie':
      [ 'personalization_id="REMOVEDTHIS=="; Max-Age=63072000; Expires=Tue, 1 Mar 2022 15:10:14 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None',
        'guest_id=REMOVEDTHIS; Max-Age=63072000; Expires=Tue, 1 Mar 2022 15:10:14 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None' ],
     'strict-transport-security': [ 'max-age=631138519' ],
     'x-connection-hash': [ 'REMOVEDTHIS' ],
     'x-response-time': [ '6' ],
     'x-tsa-request-body-time': [ '1' ] },
  [Symbol(Body internals)]:
   { body:
      PassThrough {
        _readableState: [ReadableState],
        readable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 2,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: true,
        _transformState: [Object] },
     disturbed: false,
     error: null },
  [Symbol(Response internals)]:
   { url: 'https://stream.twitter.com/1.1/statuses/home_timeline.json',
     status: 404,
     statusText: 'Not Found',
     headers: Headers { [Symbol(map)]: [Object] },
     counter: 0 } }
end

I used to use another package called Twit and it was like below. How can I achieve the equivalent (streaming my homepage stream as users tweet) with this package? I don't want to filter out anything, I just want to see the stream.

Thank you!

var Twit = require('twit')

var T = new Twit({
  consumer_key: '',
  consumer_secret: '',
  access_token: '',
  access_token_secret: '',
  timeout_ms: 60 * 1000 // optional HTTP request timeout to apply to all requests.
})

var stream = T.stream('user')
stream.on('tweet', tweet => {
 console.log(tweet.text) // new tweet
})

Implement Account Activity API

According to Twitter, the User and Site stream APIs will be discontinued in June to make way for the Account Activity API:

The Account Activity API will replace the User streams and Site streams APIs. Additionally, we plan to sunset those APIs on Tuesday June 19, 2018.

https://developer.twitter.com/en/docs/accounts-and-users/subscribe-account-activity/overview

Luckily there is a migration guide:

https://developer.twitter.com/en/docs/accounts-and-users/subscribe-account-activity/guides/us-ss-migration-guide

lambda Fetch Error

Hello,

First of all, many thanks for publishing and maintaining this library.

Well, I test locally (mac os, nodev10.15.3), and it is working fine, but when I try to use remotely (aws lambda), I got this error message:

ERROR { FetchError: request to https://api.twitter.com/1.1/account/verify_credentials.json fa code: 'ECONNRESET' }/api.twitter.com/1.1/account/verify_credentials.json failed, reason: Client network socket disconnected before secure TLS connection was established'

My code:

import Twitter from 'twitter-lite';

export async function handler(
  event: LambdaEvent,
  context: LambdaContext,
  callback: LambdaCallback
) {
  try {
    const client = new Twitter({
      consumer_key: process.env.twitterConsumerKey,
      consumer_secret: process.env.twitterConsumerSecret,
      access_token_key: 'my own access token key for testing',
      access_token_secret: 'my own access token secret for testing'
    });

    client
      .get('account/verify_credentials')
      .then results => {
        console.log('results', results);
      })
      .catch(console.error);
  } catch (err) {
    console.error(err);
    return callback(err);
  }
}

I am quite sure it is not a problem with this library itself, but if some one faced this issue, please share with me.
Thank you.

Add a Contributing section to the README

Hey guys, hope you don't mind opening an issue for this to get your thoughts. I forked the code to see where I could help and noticed it didn't have a 'Contributing' section. I think it would help any newcomers that come to the project.

I can do a pull request for it but wanted to make sure to capture any thoughts you guys had. Below is a draft of what I think should be the steps, let me know if you would add or change anything.

Draft:

  1. Fork and clone the repo
  2. Install the dependencies and run the tests (make sure repo is in a good state)
    2.1 Instructions for running the tests, as the tests are making network requests, testing access tokens, creating direct messages etc there is some setup needed and I think it should be documented to make it easier.
  3. Make your changes
  4. Add tests for your feature/bugfix
  5. Run tests
  6. Submit a pull request

Jason

No media entities on an image tweet

Hello,

Whether I use the Streaming API or the statuses/user_timeline endpont, there are certain tweets where I simply won't get any image data from the API, when there clearly is an image in the tweet.

This was reported by an user of mine, who got 0 images when getting tweets from this account: https://twitter.com/AzurLane_EN

When I get this user's timeline, images that I can clearly see on twitter are not visible in the tweet object that's returned. Not only are there no extended_entities, there isn't a single trace of the linked image in the entire object.

Has anyone here ever encountered this? Twitter's documentation seems to state pretty clearly that wherever there is an image, there is an extended_entities object...

Tweets don't meet track if location parameter is set

I'm experiencing an issue in which the tweets that are being streamed do not meet the track that I feed the stream when I set the location parameter. I have a bounding box around the continental United States and a track of 77 words. When I remove the locations parameter everything works fine, but with the locations parameter set the stream seems to give me any tweet within the boundaries. Is anybody else having this problem? I had the same issue with the node-twitter and that is what brought me here.

How to handle rate limiting with multiple apps

Hi,

I need to fetch large amounts of data. I am fetching tweets from every follower of a specific user, the user has over 1 million followers.

I have multiple twitter apps is it possible to handle rate limiting by using these multiple api tokens. For example when one is about to reach the limit to exchange the next request to use the next api token.

Is there an example of this?

Stream stops receiving tweets without any errors

Hello,

I'd like to know if anyone has encountered this - I've been getting streams that basically stop receiving any data from Twitter after a few hours, as if no one were positing - except I'm following thousands of accounts and they clearly are tweeting.

I am unsure whether this is a bug in twitter-lite, a bug with my code, or if twitter is deciding to stop sending tweets to my bot without warning me in any way for some reason, so I'd like to know if anyone has potential solutions.

Again, neither my onError nor my onData callbacks are being triggered, at all, so i have no actual error messages to show you, the stream just stays open but doesn't appear to receive incoming tweets.

I thought this could have been caused by twitter outages but it's been going on for a few weeks now. The stream works fine for a few hours, then drops out. This has happened to me on multiple instances of my bot with very different number of accounts followed so it has nothing to do with the number of accounts i subscribe to.

Restarting the stream (by rebooting the bot or just by re-registering the stream) fixes the issue temporarily, until the next time it stops.

Any help would be appreciated, i suspect a problem with twitter-lite but I just don't know.

Endpoint "media/metadata/create" fails

The endpoint https://upload.twitter.com/1.1/media/metadata/create.json fails due to content being sent in application/x-www-form-urlencoded, but it only accepts application/json
I tried adding it to JSON_ENDPOINTS but it fails with an invalid json response.

Code sample:

const twUploadClient = new Twitter({
  subdomain: 'upload',
  consumer_key: credentialsArray[0],
  consumer_secret: credentialsArray[1],
  access_token_key: credentialsArray[2],
  access_token_secret: credentialsArray[3]
})

// Upload image
const mediaUploadData = await twUploadClient.post('media/upload', { media_data: b64content })

// Set alt text for image
if (card.alt && card.alt.length > 0) {
  await twUploadClient.post('media/metadata/create', { media_id: mediaUploadData.media_id_string, alt_text: { text: imageAltString } })
}

Doc page for media/metadata/create for reference: https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create

POST Requests break when Twitter Returns no response

Hey guys,

I was using the POST requests for many things and it was working perfectly but if the twitter api returns no body then request breaks. An example of a POST request that breaks it is: https://developer.twitter.com/en/docs/direct-messages/typing-indicator-and-read-receipts/api-reference/new-typing-indicator

Below is the error:

{ FetchError: invalid json response body at https://api.twitter.com/1.1/direct_messages/indicate_typing.json?recipient_id=50110518 reason: Unexpected end of JSON input
    at /Users/jasonlloyd/Documents/GitHub/MIDAS-API/node_modules/cross-fetch/node_modules/node-fetch/lib/body.js:48:31
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
  name: 'FetchError',
  message: 'invalid json response body at https://api.twitter.com/1.1/direct_messages/indicate_typing.json?recipient_id=50110518 reason: Unexpected end of JSON input',
  type: 'invalid-json' }

I think its breaking at this line 66 in twitter.js return response.json().then...

I know you guys are just using node-fetch .json() to do get the json version for you but maybe it is something that needs to be looked at from the library side.

Please close if nothing you can do or you think it is not worth fixing.

Jason

Calling stream.destroy() does not emit any events

Hello,

I'm currently running a 24/7 stream on a server that occasionally needs to be stopped and started again programmatically to change track params.

When developing locally and stopping/restarting the server via the command line, it seems pretty resilient to 420 errors. When using stream.destroy(), waiting 20-30 seconds and restarting the stream I get 420 errors on the 1st or 2nd try - it seems very sensitive.

Despite listening to 'error' and 'end' events (which work at other times) I am not getting any events after stream.destroy() is called, even if deferred and I have a feeling this may be causing the 420 errors as the stream isn't being stopped properly.

Does this still ring true?

// To stop the stream:
process.nextTick(() => stream.destroy());  // emits "end" and "error" events

Long running stream with 900+ follow ids

I have a stream running 24/7 following 900+ userids but several times a day this error is thrown, the stream seems to stay alive and does not need to be restarted, but i cannot seem to catch this error and handle it appropriately.

(node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 13)
at Timer.processTimers (timers.js:223:10)
at listOnTimeout (timers.js:263:5)
at tryOnTimeout (timers.js:300:5)
at ontimeout (timers.js:436:11)
at TLSSocket.Socket._onTimeout (net.js:453:8)
at TLSSocket.emit (events.js:182:13)
at Object.onceWrapper (events.js:273:13)
at TLSSocket.emitRequestTimeout (_http_client.js:662:40)
at ClientRequest.emit (events.js:182:13)
at Object.onceWrapper (events.js:273:13)
at ClientRequest. (/home/node/app/node_modules/request/request.js:816:19)
(node:1) UnhandledPromiseRejectionWarning: Error: ESOCKETTIMEDOUT

Also usually accompanied by:

(node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 14)
at TLSWrap.onStreamRead (internal/stream_base_commons.js:111:27)
(node:1) UnhandledPromiseRejectionWarning: Error: read ECONNRESET

Square brackets misinterpreted in post params?

I have something set up like below but no matter what you do I get an unhandled error that says nothing specific about an #. If you remove the brackets from the string it works fine, escaping them doesn't seem to help, any ideas?

const params = {
  name: '[๐Ÿ”ด LIVE] name',
  description: 'new bio'
}

client.post('account/update_profile', params) {
  .then(function (data) {
    //do whatever
  })
  .catch(function (error) {
    throw error
  })

Text Works, Images Not So Much

I have the following sitting in the AWS cloud API Gateway:

import Twitter from "twitter-lite"; 
import { success, failure } from "./libs/response-lib";

export async function main(event, context, callback) {
  const data = JSON.parse(event.body);
  const client = new Twitter({
    subdomain: "api",
    consumer_key: "redacted"
    consumer_secret: "redacted"
    access_token_key: "redacted"
    access_token_secret: "redacted"
  });

  if(data.base64encodedimg)
  {
    try 
    {
      client.subdomain = "upload"; 
      const url = await client.post("media/upload", null, {
        media_data: data.base64encodedimg,
      }); 
      client.subdomain ="api";
      await client.post("statuses/update", null, {
        status: data.tweet,
        media_ids: url.media_id_string,
      });
    } catch (e) {
      console.log(e); 
      callback(null, failure({ status: false }));
    }
  } 
  else 
  {
    try 
    {
      await client.post("statuses/update", null, {
        status: data.tweet,
      });   
      callback(null, success(data.tweet));
    } catch (e) {
      console.log(e); 
      callback(null, failure({ status: false }));
    }
  }

}

I can fire off tweets all day long with a problem, but I'm not able to attach an image to them. Tweeting without an image works without a hitch as well.
I've gone through the painful process of ensuring that I'm actually sending along a base64 encoded image, so I know that's coming through as expected, at least to the gateway.
When this fails, all I receive back is a 500 response.
Thoughts?

Statuses/update with error: 32 โ€˜could not authenticate youโ€™ when ! or ' are in message

Hi guys,

Iโ€™m trying to tweet a message, and in response Iโ€™m getting error 32 with โ€˜Could not authenticate youโ€™ message. I found out that error is occure when I have in message ! or ' characters.

I also found that someone has this error couple years ago - https://twittercommunity.com/t/post-to-statuses-update-json-started-hitting-error-32-could-not-authenticate-you-with-no-code-changes/36495/9 but they posted that issue with twitter was fixed.

I tried something like this encodeURIComponent( inputStr ).replace( /[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16) ); but message on twitter is with percent character and code, no parsing on twitter side.

Maybe someone have had the same problem as me or any idea how to make workaround?

Upgrade project dependencies

Many of the current dependencies in twitter-lite are in need of updating. We should investigate which ones (if not all) we can upgrade, and bump the versions.

Support TypeScript

Is there an interest in supporting TypeScript, either by switching the source or adding type definition files?

If so I'd be happy to help :)

Thanks for the library, btw.

Equivalent to Twit.postMediaChunked?

As we all know Twit is getting more and more broken by the day (especially postMediaChunked), so an alternative is more than welcome. Has support for chunked media uploaded been added? Right now no good library exists to easily upload a file to Twitter via this API, so having this functionality would be amazing.

Unable to do post request to oauth/request_token

I have been trying to do

client.post("oauth/request_token", {oauth_callback:secret.callbackURL}).then(function(res){
    console.log(res);
});

but keep getting the following error { errors: [ { code: 89, message: 'Invalid or expired token.' } ] }

any help?

"Null" Consumer_Key in Authorization Header

Running a simple GET request to verify credentials gave the following error in twitter.m.js

e.headers.raw() is not a function

To debug it, I removed it from h._handleResponse function. The above error went away but received a response from twitter server of 401.

On looking the request, I found OAuth oauth_consumer_key="null" in Authorization header.

Seems like the function is not setting the auth header correctly?

Content-Transfer-Encoding

Running into issues uploading images as base64 encoding. I'm able to send text tweets without an issue, just can't upload images.

Here's a response from Twitter:

{id: "1085284850389659650",โ€ฆ}
id: "1085284850389659650"
media: "/9j/4AAQSkZJRgABAgAAAQABAAD/4AAcT2NhZCRSZXY6IDIwMT........"
res: {request: "/1.1/media/upload.json", error: "media type unrecognized."}
error: "media type unrecognized."
request: "/1.1/media/upload.json"

ID - the id of the Tweet I was able to send.
Media - the base64 encoded image I wanted to send.
res - what should have been a response back from Twitter with the the media_id

Looking at the guide, the recommendation is to set the "Content-Transfer-Encoding" header to "base64", which I don't see a way to do via the API.

Could this be a possible reason for the "media type unrecognized" response back from Twitter?

Code I'm using, where params.img is a url to an image hosted in an s3 bucket:

import Twitter from "twitter-lite"; 
import { success, failure } from "./libs/response-lib";
const https = require('https');

function getImage(url, callback){
  https.get(url, res => {
    const bufs = []; 
    res.on('data', function(chunk) {
      bufs.push(chunk)
    });
    res.on('end', function() {
      var data = Buffer.concat(bufs);
      data = Buffer.from(data).toString('base64');
      callback(null, data);
    });
  })
  .on('error', callback); 
}

export async function main(event, context, callback) {
  const params = JSON.parse(event.body);
  const client = new Twitter({
    subdomain: "api",
    consumer_key: "REDACTED", 
    consumer_secret: "REDACTED",
    access_token_key: "REDACTED",
    access_token_secret: "REDACTED"
  });
  const upload_client = new Twitter({
    subdomain: "upload",
    consumer_key: "REDACTED", 
    consumer_secret: "REDACTED",
    access_token_key: "REDACTED",
    access_token_secret: "REDACTED"
  });
  
  if(params.img)
  {
    try 
    {
      let data_res = "nothing";
      await getImage(params.img, function(err, data){
        if(err)
        {
          callback(null, failure({status: err, params: params}));
        } 
        else 
        {
          data_res = data;
        }
      });
      const res = await upload_client.post("media/upload", null, {
        media_data: data_res,
      }); 
      const resp = await client.post("statuses/update", null, {
        status: params.tweet,
        media_ids: res.media_id_string,
      });
      callback(null, success({id: resp.id_str, media: data_res, res: res}));
    } 
    catch (e) 
    {
      callback(null, failure({ status: e, params: params}));
    }
  } 
  else 
  {
    try 
    {
      const resp = await client.post("statuses/update", null, {
        status: params.tweet,
      });   
      callback(null, success(resp.id_str));
    } 
    catch (e) 
    { 
      callback(null, failure({ status: false }));
    }
  }

}

Support other Twitter APIs

This issue is meant to track support for these new Twitter APIs; #50 and #55 are subsumed under this issue. Because we already support GET, POST, and PUT requests to any subdomain of twitter.com (see the Twitter constructor, namely the subdomain property), I'm not even sure that much has to be done for these new APIs besides some testing - with the exception of Filtered Streams, which I believe will require more work.

If anyone chooses to tackle one of these new APIs, please open a separate issue so we can track/discuss your progress there.

On a side note, you can see all the API endpoints at https://developer.twitter.com/en/docs/api-reference-index.

New Twitter APIs

Access to full response object

Currently, it is assumed you only want the data. However if you get back a rate limiting error it comes back as a successful response. Right now, the catch block only gives you the error if something catastrophic happens, like a dropped connection. So as long as Twitter responds you get a successful response body and the consumers have to check the body to see if there is an error instead of relying on status codes. See https://github.com/Preposterous/twitter-lite/blob/master/twitter.js#L75

TypeError: stream.destroy is not a function

I am using this code:

const client = new Twitter({
consumer_key: "xyz" // from Twitter.
consumer_secret: "xyz" // from Twitter.
access_token_key: "abc" // from your User (oauth_token)
access_token_secret: "abc" // from your User (oauth_token_secret)
});

const parameters = {
track: "#bitcoin,#litecoin,#monero",
follow: "422297024,873788249839370240", // @OrchardAI, @tylerbuchea
locations: "-122.75,36.8,-121.75,37.8", // Bounding box - San Francisco
};

const stream = client.stream("statuses/filter", parameters)
.on("start", response => console.log("start"))
.on("data", tweet => console.log("data", tweet.text))
.on("ping", () => console.log("ping"))
.on("error", error => console.log("error", error))
.on("end", response => console.log("end"));

// To stop the stream:
process.nextTick(() => stream.destroy()); // emits "end" and "error" events

and get the following error:

process.nextTick(() => stream.destroy());
^

TypeError: stream.destroy is not a function

Could not find a declaration file for module 'twitter-lite'

import Twitter from 'twitter-lite'

Thank you for this library. I was excited to use it but the above line of code gives the dreaded 'Could not find a declaration file for module' message.

Any help with this issue of mine would be greatly appreciated.

400 CORS Error When Requesting Request-Token from Client-Side

I am following the docs provided to achieve the user auth flow in a local dev environment. But on sending request I get "400" response.
My temporary oauth_callback (and the one pre-registered in Twitter App) is http://127.0.0.1:8001

Here is my code to get Request Token:

import Twitter from "twitter-lite"

const config = {
  consumer_key: `${process.env.TWITTER_CONSUMER_KEY}`,
  consumer_secret: `${process.env.TWITTER_SECRET_KEY}`,
}

getToken = () => new Twitter(config).getRequestToken(`${process.env.TWITTER_CALLBACK_URL}`)

And then on a simple button click, the getToken function gets called as follows:

onClick = event => {
  event.preventDefault()
  getToken()
   .then(res => console.log(res))
   .catch(err => console.error(err))
}

Here are the errors in console:

Failed to load resource: the server responded with a status of 400 ()

Access to XMLHttpRequest at 'https://api.twitter.com/oauth/request_token?oauth_callback=http%3A%2F%2F127.0.0.1%3A8001' from origin 'http://localhost:8001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

TypeError: Network request failed at XMLHttpRequest.xhr.onerror

Support for new twitter Premium Search and LABS API

This lib looks promising, I did not use it yet but it seems maintained comparing to the "Twit" one.

There are few new endpoints available for premium sandbox users (anyone can apply if they already have a dev account on Twitter)
https://developer.twitter.com/en/docs/tweets/search/overview/premium

and also the new "Fitered Stream" which will replace the current "status/filter" endpoint
https://developer.twitter.com/en/docs/labs/filtered-stream/overview

thanks

image upload not working

Hi there,

I can't make this snipped work

const Twitter = require('twitter-lite')

var client = new Twitter({
  consumer_key: 'changed',
  consumer_secret: 'changed',
  access_token_key: 'changed',
  access_token_secret: 'changed'
})

var data = require('fs').readFileSync('./mymedia.png')

client.post('media/upload', { media: data })
  .then((err, media, res) => {
    console.log(err, media, res)
  })

The only thing i got is err = { errors: [ { message: 'Sorry, that page does not exist', code: 34 } ] }

Did I miss something ? I'm on a macOS with nodejs v9 and the latest version of this lib.
Uploading the same image with twurl works like a charm.

Thanks

400 CORS Error when using library from the client

Maybe this is something on my end, but I am following the documentation and I have this code

import Twitter from 'twitter-lite'

const client = new Twitter({
  subdomain: "api",
  consumer_key: process.env.CONSUMER_KEY, // from Twitter.
  consumer_secret: process.env.CONSUMER_SECRET, // from Twitter.
  access_token_key: process.env.ACCESS_TOKEN_KEY, // from your User (oauth_token)
  access_token_secret: process.env.ACCESS_TOKEN_SECRET // from your User (oauth_token_secret)
});

client
  .get("statuses/user_timeline.json?screen_name=twitterapi&count=2")
  .then(results => {
    console.log("results", results);
  })
  .catch(console.error);

but I am getting this response when running my code

Failed to load resource: the server responded with a status of 400 ()

Access to XMLHttpRequest at 'https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=twitterapi&count=2.json' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

TypeError: Network request failed at XMLHttpRequest.xhr.onerror

However, I am able to successfully hit this end point with the same tokens in Postman. Any ideas! Hoping to get this working soon, thanks!

Test credentials

Useful for adding end-to-end tests, e.g. that both client.get('statuses/show/964185606803853314') and client.get('statuses/lookup', { id: '964185606803853314,965299128162029568', map: true, }) return the same tweet. (These are tests I had run locally before my #10 PR).

URL encoded characters when creating a direct message

Hey. This is my first post in this repository. Thank you for building and maintaining this library!

I'm sending a direct message like this:

const messageParams = {
  event: {
    type: 'message_create',
    message_create: {
      target: {
        recipient_id: message.message_create.sender_id
      },
      message_data:  {
        text: `(away) ${randomGreeting()} - i am not on twitter for a bit - email me instead?`
      }
    }
  }
};
client.post('direct_messages/events/new', messageParams).then(response => {
  console.log(response);  // Raw response object.
  usersRepliedTo.add(message.message_create.sender_id);
});

and seeing content like this come through on the Twitter website:

screen shot 2019-01-09 at 6 47 10 am

Am I doing something wrong? Or is data getting URL encoded somewhere along the way that it shouldn't be?

v0.9.3 not working

Version 0.9.3 seems to be missing a dist folder after I npm install it. Had to downgrade back to 0.9.1

1.0 Proposal - Please read & comment!

1.0 Proposal

Twitter Lite is gaining traction and after learning how folks have been using it, here are changes I would make:

Breaking Changes

Turn all request arguments into keyword arguments:

before:
async get (resource, parameters)
async post (resource, body, parameters)

after:
async get({ resource, params })
async post({ resource, body, params })

There won't be any confusion in argument ordering.

Return results, response, error from requests

Currently we return just the results but there are rate limiting errors that are 200-level and don't crash.

New API would look similar to:

return {
  results, // tweets, etc.
  response, // raw response object. You'd have to do your own `response.json()` here
  error // error object (if any)
}

Error handling

Right now twitter-lite forces the consumer to handle errors. We can take care of the errors ourselves. ** Would love to hear what folks think of this one **

Enhancements

Create makeRequest function that handles all requests under the hood. This will reduce code size.

  • create a makeRequest function that handles:
    • get
    • post
    • stream

This will reduce bundle size and make handling errors / responses easier.

  • add flowtype support
    Easier to debug errors

Support other Twitter APIs

Hey twitter-lite,
Really enjoy to work with yours twitter Promise library! Thanks a lot.

Twitter had more APIs,
that I think that you can support with only little changes in the subdomain, endpoint section.
Maybe can be manege through GET params.

For example:
ads-api.twitter.com/5/stats/accounts
subdomain = 'ads-api'
endpoint = '5'

data-api.twitter.com/insights/engagement/totals
subdomain = 'data-api'
endpoint = ''

Twitter Ads API
https://developer.twitter.com/en/docs/ads/general/guides/getting-started

Engagement API
https://developer.twitter.com/en/docs/metrics/get-tweet-engagement/api-reference/post-insights-engagement

Can you support it?

Running a post example

Hello! I am new to this and i would like to know why this code is not working for me:

const Twitter = require('./twitter'); 
const Stream = require('./stream');

const client = new Twitter({
  subdomain: "api",
  consumer_key: process.env.CONSUMER_KEY, // from Twitter.
  consumer_secret: process.env.CONSUMER_SECRET, // from Twitter.
  access_token_key: process.env.ACCESS_TOKEN_KEY, // from your User (oauth_token)
  access_token_secret: process.env.ACCESS_TOKEN_SECRET // from your User (oauth_token_secret)
});

client.post('statuses/update', {
      status: 'Hello there!',
    });

I am getting this error:

UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:8844) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.