Can Bots be useful in blockchain?
Bots are becoming relevant nowadays due to their usefulness. You may have noticed people using bots to gain visibility, send messages to thousands of people, and gather information. They mostly run on messaging platforms.
So, the answer to the question is YES. These bots are useful. They help get first-hand information about blockchain networks like Ethereum and Bitcoin, without visiting block explorers or wallets to check for information.
In this article, you will learn how to build a Telegram bot that fetches data from Rootstock using RootStock API.
You will be making use of BotFather; Botfather is a Bot creator.
Click on BotFather, and add it to your telegram.
Click on start
, and it’s going to give different commands (study that later).
To create a new bot, input /newbot.
4. Enter the name of your bot.
5. Enter your username for your bot, and end it with ”Bot.”
Create a new folder for this project,
mkdir RootstockBot
cd RootstockBot
Initialize npm in the root directory using this command:
npm init -y
Install the following dependencies using this command:
npm install node-telegram-bot-api web3 dotenv body-parser express nodemon
Brief Explanation:
node-telegram-bot-api: This is a library that interacts with the Telegram Bot API. It allows you to create and manage Telegram bots, handle messages, and perform various bot-related tasks.
web3: This is a JavaScript library for interacting with the Ethereum virtual machine (EVM). It provides an interface to interact with smart contracts, send transactions, and query blockchain data e.g., Rootstock.
dotenv: This library loads environment variables from a .env
file into process.env
. It helps manage configuration settings and secrets securely.
Body-parser: This middleware parses incoming request bodies in a middleware before your handlers, available under the req.body
property. It supports parsing JSON, URL-encoded, and raw bodies.
express: This is a fast, unopinionated, minimalist web framework for Node.js. It provides a robust set of features for web and mobile applications.
nodemon: This is a utility that monitors for any changes in your source and automatically restarts your server. It helps in development by reducing the need to manually restart the server after changes.
Create a new folder in the root directory and a new file index.js
cd RootstockBot
mkdir src
cd src
touch index.js
Inside the root directory RootStockBot
, create .env
and .gitignore
cd -
touch .env .gitignore
Add your Rootstock API Key and Bot Token inside .env
file
RSK_API_KEY=
BOT_TOKEN=
Inside the .gitignore
file, add the following:
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
*.env
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Your folder should look like this:
In this section, you will program the bot by stating the commands. All this action will be executed inside the index.js
file.
Before you start, you need to define how to run the app, and specify the kind of module you are using. In this case, you will be using the ECMAScript modules (ESM) and npm start
to run the app.
Add the following to package.json
file:
{
"name": "rootstockbot",
"version": "1.0.0",
"main": "index.js",
"type": "module",//ECMAScript modules
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon src/index.js" //command to run the app
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"body-parser": "^1.20.3",
"dotenv": "^16.4.5",
"express": "^4.21.0",
"node-telegram-bot-api": "^0.66.0",
"nodemon": "^3.1.7",
"web3": "^4.13.0"
}
}
Import the dependencies and environment variables.
import dotenv from "dotenv";
import express from "express";
import bodyParser from "body-parser";
import TelegramBot from "node-telegram-bot-api";
import Web3 from "web3";
// Load environment variables from .env file
dotenv.config();
Declare an asynchronous function called main.
async function main (){
}
Initiate an express app, and set the port number on which the Express server will listen for incoming HTTP requests.
const app = express();
const port = 3000;
Add middleware to parse incoming request bodies as JSON, initialize the telegram bot and Rootstock RPC provider.
// Parse the request body as JSON
app.use(bodyParser.json());
// Create a TelegramBot instance with your bot token
const botToken = process.env.BOT_TOKEN;
const bot = new TelegramBot(botToken, { polling: true });
const web3 = new Web3(
`https://rpc.testnet.rootstock.io/${process.env.RSK_API_KEY}`
);
Defines an object stating various commands that the Telegram bot can respond to.
const commands = {
start: '/start - Start the bot',
balance: '/balance <address> - Check the balance of a wallet',
transactions: '/transactions <address> - Get recent transactions of a wallet',
latestblock: '/latestblock - Get the latest block number',
help: '/help - Show this help message',
gasprice: '/gasprice - Get the current gas price',
};
Define the help message:
const sendHelpMessage = (chatId) => {
const helpMessage = `You can use the following commands:\n` +
Object.values(commands).join('\n');
bot.sendMessage(chatId, helpMessage);
};
bot.onText(/\/help/, (msg) => {
const chatId = msg.chat.id;
sendHelpMessage(chatId);
});
sendHelpMessage
constructs and sends a help message listing available commands to a specified chat. It takes chatId
as a parameter to specify the chat where the message will be sent.
The /help
command tells the bot to respond when it receives a message matching /help
. It extracts the chatId
from the message and calls sendHelpMessage
to send the help message to that chat.
Define the start command.
bot.onText(/\/start/, (msg) => {
const chatId = msg.chat.id;
bot.sendMessage(chatId, `Hello! Welcome to the RootstockBot.`);
});
This provides a friendly welcome when the bot is being launched. In this case, if the user clicks on START
, they will get “Hello! Welcome to the RootstockBot.”
Define the Balance Checker command
bot.onText(/\/balance (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const walletAddress = match[1];
try {
const balance = await web3.eth.getBalance(walletAddress);
const balanceInEth = web3.utils.fromWei(balance, "ether");
bot.sendMessage(chatId, `The balance of ${walletAddress} is ${balanceInEth} RBTC.`);
} catch (error) {
bot.sendMessage(chatId, `Failed to fetch balance for ${walletAddress}. Please try again later.`);
console.error(error);
}
});
await web3.eth.getBalance(walletAddress)
: This method fetches the wallet address from the Rootstock network. It takes the inputted wallet address as a parameter.
web3.utils.fromWei(balance, “ether”)
: This method converts the balance
from wei to ether e.g., if the balance is 195881312000000, it converts it to 0.000195881312.
Define Current gas price command
// Handle /gasprice command
bot.onText(/\/gasprice/, async (msg) => {
const chatId = msg.chat.id;
try {
const gasPrice = await web3.eth.getGasPrice();
const gasInEth = web3.utils.fromWei(gasPrice, "ether");
bot.sendMessage(chatId, `The current gas price is ${gasInEth} eth.`);
} catch (error) {
bot.sendMessage(chatId, `Failed to fetch gas price. Please try again later.`);
console.error(error);
}
});
await web3.eth.getGasprice()
method fetches the current gas price and converts the price to ether using the web3.utils.fromWei(gasprice, “ether”)
method.
Defines the transactions command
// Handle /transactions command
bot.onText(/\/transactions (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const walletAddress = match[1];
try {
const transactionCount = await web3.eth.getTransactionCount(walletAddress);
bot.sendMessage(chatId, `${walletAddress} has made ${transactionCount} transactions.`);
} catch (error) {
bot.sendMessage(chatId, `Failed to fetch transactions for ${walletAddress}. Please try again later.`);
console.error(error);
}
});
This command defines the number of transactions performed by an account. It uses the await web3.eth.getTransactionCount
method taking the inputted wallet address and returning the number of transactions.
Define the latest Block Number
bot.onText(/\/latestblock/, async (msg) => {
const chatId = msg.chat.id;
try {
const latestBlock = await web3.eth.getBlockNumber();
bot.sendMessage(chatId, `The latest block number is ${latestBlock}.`);
} catch (error) {
bot.sendMessage(chatId, 'Failed to fetch the latest block number. Please try again later.');
console.error(error);
}
});
This command uses the await web3.eth.getBlockNumber()
to fetch the latest number on Rootstock network.
Define unrecognized command
// Handle unrecognized commands
bot.on('message', (msg) => {
const chatId = msg.chat.id;
const text = msg.text;
if (!Object.keys(commands).some(cmd => text.startsWith(`/${cmd}`))) {
bot.sendMessage(chatId, `Sorry, I didn't recognize that command. Please use the following commands:\n` +
Object.values(commands).join('\n'));
}
});
This event listener checks incoming messages to see if they match any available commands in the commands
object and sends an error message with available commands.
To ensure that the server starts and listens for incoming requests.
Add the following:
// Start the Express server
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
All the command code are to be written inside the main()
function.
Add the following to handle errors that might occur during the execution of the main
function.
main().catch(console.error);
So, your script should look like this:
import dotenv from "dotenv";
import express from "express";
import bodyParser from "body-parser";
import TelegramBot from "node-telegram-bot-api";
import Web3 from "web3";
// Load environment variables from .env file
dotenv.config();
async function main() {
const app = express();
const port = 3000;
// Parse the request body as JSON
app.use(bodyParser.json());
// Create a TelegramBot instance with your bot token
const botToken = process.env.BOT_TOKEN;
const bot = new TelegramBot(botToken, { polling: true });
const web3 = new Web3(
`https://rpc.testnet.rootstock.io/${process.env.RSK_API_KEY}`
);
const commands = {
start: '/start - Start the bot',
balance: '/balance <address> - Check the balance of a wallet',
transactions: '/transactions <address> - Get recent transactions of a wallet',
latestblock: '/latestblock - Get the latest block number',
help: '/help - Show this help message',
gasprice: '/gasprice - Get the current gas price',
};
const sendHelpMessage = (chatId) => {
const helpMessage = `You can use the following commands:\n` +
Object.values(commands).join('\n');
bot.sendMessage(chatId, helpMessage);
};
bot.onText(/\/help/, (msg) => {
const chatId = msg.chat.id;
sendHelpMessage(chatId);
});
// Handle /start
bot.onText(/\/start/, (msg) => {
const chatId = msg.chat.id;
bot.sendMessage(chatId, `Hello! Welcome to the RootstockBot.`);
});
// Handle /balance command
bot.onText(/\/balance (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const walletAddress = match[1]; // Extract the address from the command
try {
const balance = await web3.eth.getBalance(walletAddress);
bot.sendMessage(chatId, `The balance of the address ${walletAddress} is ${web3.utils.fromWei(balance, 'ether')} ETH.`);
} catch (error) {
bot.sendMessage(chatId, 'Failed to fetch the balance. Please try again later.');
console.error(error);
}
});
// Handle /gasprice command
bot.onText(/\/gasprice/, async (msg) => {
const chatId = msg.chat.id;
try {
const gasPrice = await web3.eth.getGasPrice();
const gasInEth = web3.utils.fromWei(gasPrice, "ether");
bot.sendMessage(chatId, `The current gas price is ${gasInEth} eth.`);
} catch (error) {
bot.sendMessage(chatId, `Failed to fetch gas price. Please try again later.`);
console.error(error);
}
});
// Handle /transactions command
bot.onText(/\/transactions (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const walletAddress = match[1];
if (!walletAddress) {
bot.sendMessage(chatId, 'Please provide a valid address.');
return;
}
try {
const transactionCount = await web3.eth.getTransactionCount(walletAddress);
bot.sendMessage(chatId, `${walletAddress} has made ${transactionCount} transactions.`);
} catch (error) {
bot.sendMessage(chatId, `Failed to fetch transactions for ${walletAddress}. Please try again later.`);
console.error(error);
}
});
// Handle /latestblock command
bot.onText(/\/latestblock/, async (msg) => {
const chatId = msg.chat.id;
try {
const latestBlock = await web3.eth.getBlockNumber();
bot.sendMessage(chatId, `The latest block number is ${latestBlock}.`);
} catch (error) {
bot.sendMessage(chatId, 'Failed to fetch the latest block number. Please try again later.');
console.error(error);
}
});
// Handle unrecognized commands
bot.on('message', (msg) => {
const chatId = msg.chat.id;
const text = msg.text;
if (!Object.keys(commands).some(cmd => text.startsWith(`/${cmd}`))) {
bot.sendMessage(chatId, `Sorry, I didn't recognize that command. Please use the following commands:\n` +
Object.values(commands).join('\n'));
}
});
// Start the Express server
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
}
main().catch(console.error);
Push your folder to GitHub, and deploy your application using Render.
You can add more commands using the avaliable RPC API Methods.
This article has guided you in creating a telegram bot that queries data from the Rootstock network.
Demo below: