top of page

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

  • Writer's pictureAhmed Tarek

Learn to Build a Twitter Auto-Retweet Bot With Node.js and TypeScript

A Twitter Bot to retweet any tweets with certain keywords or hashtags built with Node.js, TypeScript, DI and IoC.


Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering
Photo by Rock'n Roll Monkey on Unsplash

In this article, we are going to build a Twitter Bot which would retweet any public tweets including one or more keywords or hashtags.


However, please keep in mind the following:

  1. The Bot we are going to create is just a sample Bot. It is created for educational purposes and could not be used as a fully-fledged production material.

  2. You can use what you would learn from this article, and the shared code, as a starter kit for you to start working on your production code if you wish.

  3. You might question how complicated the solution is for that simple task. I totally agree with you that the same output could have been achieved using a simpler implementation. However, you can think about it as a training and an opportunity to learn new things if you don’t already know them.


Now having that said, buckle up and enjoy the ride.


 

Design Details


First, let me explain some important design details to keep in mind while working on this project.


Patterns

Dependency Inversion (DI) and Inversion Of Control (IoC) Containers:

  • DI principle is applied to all project modules

  • InversifyJs node package is used as the IoC container


Languages and Frameworks


Package Manager

  • npm package manager.


Node Packages

  1. typeScript: for TypeScript support.

  2. ts-node: TypeScript execution and REPL for node.js, with source map and native ESM support.

  3. inversify: A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.

  4. config: Organizes hierarchical configurations for your app deployments.

  5. twit: Twitter API Client for node.

  6. @types/node: Contains type definitions for Node.js.

  7. @types/config: Contains type definitions for node-config.

  8. @types/twit: Contains type definitions for twit.


Modules and Services

  1. ConfigService: This is the service responsible for providing the configurations for the whole system.

  2. TwitterService: This is the service responsible for the integration with Twitter APIs. It wraps Twitter APIs -the ones we need in our project- and present them into an appropriate domain specific language (DSL).

  3. TwitterBotService: This is the service responsible for wrapping the main Bot module.

  4. DependencyInjectionService: This is the service responsible for creating the IoC container and setting the right types bindings.


Dependencies

  1. ConfigService uses the config node package.

  2. TwitterService uses the twit node package and depends on the ConfigService to read the Twitter API keys and tokens from the configurations.

  3. TwitterBotService depends on the TwitterService to perform the proper Twitter API calls.

  4. DependencyInjectionService doesn’t depend on other services but it is aware of each service interface/abstraction and its corresponding implementation.


 

Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering

Solution Structure


  1. config: Directory where different environment configuration files exist. The accepted file names are provided by config node package as follows; default.json, development.json, production.json, and custom-environment-variables.json.

  2. dist: Directory where all the TypeScript files are transpiled to be used later for deployment.

  3. node_modules: Directory where all node modules reside.

  4. src: Directory with all the code.

  5. src/app: Sub-directory with all the code except the main index.ts file.

  6. src/app/config: Directory with the Config Service code.

  7. src/app/dependency-injection: Directory with the Dependency Injection Service code.

  8. src/app/twitter: Directory with the Twitter Service code.

  9. src/app/twitter-bot: Directory with the Twitter Bot Service code.


 

Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering
Photo by DJ Johnson on Unsplash

Prerequisites


Knowledge

  1. TypeScript

  2. Node.js

  3. Git

  4. Heroku


Accounts

  1. Creating a Twitter account for your Bot.

  2. Creating a Twitter application and getting API keys and tokens from Twitter Developer Dashboard.

  3. Creating a Heroku account and application.


Software

  1. VSCode or any IDE


 

Implementation


This is where the action begins, but first, please make sure the software prerequisites are already installed before moving forward.


Creating the Project Solution

  1. Create the main TwitterBot directory for the solution.

  2. Open the command line and browse to the main directory created on the first step.

  3. Run npm init and set your project settings or npm init --yes for defaults.

  4. Run npm install config inversify twit ts-node typescript

  5. Run npm install @types/config @types/node @types/twit

  6. Run tsc init

  7. Run code . to open VS Code.

  8. Create the src folder under the root directory.

  9. Create an empty index.ts under the src directory.

  10. Create the app folder under the src directory.


tsconfig.json

  1. exclude => [“./node_modules”]

  2. compilerOptions.target => “es6”

  3. compilerOptions.lib => [“es6”, “dom”]

  4. compilerOptions.experimentalDecorators => true

  5. compilerOptions.emitDecoratorMetadata => true

  6. compilerOptions.module => “commonjs”

  7. compilerOptions.rootDir => “./src”

  8. compilerOptions.moduleResolution => “node”

  9. compilerOptions.types => [“reflect-metadata”]

  10. compilerOptions.outDir => “./dist”

  11. compilerOptions.esModuleInterop => true

  12. compilerOptions.forceConsistentCasingInFileNames =>true

  13. compilerOptions.strict => true

  14. compilerOptions.skipLibCheck => true



 

