Skip to main content

Telegram Bot

Let's dive into the exciting world of building a Telegram bot that sends messages based on Data Stream from Aptos Blockchain!

  1. Step 0: Empty Boilerplate from Go SDK
  2. Step 1: Raw Repo with Boilerplate
  3. Step 2: Added Stream
  4. Step 3: Added Filtering
  5. Step 4: Added Telegram Bot

Step-by-step tutorial

  1. Step 0: Empty Boilerplate from Go SDK -> Step 1: Raw Repo with Boilerplate

    1. First things first, set up your own dApp in the Developer Portal and subscribe to the Apos topic. If you prefer, you can use the following Access Token:

    2. To get started, clone the repository by running the following command in your shell:

      git clone [email protected]:SyntropyNet/aptos-hackathon-2023.git
    3. Navigate to the project directory:

      cd aptos-hackathon-2023/0-GoingOn-empty-repo
    4. Once you're in the right place, let's start with the basic code in Step 0: Empty Boilerplate from Go SDK

    5. Connect to the NATS server by setting the NATS server address ( and adding your Access Token:

      1. Our NATS server address:
      2. add your or our Access token: SAAEPLBNRA56YZTG4XN674JQXJ6L5KKVNUB7XUW5YOFJXKV2PYQ2FJJ4ZU
    6. When you run make serve, you should see the awesome message Connected to NATS server in your terminal!

  2. Step 1: Raw Repo with Boilerplate -> Step 2: Added Stream
    In this step, we'll subscribe to the Aptos topic (stream) and add a handler function to handle the stream. When you receive data, a cool message saying Got msg on: syntropy.aptos.mainnet.tx will be printed in the terminal.

    1. Subscribe to Aptos topic (stream).
      aptosTopic = "syntropy.aptos.mainnet.tx"

    2. Add a handler function to handle the stream.

        service.AddHandler(aptosTopic, func(data []byte) error {
    return PrintData(ctx, service, data)
    1. Add the ability to print a message in the terminal.

      func PrintData(ctx context.Context, service *pubsub.NatsService, data []byte) error {

      message := fmt.Sprintf("Got msg on: %s", aptosTopic)


      return nil
    2. When you type make serve in your terminal, you should be able to see the following message every time there's data flowing:
      Got msg on: syntropy.aptos.mainnet.tx

  3. Step 2: Added Stream -> Step 3: Added Filtering
    Let's enhance our code by filtering the data. We'll use the aptos.json and aptostypes.go files to work with Aptos Transaction Data. By implementing basic filtering and event-based filtering, we'll show only user transactions. The printed message will now include the sender address, receiver address, withdraw amount, and deposit amount. Time to make sense of the data and add some order!

    1. Now we have the data flowing, let's have a look at the files aptos.json and aptostypes.go - first one contains a raw Aptos Transaction Data, second one - "translates" it to the Go stuct, so we can easy reuse it in PrintData function.

    2. Let's use AptosTransaction type in the PritnData function

          var aptosTx AptosTransaction
      if err := json.Unmarshal(data, &aptosTx); err != nil {
      log.Printf("ERROR: %s", err.Error())
      return nil
    3. Ok, so now we can print even more in the message:

          address := aptosTx.Txn.Sender
      depositAddr := ""
      depositAmount := ""
      withdrawAmount := ""
    4. Let's do the basic filtering and show only user_transactions

          if strings.Compare(aptosTx.Txn.Type, "user_transaction") != 0 {
      return nil
    5. Let's filter using events attached to the transaction:

      deposit := false
      withdraw := false

      events := aptosTx.Txn.Events

      for _, evt := range events {
      t := evt.Type
      depositAddr = evt.GUID.AccountAddress
      if strings.Contains(t, "::coin::DepositEvent") {
      if deposit {
      log.Println("Multiple deposit events found!", depositAmount, depositAddr)
      depositAmount = evt.Data.Amount
      deposit = true
      if strings.Contains(t, "::coin::WithdrawEvent") {
      if withdraw {
      log.Println("Multiple withdraw events found!", withdrawAmount)
      withdrawAmount = evt.Data.Amount
      withdraw = true

    6. Let's print the expected message:

      message := fmt.Sprintf("Got msg on: %s, sender address: %s, receiver address: %s, withdraw amount: %s, deposit amount: %s",
      aptosTopic, address, depositAddr, withdrawAmount, depositAmount)

    7. The final message in the terminal after typingmake serve will look like this:

      Got msg on: syntropy.aptos.mainnet.tx, sender address: 0x1d8727df513fa2a8785d0834e40b34223daff1affc079574082baadb74b66ee4, receiver address: 0x54ad3d30af77b60d939ae356e6606de9a4da67583f02b962d2d3f2e481484e90, withdraw amount: 520400, deposit amount: 520400
  4. Step 3: Added Filtering -> Step 4: Added Telegram Bot

    1. We're almost there! The Telegram bot is ready for action. No need to worry about setting it up, it's all done for you! If you want to use your own bot, talk to @botfather on Telegram and he'll guide you. For following along, join the group here:

    2. Input the Telegram bot and chat details:

          botToken    = "6203625523:AAHGYx1judXEh6sq8sOjYSVydFGxMeXgdog"
      chatID = -842363663
    3. Create a bot instance in the main function

      // Add this after the const
      var bot *tgbotapi.BotAPI

      // Paste this in main function after connecting to NATS
      // Create a new bot instance
      var err error
      bot, err = tgbotapi.NewBotAPI(botToken)
      if err != nil {

    4. Handle sending a message.

      func sendTelegramMessage(message string) {
      msg := tgbotapi.NewMessage(chatID, message)

      // Send the message
      _, err := bot.Send(msg)
      if err != nil {
    5. Send a message in the PrintData function.