paint-brush
Let's Build a Telegram Bot in Rust. Part 1by@exactor
5,394 reads
5,394 reads

Let's Build a Telegram Bot in Rust. Part 1

by Maksim KuznetsovJune 20th, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

This article will be released as a series of articles and will consist of several parts. This article is written entirely in Rust. It is based on the framework Actix-web and Teloxide for developing a bot. The message will still be saved on the telegram side and will be available on many devices. It will allow you to receive notifications about the work performed, errors, and other custom notifications from various sources. This is very useful when working with data collection scripts, or as part of CI-CD when you need to notify the team about a successful deployment.
featured image - Let's Build a Telegram Bot in Rust. Part 1
Maksim Kuznetsov HackerNoon profile picture


This article will be released as a series and will consist of several parts. During the publication of articles, the code will change.


In this article, I would like to address these questions:

  • about the project
  • Why do I do it
  • Main features
  • What did you use
  • Architecture
  • Deploy
  • Why rust


This project is dedicated to creating a notification system based on telegram, which will allow you to receive notifications about the work performed, errors, and other custom notifications from various sources. This is very useful when working with data collection scripts, or as part of CI\CD when you need to notify the team about a successful deployment.


The main features of the project:

  • Create unique URLs to accept a request
  • Sending a message to Telegram Bot
  • Creating a Single URL to Notify Multiple Users
  • Sending files (up to 100MB)
  • Lack of logging
  • Minimum information storage (telegram chat id only)


This project is written entirely in Rust. Actix was used as a backend framework, and Teloxide is a library for developing a bot.


When choosing Teloxide, I took active development as the main criterion, because when analyzing libraries on crates.io, most of them were updated at least 1 year ago. The simplicity of work and development was also taken into account, but this was already secondary. An example of deploying such a bot on Heroku using the Warp Backend framework was a bonus. Therefore, I initially stopped and started development on it, but this did not bring success. Plus, all attempts to connect the bot and backend into one instance (service) ended in failure. But nothing prevents us from doing 2 services. Therefore, for the convenience of development, Actix-web was chosen as the backend framework. Also based on a performance study Actix-web was chosen.

Teloxide:

  • Declarative design. teloxide is based on pattern chains of responsibilities and it helps to create pipelines of message processing in a highly declarative and extensible style.

  • Dialogues management subsystem. The dialog management subsystem is simple and easy to use and does not depend on how and where the dialogs are stored. Supports SQLite, Redis. In this project, I completely try to abandon this feature, because. I consider it unethical to store this kind of information, plus the free version of Heroku restricts storing information, and storing sent messages will not affect the service in any way. The message will still be saved on the telegram side and will be available on many devices.

  • Strongly typed commands. Commands are described by enumerators that are really useful in user input validation.


Actix-web. Main Features for this project:

  • Multipart streams

  • Full Tokio compatibility

  • Keep alive and slow requests handling

  • A lot of documentation and examples

  • The simplicity of development looks like Flask/FastAPI


Example

use actix_web::{get, web, App, HttpServer, Responder};
 
#[get("/hello/{name}")]
async fn greet(name: web::Path<String>) -> impl Responder {
    format!("Hello {name}!")
}
 
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new().service(greet)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

In difference, warp looks more complex.


Example Warp

use warp::Filter;
 
#[tokio::main]
async fn main() {
    // GET /hello/warp => 200 OK with body "Hello, warp!"
    let hello = warp::path!("hello" / String)
        .map(|name| format!("Hello, {}!", name));
 
    warp::serve(hello)
        .run(([127, 0, 0, 1], 3030))
  


Bot commands:

  • /help - display this text.
  • /start - Starting bot use.
  • /add - Adding an existing ID. This command must look like /add <UUID> that exists. By this command, we can share one external id between multiple users.
  • /remove - Removing ID. This command must look like /remove <UUID> that exists. By this command, we unlink the external id from your account.
  • /list - List of all your connected IDs.
  • /new - Adding a new ID. This command returns the new UUID that you can share with users.


Architecture.


It consists of two parts: backend (API) and telegram bot. They are both connected to the database (Postgres). The bot has a full-fledged CRUD, unlike the Backend, because. for the full-fledged work of the backend, we only need a bunch of External ID (UUID) and telegram chat ID for successful sending. Why is an external ID needed? Because The telegram chat id is a regular integer, it's enough just to brute force them. To improve security, an external ID was made, plus this opens up a feature for us that several users can hide behind one external ID.


Why Rust. In most cases, Python is used for developing bots, and for backend development too, but in this project, I would like to use something non-standard, especially since the teloxide library is updated quite often and continues to increase functionality. Backend in Python is, of course, convenient, you can quickly develop, but performance suffers greatly. As mentioned above Actix-web RPS can handle a lot more than Flask/FastAPI/Django.


Deploy.

The plan is to use Heroku as a platform for deploying my service. The choice is due to the fact that Heroku provides additional services (Postgres) and at the same time Rust has its own build-pack, so, as in the case of another language, you will only need to write a Procfile.


For this service, I decided to choose Rust as a practice for developing services (microservices), as well as gaining experience in Rust development. I hope this service will be useful and will help you in your work or pet projects.