package.json

  1. main => “dist/index.js”

  2. scripts => define “tsc”: “tsc”

  3. scripts => define “test”: “echo \”Error: no test specified\” && exit 1"

  4. scripts => define “build”: “rm -rf ./dist/ && npm run tsc — project”

  5. scripts => define “start”: “ts-node ./src/index.ts”

  6. scripts => define “postinstall”: “npm run tsc”

  7. scripts => define “watch”: “tsc -p tsconfig.json -w”

  8. On your command-line, run node --version and replace the “16.13.0” on the line below with your version.

  9. engines => define “node”: “16.13.0”

  10. On your command-line, run npm --version and replace the “8.1.2” on the line below with your version.

  11. engines => define “npm”: “8.1.2”



 

Different Environments Configurations


First, follow these steps to create the folder/file structure and then I will explain what is going on.

  1. Create the config folder under the root directory.

  2. Create an empty default.json under config.

  3. Modify the content of default.json to be {}.

  4. Create an empty development.json under config.

  5. Modify the content of development.json to be {}.

  6. Create an empty production.json under config.

  7. Modify the content of production.json to be {}.

  8. Create an empty custom-environment-variables.json under config.

  9. Modify the content of default.json to be as follows



Let me explain what is going on here:

  1. The folder/file structure we created is consistent with the requirements of the config node package.

  2. The config package checks for the process.env.NODE_ENV variable and loads the corresponding .json file.

  3. The special thing here is the custom-environment-variables.json file. This file is used to keep the sensitive configurations hidden from the source code.

  4. So, when we add “consumerKey”: “TWIT_CONSUMER_KEY” to the custom-environment-variables.json file, this means that whenever the configuration “consumerKey” is being retrieved, its value should be read from the process.env.TWIT_CONSUMER_KEY variable.

  5. Later, we would know how to set these process.env.* variables on the deployment server where it should be safe.

  6. To know more about config node package and its related setup, you can check this link.


 

Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering
Photo by Mitchell Luo on Unsplash

Code Implementation


Now, we are ready for starting coding. I would split the code implementation into parts and for each part, I would explain the code and provide any helpful notes I have.


Config Service

This is the service responsible for providing the configurations for the whole system.


Steps:

  1. Create config folder under src/app/.

  2. Create config-service.ts under src/app/config.

  3. Copy the code below to config-service.ts.



Explanation:

  1. Here we are defining the interface IConfigManager which would provide the required configuration settings.

  2. We are also defining the class ConfigManager which implements the interface IConfigManager.

  3. In the properties implementations, we are using the config node package to read the configurations.

  4. That’s why we needed import config from ‘config’;

  5. We also decorated the class with the @injectable() decorator -defined by InverisfyJS node package- so that the ConfigManager class could be injected whenever needed.

  6. However, this is not enough for InverisfyJS node package to do its job. We also need to define a Symbol to be used as an identification token for this injectable.

  7. That’s why we added export const ConfigManagerLocator = { ConfigManager: Symbol.for(‘IConfigManager’) };

  8. And we needed import “reflect-metadata”; import { injectable } from ‘inversify’;


 

Twitter Service


This is the service responsible for the integration with Twitter APIs. It wraps Twitter APIs -the ones we need in our project- and present them into an appropriate domain specific language (DSL).


Steps:

  1. Create twitter folder under src/app/.

  2. Create twitter-service.ts under src/app/twitter.

  3. Copy the code below to twitter-service.ts.



Explanation:

  1. Here we are defining the interface ITwitterService which would provide the WatchToFilterStream function.

  2. We are also defining the class TwitterService which implements the interface ITwitterService.

  3. Now, this class depends on the ConfigService to use it to get the configuration settings related to Twitter API keys and tokens.

  4. That’s why we are injecting it in the constructor as follows @inject(ConfigManagerLocator.ConfigManager) private configManager: IConfigManager.

  5. This is the way to instruct InversifyJS to inject an instance of IConfigManager.

  6. Then, in the constructor implementation, we are using the injected IConfigManager to retrieve Twitter API keys and tokens.

  7. We are also using the twit node package to perform Twitter APIs calls but first we need to create an object of it passing in the Twitter API keys and tokens we got from the injected IConfigManager.

  8. For the WatchToFilterStream function, we are using twit node package to listen to the public tweets feed, filter them by the keywords passed in, and in case some interesting tweet is found, it would be re-tweeted.

  9. The TwitterService class would be injectable as well. So, we are decorating it with the @injectable() decorator.

  10. Also exported the symbol export const TwitterServiceLocator = { TwitterService: Symbol.for(‘ITwitterService’) };

  11. And imported:


