Bitcoin Telegram Bot
Tags: go, blockchain, programming, opensource
Published: 2021-01-29 17:05:06

Hey There!

So I've been having these 3 things in my head for a couple of months right now:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/99j036z5p1lve5meypp3.jpeg)

- **Golang**: Because I wanted to learn a new Backend language
- **Telegram**: Because __f@!$__ Whatsapp
- **Bitcoin**: Because __f@!$__ ~~The police~~ printed money

So, I got an idea that you would not be able to guess what it was

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1vs72h82vrof76o00foj.jpeg)

That's right, merging them 3 into a single project.

<!-- TABLE OF CONTENTS -->
<details open="open">
  <summary>Table of Contents</summary>
  <ol>
    <li>
      <a href="#bitcoin-telegram-bot">Bitcoin Telegram Bot</a>
    </li>
    <li>
      <a href="hands-on">Hands On</a>
      <ul>
        <li><a href="#bot-father">Bot Father</a></li>
        <li><a href="#bitcoin-api">Bitcoin Api</a></li>
        <li>
          <a href="#a-little-bit-of-code">A little bit of code</a>
            <ul>
        <li><a href="#model">Model</a></li>
        <li><a href="#api-call">Api Call</a></li>
        <li><a href="#handler">Handler</a></li>
        <li><a href="#commands">Commands</a></li>
        <li><a href="#telegram-config">Telegram Config</a></li>
        <li><a href="#the-main-file">The Main File</a></li>
        </ul>
    </li>
      </ul>
    </li>
    <li><a href="#hosting-it">Hosting It</a></li>
    <li><a href="#usage">Usage</a></li>
    <li><a href="final-words">Final Words</a></li>
  </ol>
</details>

## Bitcoin Telegram Bot

Since I've created a couple of bots for the last [Hacktoberfest](https://hacktoberfest.digitalocean.com/) I decided to create a simple telegram bot that could retrieve Bitcoin's Price for the friends that I could convince that Telegram was far superior to Whatsapp.

## Hands on

### Bot Father

Telegram has a Bot of Bots (AKA a Bot-bot). In order to create a new Bot, we will need a TOKEN. So let's head to Telegram, start a conversation with @BotFather and ask for a new Token

