paint-brush
How to Build a Telegram Bot That Queries Rootstock Data Using Rootstock RPC APIby@ileolami
585 reads
585 reads

How to Build a Telegram Bot That Queries Rootstock Data Using Rootstock RPC API

by IleolamiOctober 1st, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

In this article, you will learn how to build a Telegram bot that fetches data from Rootstock using RootStock API. You must have node installed on your device and knowledge in Javascript. You will be making use of BotFather, Botfather is a Bot creator.
featured image - How to Build a Telegram Bot That Queries Rootstock Data Using Rootstock RPC API
Ileolami HackerNoon profile picture

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.

Tool and Technologies

  1. BotFather
  2. Nodemon
  3. Dotenv
  4. Body parser
  5. Express
  6. Web3.js
  7. Rootstock API Key

Prerequisites

  1. You must have node installed on your device
  2. knowledge in Javascript
  3. Code Editor e.g., VScode

Building Telegram Bot

You will be making use of BotFather; Botfather is a Bot creator.

  1. Click on BotFather, and add it to your telegram.

  2. Click on start, and it’s going to give different commands (study that later).

  3. To create a new bot, input /newbot.

  4. 4. Enter the name of your bot.


5. Enter your username for your bot, and end it with ”Bot.”



  1. After that, you will get a response showing that you’ve created your bot. This response will carry your bot URL and access token.



Create the Necessary Folder and File

  1. Create a new folder for this project,

    mkdir RootstockBot
    cd RootstockBot
    
  2. Initialize npm in the root directory using this command:

    npm init -y
    
  3. Install the following dependencies using this command:

    npm install node-telegram-bot-api web3 dotenv body-parser express nodemon
    

    Brief Explanation:


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


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


    3. dotenv: This library loads environment variables from a .env file into process.env. It helps manage configuration settings and secrets securely.


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


    5. express: This is a fast, unopinionated, minimalist web framework for Node.js. It provides a robust set of features for web and mobile applications.


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


  4. Create a new folder in the root directory and a new file index.js

    cd RootstockBot
    mkdir src
    cd src
    touch index.js
    
  5. Inside the root directory RootStockBot, create .env and .gitignore

    cd -
    touch .env .gitignore
    
  6. Add your Rootstock API Key and Bot Token inside .env file

    RSK_API_KEY=
    BOT_TOKEN=
    
  7. 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:

A file directory of a JavaScript project is displayed in a code editor. The project is named "ROOTSTOCKBOT" and includes directories such as "node_modules" and "src", and files including "index.js," ".env," ".gitignore," "package-lock.json," "package.json," and "README.md".

Writing the Bot Command Script

In this section, you will program the bot by stating the commands. All this action will be executed inside the index.jsfile.


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.

Definition

  1. 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"
      }
    }
    
  2. 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();
    
  3. Declare an asynchronous function called main.

    async function main (){
    
    }
    
  4. 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;
    
  5. 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}`
      );
    
  6. 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',
      };
    

Program Commands

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

  2. 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.”

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

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


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

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


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


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

Conclusion

This article has guided you in creating a telegram bot that queries data from the Rootstock network.

RootstockBot Repo 

RootstockBot

Demo below:

Reference