import "reflect-metadata"; import { inject, injectable } from 'inversify'; import { ConfigManagerLocator, IConfigManager } from './../config/config-service'; import Twit from 'twit';

 

Twitter Bot Service

This is the service responsible for wrapping the main Bot module.


Steps:

  1. Create twitter-bot folder under src/app/.

  2. Create twitter-bot.ts under src/app/twitter-bot.

  3. Copy the code below to twitter-bot.ts.



Explanation:

  1. Here we are defining the interface ITwitterBot which would provide the Initialize function.

  2. We are also defining the class TwitterBot which implements the interface ITwitterBot.

  3. Now, this class depends on the TwitterService.

  4. That’s why we are injecting it in the constructor as follows @inject(TwitterServiceLocator.TwitterService) private twitterService: ITwitterService.

  5. The Initialize function is just calling the WatchToFilterStream function passing in the array of keywords to be used to filter the public tweets.

  6. The TwitterBot class would be injectable as well. So, we are decorating it with the @injectable() decorator.

  7. Also exported the symbol export const TwitterBotLocator = { TwitterBot: Symbol.for(‘ITwitterBot’) };.

  8. And imported import “reflect-metadata”; import { TwitterServiceLocator } from ‘../twitter/twitter-service’; import { inject, injectable } from “inversify”; import { ITwitterService } from “../twitter/twitter-service”;.


 

Dependency Injection Service

This is the service responsible for creating the IoC container and setting the right types of bindings.


Steps:

  1. Create dependency-injection folder under src/app/.

  2. Create dependency-injection.ts under src/app/dependency-injection.

  3. Copy the code below to dependency-injection.ts.



Explanation:

  1. Here we are creating the IoC container using const container = new Container();.

  2. Then we are binding each interface/abstraction to its default implementation.

  3. container.bind<IConfigManager>(ConfigManagerLocator.ConfigManager).to(ConfigManager).inSingletonScope();.

  4. container.bind<ITwitterService>(TwitterServiceLocator.TwitterService).to(TwitterService).inSingletonScope();.

  5. container.bind<TwitterBot>(TwitterBotLocator.TwitterBot).to(TwitterBot).inSingletonScope();.

  6. Don’t forget to import import “reflect-metadata”;.

  7. And all the other required imports.

  8. And finally, export default container;.


 

index.ts

This is where our project starts running.



Explanation:

  1. We are using the container to create an instance of ITwitterBot.

  2. const twitterBot = container.get<ITwitterBot>(TwitterBotLocator.TwitterBot);.

  3. Then we are using this instance to call the Initialize function passing in the keywords javascript, dotnet, and nodejs.

  4. Don’t forget to import import “reflect-metadata”;.

  5. And all the other required imports.


 

Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering
Photo by Roman Synkevych on Unsplash

Pushing to Git

Explaining how to use GIT is out of scope of this article. However, one of the important things to keep in mind is the .gitignore file.



I am using the default “node” template from GIT and just added the final line .env.


 

Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering
Photo by Markus Winkler on Unsplash, modified by Ahmed Tarek

Deployment to Heroku


Explaining how to deploy to Heroku is out of scope of this article. However, one of the important things to keep in mind is how to set the process.env variables on Heroku. This could be achieved by two ways.


Through Command-line:

  1. Using the command line, run heroku login.

  2. Follow the on-screen instructions to log in to Heroku successfully.

  3. Run the following but don’t forget to replace the your-something part with the keys and tokens you got from Twitter.


heroku config:set NPM_CONFIG_PRODUCTION=true.heroku config:set NODE_ENV="production".heroku config:set TWIT_CONSUMER_KEY="your-consumer-key".heroku config:set TWIT_CONSUMER_SECRET="your-consumer-secret".heroku config:set TWIT_ACCESS_TOKEN="your-access-token".heroku config:set TWIT_ACCESS_TOKEN_SECRET="your-access-token-secret".

 

Through Heroku Dashboard


Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering
Image by by Ahmed Tarek

Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering
Image by by Ahmed Tarek

See it In Action


Now, if you go to the Twitter account you created for the Bot, you should see some retweets. It could take some time depending on the keywords, but it should happen at last.


 

How to Pause The Bot


Browse to your Heroku account, your app, Resources tab, hit the pencil to edit, and finally turn the radio button off as in the image below.


Learn to Build a Twitter Auto-Retweet Bot With JavaScript (JS) Node.js Nodejs Node TypeScript Dependency Injection (DI), Inversion of Control (IoC), and IoC Containers. Best Practice Code Coding Programming Software Development Architecture Engineering

That’s it, hope you found reading this article as interesting as I found writing it.



Recent Posts

See All

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

bottom of page