How to make a cryptocurrency Telegram bot with Rust and Teloxide

In this post, you will learn how to make a simple cryptocurrency Telegram chat bot similar to the cover of this post.

You can find the original post here.

The Rust programming language will be used mainly for this post.

🦀 Why Rust?

Rust is blazingly fast and memory-efficient: with no runtime or garbage collector, it can power performance-critical services, run on embedded devices, and easily integrate with other languages.

While the code used here is very simple, hope it can be helpful to you to start with the language. You can find code used for this post here.

We will use Teloxide and binance-rs. Please, spend time to read the documentations before you read on.

There are a few Rust Telegram framework but I decided to use Teloxide because you can get help from its authors easily with its Telegram channel.

If you don’t have Rust in your machine yet, please follow the instruction of the official Rust website.

You can also optionally install cargo edit and cargo watch to help you develop better.

$cargo install cargo-edit
$cargo install cargo-watch

I referred it before I write this post. If you are familiar with JavaScript, it can be helpful to read that first.

To test this tutorial, you need at least a Telegram account.

You can also create an account at Binance if you want to use its API more later.

Table of Contents

  1. Set up a Telegram bot with BotFather.
  2. Prepare the development environment to use Teloxide and binance-rs.
  3. Build your Telegram bot to find a cryptocurrency price with Binance.
  4. Conclusion

1. Set up Telegram bot with BotFather

To make a Telegram bot, we need to make an API token and set it up.

First, please visit the BotFather page

Then, use the /help command there. It will show the commands you can use with it.

There are many of them but you will need only a few to start with.

CREATE, LIST, DELETE
/newbot - create a new bot
/mybots - list your bots
/deletebot - delete a bot
UPDATE META DATA
/setdescription - change bot description
/setabouttext - change bot about info
/setuserpic - change bot profile photo

You can start your bot with /newbot command and follow its instruction. Use binance_bot for your botname or use another if you want.

You will have a token if you see the message similar to this.

Save it well and we will use it later at .env file we will make.

Then, you can use /setuserpic to save your bot image.

I used Binance image. But, you can use others.

Congratulations, if you could make it to this point, you are ready to write a Telegram bot with whatever programming language.

2. Prepare the development environment to use Teloxide and binance-rs

Previously, we could set up a Telegram bot and get a API token to remotely control it with the Rust code we will write.

In this part, we will install Teloxide and binance-rs crates and set up a minimal development environment.

Use this commands first to start a Rust project.

$cargo new binance_bot

This will create a few files in binance_bot folder. Edit your Cargo.toml file in it with this.

[package]
name = "binance_bot"
version = "0.1.0"
edition = "2018"
[dependencies]
# 1.
dotenv = "0.15.0"
dotenv_codegen = "0.15.0"
# 2.
teloxide = { version = "0.4", features = ["frunk", "macros", "auto-send"] }
log = "0.4.8"
pretty_env_logger = "0.4.0"
tokio = { version = "1.3", features = ["rt-multi-thread", "macros"] }
binance = { git = "https://github.com/wisespace-io/binance-rs.git" }

It won’t be that different from what REAMD.md file from Teloxide gives you. But, there are some differences you need to know.

1. We will use dotenv instead of manually setting TELOXIDE_TOKEN different from the command given by its documentation.

# Unix-like
$ export TELOXIDE_TOKEN=<Your token here>
# Windows
$ set TELOXIDE_TOKEN=<Your token here>

2. If you don’t include macros to teloxide features, some of its examples won’t work with ‘can’t find derive macro’ etc.

Then, create a .env file with $touch .env and include your token to it.

TELOXIDE_TOKEN=<YOUR TOKEN FROM THE PREVIOUS PART>

src/main.rs file shoud be made for you already.

Paste this code adapted from the official example to use .env file to it.

use dotenv::dotenv;
use std::env;
use teloxide::prelude::*;#[tokio::main]
async fn main() {
run().await;
}
async fn run() {
dotenv().ok(); // Read .env and set env variables with this
teloxide::enable_logging!();
log::info!("Starting dices_bot...");
let bot = Bot::from_env().auto_send(); teloxide::repl(bot, |message| async move {
println!("dice");
message.answer_dice().await?;
respond(())
})
.await;
// INFO binance_bot > Starting dices_bot...
}

Use $cargo run --release and wait a little bit before the Rust compiler ends its job.

Then, while it is working at your console, visit your Telegram bot you made and type whatever you want. It will show somewhat similar to this.

Hope you could make it. You verified that you can use Teloxide crate in your machine. In the next part, we will use binance-rs along with it. You can edit your main.rs file with this to see binance-rs crate will work at your development environment.

use binance::api::*;
use binance::market::*;
fn main() {
let market: Market = Binance::new(None, None);
// Latest price for ALL symbols
// match market.get_all_prices() {
// Ok(answer) => println!("{:#?}", answer),
// Err(e) => println!("Error: {:#?}", e),
// }
match market.get_price("BTCUSDT") {
Ok(answer) => println!("{:#?}", answer),
Err(e) => println!("Error: {:#?}", e),
}
}

Test the example code above with $cargo run --release command. It should show current Bitcoin price for USDT token at your console.

I used Macbook air with M1 chip to compile it. But it failed to compile with arm64 architecture.

Search how to use i386 arch instead if you find an issue with it. You can see which arch your machine uses with $arch.

3. Build a Telegram bot to find a cryptocurrency price with Binance.

In this part, we will modify the commands example from Teloxide.

