Building a Twitter API v2 Bot in 15 minutes
While browsing Twitter today, two tweets from my favorite accounts showed up in a row. One, from CMS Holdings, was his trademark "THIRTY THOUSAND UNITED STATES DOLLARS PER BITCOIN" meme. The next, from Count von Count, showed The Count from Sesame Street counting up to another number.
And it clicked. How hard would it be to build a twitter bot to tweet the current Bitcoin price in Count von Count's voice?
TLDR: It took 15 minutes.
Modern web development is amazing for projects like these. With npm, node, and typescript, you can knock something out in no time.
Here's how I did it.
Step 1. Create a node + Typescript app
First up, create a new node project with typescript support. Thanks to sane defaults we can just fire these commands off and think nothing of it.
mkdir count-von-count-bot
cd count-von-count-bot
git init
echo "node_modules\n.env" > .gitignore
npm init -y
npm i -D ts-node @types/node
npx tsc --init
Awesome, we've now got the shell for our bot.
Step 2. Get the Twitter credentials
Next up we have to get Twitter credentials for our bot. This step probably took the longest to figure out since I don't use the Twitter API Dashboard reguarly, especially not since they released API v2.
To get a Bot account, do the following:
- Visit the Twitter Developer Dashboard and click "Sign Up" in the top right.
- Tap "Hobbyist" and "Making a Bot".
You'll have to fill out a Bot application, which should be auto-approved.
Once you're finished, you'll be given an API Key and API Secret. Put these into your .env
file like so:
TWITTER_API_KEY="<your-api-key>"
TWITTER_API_SECRET="<your-api-secret>"
Step 3. Enable write access from the Bot
This is the step that was most opaque. Once you're in the twitter developer dashboard, click into your app from the sidebar under "Projects & Apps"
On the bottom of this page, find "User authentication settings", and click "Edit"
Once there, enable "OAuth 1.0a" access, and select "Read and Write".
For callback URL and website URL, just fill in any valid URLs, since this will be a private app.
Step 4. Get Credentials for your user
On the app page, click "Keys and tokens", click "Generate" for "Access Token and Secret".
Copy those values into your .env
file like so:
TWITTER_ACCESS_TOKEN="<your-access-token>"
TWITTER_ACCESS_TOKEN_SECRET="<your-access-token-secret>"
Once you're done, you should see that these tokens were "Created with Read and Write permissions".
Step 5. On to the code
The code part is pretty simple. NPM offers a bunch of high quality, maintained packages for this project.
First, lets build a script that will pull in the latest Bitcoin price from CoinGecko. CoinGecko has an easy-to-use API which we'll be using here.
We'll be making basic HTTP requests to this endpoint, so let's add fetch
to node
npm i cross-fetch
And build an index.ts file with the following:
import fetch from "cross-fetch"
const fetchBitcoinPrice = async () => {
const resp = await fetch(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=USD"
)
const json = await resp.json()
return json.bitcoin.usd
}
const main = async () => {
const price = await fetchBitcoinPrice()
console.log(price)
}
main()
.then(() => {
process.exit(0)
})
.catch((e) => {
console.error(e)
process.exit(1)
})
And update our package.json
to have a script for this:
{
// ...
"scripts": {
"tweet": "ts-node index.ts"
}
// ...
}
Step 6. Try it out
npm run tweet
You should see something like:
29642
💥 You have the bitcoin price showing up.
One of the beauties here is that it's very low risk if this API request fails. If we don't tweet at some point, who cares? In a real world scenario, you'd want some graceful error handling, but we don't need it here.
Step 7. Make it words
Ok, next up, how do we take this number and make it into words? Hmm... let's try searching npm for number to words
.
Bingo.
Let's add it to our project.
npm i number-to-words
And add it to our index.ts
file:
import fetch from "cross-fetch"
+import { toWords } from "number-to-words"
const fetchBitcoinPrice = async () => {
const resp = await fetch(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=USD"
)
const json = await resp.json()
return json.bitcoin.usd
}
const main = async () => {
const price = await fetchBitcoinPrice()
+ const words = toWords(price)
- console.log(price)
+ console.log(words)
}
main()
.then(() => {
process.exit(0)
})
.catch((e) => {
console.error(e)
process.exit(1)
})
And run it again:
npm run tweet
You should see something like:
twenty-nine thousand, six hundred forty-two
Step 8. Make it a tweet
Now how do we make it Tweet with Twitter API v2. npm gives us twitter-api-v2
as a widely used package, lets give it a shot.
For this, we'll need our API secrets, so let's load from .env
using the dotenv
package.
npm i dotenv twitter-api-v2
And hook it up to our app:
+import "dotenv/config"
import fetch from "cross-fetch"
import { toWords } from "number-to-words"
+import { TwitterApi } from "twitter-api-v2"
const fetchBitcoinPrice = async () => {
const resp = await fetch(
"https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=USD"
)
const json = await resp.json()
return json.bitcoin.usd
}
+const tweet = async (message: string) => {
+ const appKey = process.env.TWITTER_API_KEY;
+ const appSecret = process.env.TWITTER_API_SECRET;
+ const accessToken = process.env.TWITTER_ACCESS_TOKEN;
+ const accessSecret = process.env.TWITTER_ACCESS_TOKEN_SECRET;
+ if (!appKey || !appSecret || !accessToken || !accessSecret) {
+ throw new Error("key or secret is not set");
+ }
+ const twitterClient = new TwitterApi({
+ appKey,
+ appSecret,
+ accessToken,
+ accessSecret,
+ });
+ await twitterClient.v2.tweet(message);
+};
const main = async () => {
const price = await fetchBitcoinPrice()
const words = toWords(price)
- console.log(words)
+ await tweet(words)
}
main()
.then(() => {
process.exit(0)
})
.catch((e) => {
console.error(e)
process.exit(1)
})
OK, now we can tweet, lets give it a shot!
npm run tweet
And check your twitter account! 🎉
Step 9. Automate this
To automate this, we just need a cron job, ideally hosted on someone elses computer for free.
There are plenty of options here, like Render or Vercel, but we stuck to the OG: Heroku.
In Heroku, create a new app, add all of the environment variables from .env
to the UI, and set up the "Heroku Scheduler" addon. Since this is not a web app, you'll never really hit your Dyno usage limits. So set up a bunch of times per day to run your script npm run tweet
.
But! We have one caveat. Since we're running ts-node
directly here, without pre-compiling our Typescript app, we need to move all of our devDependencies
to dependencies
since they will be used in "production" here. Your package.json
should look like:
{
"name": "count-von-bitcoin",
"version": "1.0.0",
"description": "",
"scripts": {
"tweet": "ts-node index.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@types/node": "^17.0.35",
"@types/number-to-words": "^1.2.1",
"cross-fetch": "^3.1.5",
"dotenv": "^16.0.1",
"number-to-words": "^1.2.4",
"ts-node": "^10.8.0",
"twitter-api-v2": "^1.12.2"
}
}
Push your code to heroku and you're done!
Check out our working version of the Bot here: @CountVonBitcoin on Twitter