![image](https://camo.githubusercontent.com/c07566ea71a37a53d18f8e76987918dbb06289e882ba6ddc83b85f539a6aab7c/68747470733a2f2f692e696d6775722e636f6d2f524338616e48412e706e67)

You can also play a little bit more with @BotFather. For example, you can use the /setcommands to define the uses your bot has on the '/' icon:

```
price - Gets BTC actual price
historic - Gets a percentage between Today's and Yesterday's price
summary - Gets both the price and historic values
```

![image](https://i.imgur.com/ACmSAF1.png)

### Bitcoin API

After getting the TOKEN, I needed Bitcoin's price. And as far as I know, I wasn't going to hardcode it into a Bot or hot-swap its value manually. So I looked for an API that could do that for me. 

![image](https://i.imgur.com/SLl9Hwa.png)

(We all look up for things like this, don't let the impostor syndrome tell you otherwise)

I found [this](https://bitex.la/developers#asset-management) public API that didn't need an user, token nor anything to consult BTC's price, thus my reaction was obvious:

![imagenDeSamuel](https://i.imgur.com/X7OjU7x.png)

With a simple CURL we can get a JSON with the prices. So I decided to use this:

![image](https://i.imgur.com/fvv4ulw.png)

### A little bit of code

(You can skip this section if you don't like boring code. But there are memes)

If you read my previous posts, I've been researching and testing how GO modules worked. I don't like convoluted files where I can't find where things are, so my folder structure looks like this:

![image](https://i.imgur.com/tDI2FJs.png)

#### Model

I began by writing a model for Bitex's response:

```golang
package model

type Price struct {
	Last            float32 `json:"last"`
	PriceBeforeLast float32 `json:"price_before_last"`
	Open            float32 `json:"open"`
	High            float32 `json:"high"`
	Low             float32 `json:"low"`
	Vwap            float32 `json:"vwap"`
	Volume          float32 `json:"volume"`
	Bid             float32 `json:"bid"`
	Ask             float32 `json:"ask"`
}
```
and then utilizing it on another module which was intended only for the API Call

#### API Call

```golang
package utils

import (
	"encoding/json"
	"net/http"

	"github.com/tomassirio/bitcoinTelegram/model"
)

func GetApiCall() (*model.Price, error) {
	resp, err := http.Get("https://bitex.la/api-v1/rest/btc_usd/market/ticker")
	p := &model.Price{}

	if err != nil {
		return p, err
	}

	err = json.NewDecoder(resp.Body).Decode(p)
	return p, err
}
```

Lovely GO handles REST requests like a boss

#### Handler

The handler is a little bit sloppy written. I've been using JavaScript (Which I'm definitely not keen on) to create Discord Bots. So I tried to emulate my previous handlers in GO... so we have this now.

```golang
package handler

import (
	"github.com/tomassirio/bitcoinTelegram/commands"
	tb "gopkg.in/tucnak/telebot.v2"
)

func LoadHandler(b *tb.Bot) map[string]func(m *tb.Message) {
	commandMap := make(map[string]func(m *tb.Message))

	commandMap["/price"] = func(m *tb.Message) {
		res, _ := commands.GetPrice()
		b.Send(m.Chat, "BTC's Current price is: U$S "+res)
	}

	commandMap["/historic"] = func(m *tb.Message) {
		res, g, _ := commands.GetHistoric()
		b.Send(m.Chat, "BTC's Price compared to yesterday is: "+res)
		b.Send(m.Chat, g)
	}

	commandMap["/summary"] = func(m *tb.Message) {
		p, h, _ := commands.GetSummary()
		b.Send(m.Chat, "BTC's Current price is: U$S "+p+"\nBTC's Price compared to yesterday is: "+h)
	}

	return commandMap
}
```

What's most remarkable about this handler is that it's basically a Map from String to a function. This will make sense once we get to the main function.

#### Commands

I only created 3 commands for this bot. It wasn't a deep project, it was mostly for the lulz, so bear with the simplicity of them for now

```golang
func GetPrice() (string, error) {
	p, err := utils.GetApiCall()
	return fmt.Sprintf("%.2f", p.Last), err
}

func GetHistoric() (string, *tb.Animation, error) {
	p, err := utils.GetApiCall()
	l := p.Last
	o := p.Open
	his := ((l - o) / o) * 100
	if !math.Signbit(float64(his)) {
		g := &tb.Animation{File: tb.FromURL("https://i.pinimg.com/originals/e4/38/99/e4389936b099672128c54d25c4560695.gif")}
		return "%" + fmt.Sprintf("%.2f", ((l-o)/o)*100), g, err
	} else {
		g := &tb.Animation{File: tb.FromURL("http://www.brainlesstales.com/bitcoin-assets/images/fan-versions/2015-01-osEroUI.gif")}
		return "-%" + fmt.Sprintf("%.2f", -1*((l-o)/o)*100), g, err
	}
}

func GetSummary() (string, string, error) {
	p, err := utils.GetApiCall()
	l := p.Last
	o := p.Open
	his := ((l - o) / o) * 100
	if !math.Signbit(float64(his)) {
		return fmt.Sprintf("%.2f", p.Last), "%" + fmt.Sprintf("%.2f", ((l-o)/o)*100), err
	} else {
		return fmt.Sprintf("%.2f", p.Last), "-%" + fmt.Sprintf("%.2f", -1*((l-o)/o)*100), err
	}
}
```

#### Telegram Config

'Member that Token we got from @BotFather? Oooh, I 'member. Of course, there's another module for that. How do you think we could find these things if there was no structure?

```golang
type Config struct {
	Token string
}

func LoadConfig() *Config {
	// load .env file from given path
	// we keep it empty it will load .env from current directory
	err := godotenv.Load(".env")

	if err != nil {
		log.Fatalf("Error loading .env file")
	}

	return &Config{Token: os.Getenv("TOKEN")}
}
```

I'm not going to copy the contents of the .env file, but I will show you the .env.example

```
TOKEN=REPLACE_WITH_TOKEN
```

I really hope you weren't expecting anything else.

#### The main file

Our Bot is almost built, now we have to tell Go: Dude, we need this up and running:

```golang
func main() {

	b, err := tb.NewBot(tb.Settings{
		// You can also set custom API URL.
		// If field is empty it equals to "https://api.telegram.org".
		Token:  config.LoadConfig().Token,
		Poller: &tb.LongPoller{Timeout: 10 * time.Second},
	})

	if err != nil {
		log.Fatal(err)
		return
	}

	for k, v := range handler.LoadHandler(b) {
		b.Handle(k, v)
		log.Println(k + "✅ Loaded!")
	}

	b.Start()

}
```

So the program basically gets the TOKEN from the Config module, it checks that there were no errors, and (here comes my favorite part) we cycle through the command map on the Handler module in order to load every single command onto the Bot.

Let's look at it again since it was so satisfying:

![image](https://i.imgur.com/SsXGHKc.png)

### Hosting it

I used my Raspberry 4 to host this bot (as well as the others) but you can use Heroku, AWS, Gcloud or just an old computer that you are not using right now

![image](https://i.imgur.com/6ISblN4.png)

To host it you will need:

- A computer, service or server where to host the bot.
- Git
- Golang v1.13
- A device with Telegram (to use it)

Open a Terminal and copy these commands (Linux & Mac devices):

```bash
cd ~
git clone https://github.com/tomassirio/BitcoinTelegramBot.git
cd ./BitcoinTelegramBot
mv .env.example .env
go get github.com/tomassirio/bitcoinTelegram
go run main.go
```

**Warning:**
This won't work unless you replace the __REPLACE_WITH_TOKEN__ on the .env file with the Token granted by @BotFather

### Usage

Now head over to Telegram and look for the Bot you created on @BotFather and use any of the 3 commands that were configured:

```sh
    * /price : Get's bitcoin's Last price
    * /historic : Gets a percentage between Today's and Yesterday's price
    * /summary : Gets both the price and historic values
```

![image](https://i.imgur.com/3IiV3CA.png)

Also, you can add it to your favorite group (or any group whatsoever) by tapping on the chat's options and then the **Add to Group** Button:

![https://i.imgur.com/4Qs7ejo.png](https://i.imgur.com/4Qs7ejo.png)

### Final Words

I really hoped you enjoyed this post as much as I did writing it. I don't consider myself a good developer, but that's exactly why I'm doing these posts. You don't need to be an expert in order to share your experiences with other people. Also, every opportunity to create more memes should be approached as fast and as creatively as possible.

If you want to check out the repository, you can do so by going to the following [link](https://github.com/tomassirio/BitcoinTelegramBot).

- You can Fork it and do your own version of it. 
- You can tweak it and create a pull request to merge it to the original version. 
- You can do whatever you want with it because it's open-source. 
- If you want to leave a Star on it, I'll be really grateful since it will help me in my professional career. 
- If you want to buy me a beer, the link is ~~in the description below, don't forget to like and subscribe~~ in the footer of the repository

As always, happy ~~balding~~ coding!