Please, read and test its code first. Then, you can modify your main.rs file similar to this.

use dotenv::dotenv;
use std::{env, error::Error};
use teloxide::{payloads::SendMessageSetters, prelude::*};
use teloxide::utils::command::BotCommand;
use teloxide::{utils::markdown::link};
use teloxide::types::ParseMode::MarkdownV2;
use binance::api::*;
use binance::market::*;
fn to_uppercase(string: &str) -> String {
string.chars().map(|c| c.to_ascii_uppercase()).collect()
}
// Command examples// /help
// /register
// /price BTC
// /price BTC USDT
// /price btc sudt
// /price BNB BTC
// /price bnb btc
#[derive(BotCommand)]
#[command(rename = "lowercase", description = "These commands are supported:")]
// 1.
enum Command {
#[command(description = "display this text.")]
Help,
#[command(description = "show a Binance sign up page.")]
Register,
#[command(description = "show a cryptcurrency price in USDT by default.")]
Price(String),
}
async fn responses_to_command(
cx: UpdateWithCx<AutoSend<Bot>, Message>,
command: Command,
) -> Result<(), Box<dyn Error + Send + Sync>> {
let market: Market = Binance::new(None, None);
match command {
// 1.
Command::Help => cx.answer(Command::descriptions()).send().await?,
// 2.
Command::Register => {
let register_link = link("https://accounts.binance.com/en/register?ref=SQ86TYC5", "Don't have a Binance account yet? You can register here\\.");
cx.answer(register_link).parse_mode(MarkdownV2).send().await?
},
// 3.
Command::Price(crpytocurrency) => {
let mut iter = crpytocurrency.split_whitespace();
if let Some(first_crypto_symbol) = iter.next() { let second_crypto_symbol = if let Some(second_crypto_symbol) = iter.next() {
println!("There was a second_crypto_symbol.");
second_crypto_symbol
} else {
println!("There was no second_crypto_symbol. Use default.");
"USDT"
};
let target = to_uppercase(
&format!("{}{}", &first_crypto_symbol, &second_crypto_symbol)
);
match market.get_price(target) {
Ok(symbol_price) => {
println!("{:#?}", &symbol_price);
cx.answer(format!("The price you want is {:#?}. ", &symbol_price.price)).await?
},
Err(e) => {
eprint!("{:#?}", e);
cx.answer(format!("Something went wrong. Did you use the correct cryptocurrency pair?")).await?
},
}
} else {
cx.answer("Cryptocurrency symbols were not specified. To start with, you can use /price ETH or /price ETH USDT.").await?
}
}
};
Ok(())
}
#[tokio::main]
async fn main() {
run().await;
}
async fn run() {
dotenv().ok();
teloxide::enable_logging!();
log::info!("Starting the_bot...");
let bot = Bot::from_env().auto_send();
let bot_name: String = "binance_bot".into();
teloxide::commands_repl(bot, bot_name, responses_to_command).await;
}

Use $cargo run — release and visit your bot.

Type /help there, then test a few commands similar to these below.

/help
/register
/price BTC
/price BTC USDT
/price btc usdt
/price BNB BTC
/price bnb btc

Some of them will show results similar to these.

(Well, it is interesting to see its historical drop while writing this post. First one is from when I wrote the code for this and the latter is from when I write this post.)

The payloads here will be command relevant parts and others are for set up. So, I will explain them only.

1. It shows the description texts you define from enum Command part. You need to edit there only.

2. It wasn’t easy to find how to use markdown with Teloxide by reading its documentation. It will be helpful to know Telegram wants you to use \\ for some characters when you use markdown.

3. The user can send various inputs along with /price commands. You can see we can handle them easily with Rust API.

For example, user can send an empty input, input without second cryptocurrency symbol (it is handled by default USDT), input with more than 2 cryptocurrency symbols etc.

You could find the beauty of the Rust language if you thought the code to handle this with other programming languages.

If you have any question with the Teloxide, you can refer to its documenation.

4. Conclusion

In this post, we learnt how to set up Rust env to use Teloxide framework, hope you can make all of this part work without any issue. I included many images to help you follow the example better.

If you are familiar with Binance, you can write more commands to use its account relevant API also.

If you liked the post, please share it with others. I am plan to share more blockchain relevant stuffs. I am interested in ETH and POLKADOT.

If you need to hire a developer, you can contact me.

I will write a blog post to deploy this if many people find this post useful.

Thanks.

--

--

--

Visit www.steadylearner.com/blog for more contents. Contact me with https://www.linkedin.com/in/steady-learner-3151b7164 for job offers.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Microservice Orchestration

WHY IT IS IMPORTANT TO INVEST IN SOFTWARE TESTING

Fixing the ‘Large files detected’ error from GitHub on WSL2

Create Folders inside a repository in Github

ArchiCAD Review

Creating Tags For Resources in Single Account and Multiple Accounts Using Cloud-Custodian

Tortoise ORM migrations with aerich

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Steadylearner

Steadylearner

Visit www.steadylearner.com/blog for more contents. Contact me with https://www.linkedin.com/in/steady-learner-3151b7164 for job offers.

More from Medium

Solidity Tutorial : All about Value Types.

Setup Geth Ethereum Private Blockchain and connect to Remix

Understanding Solidity Assembly: Using `shr` and `shl` for Byte Manipulation

Deploy Solidity Contract on MetisDAO in 20 Minutes — Tutorial with Source Code

How to select right compiler on Remix IDE