Creating and deploying a Telegram bot using Railway

A step-by-step guide to creating a Telegram bot using NodeJS and TypeScript and deploying it using Railway

Creating and deploying a Telegram bot using Railway

A step-by-step guide to creating a Telegram bot using NodeJS and TypeScript and deploying it using Railway

This is the third post in a series of posts documenting my journey building Botler - your personal AI butler. In this post, I will create a new Telegram bot from scratch and then deploy it to production using Railway.

As usual, before we begin, let's take a lot at what we've done so far. In the first post, we scaffolded our NextJS app with a database, APIs, and authentication. And then in the second post, we set up a product for sale on Gumroad and then put things in place to dynamically create user accounts whenever someone makes a purchase.

Now, we'll go over the process of creating a Telegram bot from scratch using NodeJS, TypeScript, and Telegraf and then deploying it to production using Railway.

Scaffolding our NodeJS app

Normally, to do this, I'd start by creating a new folder and running yarn init to initialize my package.json. Once that's done, I'd have to set up TypeScript and all the libraries I need. But, we aren't going to do all that this time around.

One of the advantages of using Railway is that they have several starter projects that we can simply deploy to their platform with the click of a button (for real!). In our case, we want to use something with Express and TypeScript, and luckily for us, they have one which works comes with those and nodemon and body-parser already set up!

Now, all we have to is click the Deploy on Railway button and we should be good to go.

Deploy on Railway
Deploy on Railway

Another cool thing about Railway which you may or may not have noticed in the GIF above is that all your environments live in the cloud. Yep, that means you have everything set up to deploy to production from the moment you set up your app. This takes away so much pain and additional work you'd have to do if you left it all for after you were done developing.

Now that we've set up the starter under our projects, let's clone it and use the Railway CLI to connect our local set up to our deployment on the dashboard.

Railway CLI
Railway CLI

Connecting to our Telegram bot

A small note, I am not going to go over the process of creating the bot because I already have one set up and also because it is very well documented on the official website. You can read all about it here.

Okay, now that our app has been scaffolded and our deployment pipeline set up, we can focus on connecting our app to our Telegram bot. To do this, we're going to use Telegraf. In the past, I've used node-telegram-bot-api but we're going with Telegraf because their latest version has Typescript typings and also because they have a Scenes flow built-in which we can use to create interactive commands.

There are many other options available in several other languages. You can view all of them here and choose the one which works best for you.

To install Telegraf, we'll run:

yarn add telegraf
Add Telegraf

Once we've added the package, we'll write the bare minimum code to get our bot running.

import { Telegraf } from 'telegraf'

const bot = new Telegraf(process.env.BOT_TOKEN)

bot.start((ctx) => ctx.reply('Welcome'))

bot.launch()
index.js

In the snippet above, we import the package and initialize it with our bot token. The token comes from an environment variable. After that, we pass a .start function to the bot which runs whenever someone has their first interaction with the bot or messages our bot with the command /start. Upon configuring the command, we launch the bot.

A small note about the environment variables with Railway. You can simply add them to your environment in your dashboard and then Railway automagically deploys your app making the environment variables available for you. And, whenever you want to run the app locally, just run railway run yarn dev and your environment variables will directly be provided to the app!

/start command
/start command

Setting up a production environment

We have a basic bot running in development. Now, we want to add a production environment to our project and deploy our bot to production. To do that, we can simply use our project dashboard in Railway.

In the image on the left, we add our production environment, and then in the image on the right, we select our production environment and add the BOT_TOKEN variable to it. You can read all about environments in the docs here.

Next, we're going to select our production environment from the CLI and deploy to it so we can use our bot in production.

Deployment flow
Deployment flow

In the GIF above, we run railway environment see all our environments and select the production environment. Then, we run railway up to create a new deployment. Once the deployment is complete, we message our bot to make sure that everything is working. You can read all about the CLI and all the commands it comes with here.

Tiny note time: Because we have multiple instances of our bot running per environment, we might get duplicate responses to our commands (especially relevant if you ever consider building a Discord bot). So, Railway has this really cool feature called Singleton deploys which ensures that you only have one deployment running per environment by automagically taking down older deployments every time you deploy.

Bonus: Getting notified on Telegram on deployment

On most days, that'd be where I'd end the article because that's all we were supposed to do here but I am feeling adventurous today. We've set up our Telegram bot and we have our deployment pipeline going. What we're going to do next, is to simply use our bot and have it notify us whenever the status of a build or deployment changes. To do this, we're going to make use of the webhooks Railway provides.

We're going to start by creating a new route that will receive the webhook:

/*
  "/webhooks/railway/" is the URL we used when we
  configured our webhook
/*
app.post("/webhooks/railway/", async (req, res) => {
  res.status(200)
})
Receiving the webhook

Next, we will parse the fields from the webhook and send ourselves a message whenever the webhook is received.

app.post("/webhooks/railway/", async (req, res) => {
  const { type, project: { name } } = req.body

  /*
    To get your Telegram ID, you can simply console.log and
    read the ctx.message.from whenever you message the bot
  */
  bot.telegram.sendMessage(MY_TELEGRAM_ID, `${type}: ${name}`)
  res.status(200)
})
Sending a message

And with that, we should receive a message on Telegram whenever we create a deployment on Railway!

Deployment webhook demo
Deployment webhook demo

I added a Github trigger on Railway to demonstrate the entire flow seen in the GIF above. So now, whenever I push to Github, it deploys both my development and production environment and also notifies me on Telegram about the status of the build!

Closing

Not as long as the first two but we got a lot done here as well. We've set up our Telegram bot and the deployment pipeline for it along with notifications about the status of our deployment. We can also use the webhook setup later to provide this as a feature to our other users!

If you want to build your own Telegram bot, I have created a starter project on Railway which lets you deploy your own barebones bot with the click of a button. All you need is a token from @botfather.

In the next post, we'll work on linking the bot to the dashboard so users can configure it according to their requirements. And as always, feel free to let me know if you have any feedback or questions about this post here. I am always available on Twitter and will try to get back to you as soon as possible.

And, not to forget, we're very close to having a barebones MVP ready (probably the next post) and there are only 24 (at the time of writing) pre-order spots available for a lifetime deal for the bot for only $10! So don't miss out and grab yours here!

Faraz Patankar

Probably obsessing over fantasy football, figuring out what to eat for my next meal or working on my next great (soon to be abandoned) side project but hello to you too! 🤗

More posts from this author