Posted on Leave a comment

Upgrading The Discord Twitter Bot To Use V2 API

close up photo of toy bot

Twitter Upgraded Their API & Broke My Bot!!

Imagine my frustration when I get dozens of DMs, emails, and other messages asking when I was going to upgrade my Discord Twitter bot to be compliant with the latest Twitter changes. Like damn bro, I have other things to do lol but alas I can’t let my peeps down. In this blog entry, I will show you what I did to upgrade my codebase to use the Twitter V2 API to communicate with the Discord server to send out my tweets.

v2 of the Discord Twitter bot

Upgrading The Package.json File

We are no longer using the Twit npm package and instead using the twitter-v2 npm package. Open your package.json file and change it to the following:

{
  "name": "discord-twitter-bot",
  "version": "1.0.0",
  "description": "A discord bot that sends messages to a channel whenever a specific user tweets.",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/mastashake08/discord-twitter-bot.git"
  },
  "keywords": [
    "discord",
    "twitter",
    "bot"
  ],
  "author": "mastashake08",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/mastashake08/discord-twitter-bot/issues"
  },
  "homepage": "https://github.com/mastashake08/discord-twitter-bot#readme",
  "dependencies": {
    "discord.js": "^13.8.1",
    "dotenv": "^8.2.0",
    "twitter-v2": "^1.1.0"
  },
  "engines" : {
    "npm" : ">=7.0.0",
    "node" : ">=16.0.0"
  }
}

Changes To The Twitter API

In order to use the stream API, we have to set up stream rules. We want to only show tweets from yourself so in your .env file add a new field

TWITTER_USER_NAME=

Afterward, we listen to the stream pretty much as before. Open up the main.js file and update it to the following.

require('dotenv').config()
const Twit = require('twitter-v2')
const { Client } = require('discord.js');
const client = new Client({ intents: 2048 });


var T = new Twit({
  // consumer_key:         process.env.TWITTER_CONSUMER_KEY,
  // consumer_secret:      process.env.TWITTER_CONSUMER_SECRET,
  // access_token_key:         process.env.TWITTER_ACCESS_TOKEN,
  // access_token_secret:  process.env.TWITTER_ACCESS_TOKEN_SECRET,
  // timeout_ms:           60*1000,  // optional HTTP request timeout to apply to all requests.
  // strictSSL:            true,     // optional - requires SSL certificates to be valid.
  bearer_token:  process.env.BEARER_TOKEN
})

//   //only show owner tweets
async function sendMessage (tweet, client){
  const url = "https://twitter.com/user/status/" + tweet.id;
  try {
    const channel = await client.channels.fetch(process.env.DISCORD_CHANNEL_ID)
    channel.send(url)
  } catch (error) {
        console.error(error);
  }
}

async function listenForever(streamFactory, dataConsumer) {
  try {
    for await (const { data } of streamFactory()) {
      dataConsumer(data);
    }
    // The stream has been closed by Twitter. It is usually safe to reconnect.
    console.log('Stream disconnected healthily. Reconnecting.');
    listenForever(streamFactory, dataConsumer);
  } catch (error) {
    // An error occurred so we reconnect to the stream. Note that we should
    // probably have retry logic here to prevent reconnection after a number of
    // closely timed failures (may indicate a problem that is not downstream).
    console.warn('Stream disconnected with error. Retrying.', error);
    listenForever(streamFactory, dataConsumer);
  }
}

async function  setup () {
  const endpointParameters = {
      'tweet.fields': [ 'author_id', 'conversation_id' ],
      'expansions': [ 'author_id', 'referenced_tweets.id' ],
      'media.fields': [ 'url' ]
  }
  try {
    console.log('Setting up Twitter....')
    const body = {
      "add": [
        {"value": "from:"+ process.env.TWITTER_USER_NAME, "tag": "from Me!!"}
      ]
    }
    const r = await T.post("tweets/search/stream/rules", body);
    console.log(r);

  } catch (err) {
    console.log(err)
  }

  listenForever(
    () => T.stream('tweets/search/stream', endpointParameters),
    (data) => sendMessage(data, client)
  );
}
// Add above rule

client.login(process.env.DISCORD_TOKEN)
 client.on('ready', () => {
   console.log('Discord ready')
   setup()

 })

Congrats, It’s Updated!

That’s pretty much all we had to do to update everything to use the new API. The added benefit is that it won’t show retweets in your discord server like before :0 if you enjoyed this consider becoming a patron on Patreon and help fund in-person coding classes for kids in Louisville, KY!

Leave a Reply