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:
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:
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:
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.