How to Make a Password Generator Bot - Part 2 [Golang/go-telegram]
Tags: go, telegram, webdev, beginners
Published: 2023-07-09 09:11:08

##### **Hi again DEV!**

Welcome to _part two_ of a _[nine-part series](https://dev.to/kidesleo/making-a-password-generator-bot-in-9-languages-30fn)_ on creating a simple password generator Telegram bot! In this series, we will build a fully functional bot that generates secure passwords for Telegram users in 9 different programming languages.
  

For more context or a simple introduction, read the _[introductory post](https://dev.to/kidesleo/making-a-password-generator-bot-in-9-languages-30fn)_ of this series. You can also find the source code [here](https://github.com/KidesLeo/9-Languages-1-Bot). I will also try my best to answer any questions below!

If you're a Javascript developer, Boo! Today, we'll make the bot in Golang. Go is a programming language created by Google that's fast, simple, and _very cool_. If you want to impress your peers with your coding skills, having a Go project or two helps.

All you will need for this one is a working [Golang Runtime](https://go.dev/dl/).
 

## Setting Up


Once you're in your project directory, initialize a go module. You can use any name you want for the module.

```go
go mod init go-telegram-bot
```


Once set up, we can install all the dependencies for our little project.

We will use [godotenv](https://pkg.go.dev/github.com/joho/godotenv) to import and use .env files and this [zero-dependencies telegram bot framework](https://github.com/go-telegram/bot).
 

```go
go get github.com/joho/godotenv github.com/go-telegram/bot
```

 
### Set up .env

If you do not have a telegram bot token, please refer to the _[introductory post](https://dev.to/kidesleo/making-a-password-generator-bot-in-9-languages-30fn)_ for instructions on acquiring a new bot token or alternatively, you can follow [this official](https://core.telegram.org/bots/tutorial#obtain-your-bot-token) step-by-step guide.

Once we have everything installed, create a file labeled `.env`, and write into it the following: 

```go
BOT_TOKEN=
```

Make sure to append your bot token after the equals sign, we will be reading from it later. It should look something like this

```go
BOT_TOKEN=4839574812:AAFD39kkdpWt3ywyRZergyOLMaJhac60qc
```

 [godotenv](https://pkg.go.dev/github.com/joho/godotenv) enables developers to load environment variables from a .env file into the process environment. The .env file contains key-value pairs of configuration variables, allowing developers to store sensitive information like API keys, database credentials, or other configuration values securely in a separate file. Since our bot token is of the utmost importance, this is one secure way of storing and accessing it.  

## Let’s start coding!

Now that everything is set up, let’s create a new file called `main.go` and open it in a code editor.

Let’s start by importing our dependencies.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "os"
    "os/signal"
    "strconv"
    "strings" 

    "github.com/go-telegram/bot"
    "github.com/go-telegram/bot/models"

    "github.com/joho/godotenv"
)
```

Now, that's a long list. Although it can seem overwhelming, I guarantee this is not as complicated as it looks. The code will be self explanatory and when in doubt, be sure to check out the [Golang Documentation](https://go.dev/doc/). 
  
Time for our string generation function. 

This function labeled `generateString` will receive a length parameter for the length of the generated string and return a random string containing various characters from a given character set.

  
```go
// String generation function

func generateString(length int) string {

    var characters string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-="

    var newString string

    for x := 0; x < length; x++ { // ++ is a statement.
        newString = newString + string(characters[rand.Intn(len(characters))])
    }
    return "||" + bot.EscapeMarkdown(newString) + "||"
}
```

  

As explained in [Part #0](https://dev.to/kidesleo/making-a-password-generator-bot-in-9-languages-30fn), when a user prompts for a new password, we randomly select a character from the key string a given number of times and concatenate the characters into a new variable, which we then return. 
  
I added one more thing to this function, right at the end. We have to escape all the symbols in the password so that we don’t accidentally apply markdown styles in our using our generated string and that’s just what the `bot.EscapeMarkdown` function does, this function is provided by [telegram-go](https://github.com/go-telegram/bot) which we have already imported.

You might also wonder what the “||” symbols are for. As per the Telegram [documentation](https://core.telegram.org/bots/update56kabdkb12ibuisabdubodbasbdaosd#markdownv2-style), those are the symbols used for spoiler text.

Now for the `main` course. The _main()_ function is a special type of function and it is the entry point of the executable programs. It is here where we define our initial logic.

```go

func main() {

    err := godotenv.Load()

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

    fmt.Println("Running bot...")

    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)

    defer cancel()

    b, err := bot.New(os.Getenv("BOT_TOKEN"))

    if nil != err {
        panic(err)
    }
}
```

We start by loading our `.env` file using [godotenv](https://pkg.go.dev/github.com/joho/godotenv). After passing the error check, we create a context that will be cancelled if the program receives an interrupt signal (such as pressing Ctrl+C). 

_NOTE: This pattern is commonly used to gracefully shut down a server or other long-running process when an interrupt signal is received, allowing the program to clean up any resources and exit cleanly.
The `defer cancel()` statement ensures that the context's cancellation function is called when the function containing the `ctx` variable exits, regardless of how it exits._

We then get our bot instant, initialized with our bot token. 

Let's be sure to add a quick println to show that our program works so far and start the bot.

```go
fmt.Println("Running bot...")
b.Start(ctx)
```

The bot will now run, but... nothing will happen.

Let's give it something to do.  

## Starting /start


To start responding to a `start` command, we have to register a handler (a function) listening for it.

The following is the syntax for registering a handler.

```go
bot.RegisterHandler(handlerType, pattern, matchType, HandlerFunc)
```

As you can see, we need to create a handler function to pass to the register function.

Here's our start handler.

```go
func startHandler(ctx context.Context, b *bot.Bot, update *models.Update) {

    b.SendMessage(ctx, &bot.SendMessageParams{

        ChatID: update.Message.Chat.ID,

        Text:   "Press /generate to get a newly generated password. You can also send a number to receive a password of that length.",

    })
}
```


The `startHandler` function will be invoked every time the start command is called. The function receives the an instance of the bot and context information. Using the instance we can reply to the user with initial instructions on using our bot.

Back in the `main` function we register the handler like so. 

```go
b.RegisterHandler(bot.HandlerTypeMessageText, "/start", bot.MatchTypeExact, startHandler)

fmt.Println("Running bot...")

b.Start(ctx)
```

Since we are handling a text event and expect it to match exactly to "/start" we pass in `bot.HandlerTypeMessageText` and `bot.MatchTypeExact` for the `handlerType` and `matchType` respectively.

## Time to /generate

When it comes to our password generation command, we have to prepare for two scenarios. With and without a length parameter.

If there is no parameter, we respond with a 12-character password. 

If it is specified, all we do is pass that on to the generateString function. Since telegram has a character limit of 4096, we don’t have to worry about a max length.

Unlike the [Typescript/Telegraf.js iteration of this bot](https://dev.to/kidesleo/how-to-make-a-password-generator-bot-part-1-typescripttelegrafjs-26eh)  we can split our logic at handler registration. We can assign two different functions, depending on the `matchType` mention earlier.

For the "/generate" implementation, we will use `bot.MatchTypeExact` and for the "/generate {num}" implementation, we will use `bot.MatchTypePrefix`.

Here is our base `generateHandler`.

```go
func generateHandler(ctx context.Context, b *bot.Bot, update *models.Update) {

    b.SendMessage(ctx, &bot.SendMessageParams{

        ChatID:    update.Message.Chat.ID,

        Text:      generateString(12),

        ParseMode: models.ParseModeMarkdown,

    })
}
```

And here is our `generateHandler` with support for a number parameter.

```go
func generateWithArgumentHandler(ctx context.Context, b *bot.Bot, update *models.Update) {

    commandArgs := strings.Split(update.Message.Text, " ")

    if len(commandArgs) != 2 {

        b.SendMessage(ctx, &bot.SendMessageParams{

            ChatID:    update.Message.Chat.ID,

            Text:      "Incorrect amount of arguments, try /help",

            ParseMode: models.ParseModeMarkdown,

        })
        return
    }

    newLength, err := strconv.Atoi(commandArgs[len(commandArgs)-1])

    if nil != err {

        b.SendMessage(ctx, &bot.SendMessageParams{

            ChatID:    update.Message.Chat.ID,

            Text:      "Be sure to use only integer parameters, try /help",

            ParseMode: models.ParseModeMarkdown,

        })

        return
    }

    b.SendMessage(ctx, &bot.SendMessageParams{

        ChatID:    update.Message.Chat.ID,

        Text:      generateString(newLength),

        ParseMode: models.ParseModeMarkdown,

    })

}
```

Note that we make sure to check that the command has only received one parameter and that the parameter is an integer. We also prepare messages for incorrect usage for the commands.

What is left is registering these handlers back in `main`.

```go
b.RegisterHandler(bot.HandlerTypeMessageText, "/generate", bot.MatchTypeExact, generateHandler)

//Generate handler with arguments
b.RegisterHandler(bot.HandlerTypeMessageText, "/generate", bot.MatchTypePrefix, generateWithArgumentHandler)
```
  

## /help me get going!

Last and kind of least, we have the `/help` command. Keeping it simple, here is the handler for that.


```go
func helpHandler(ctx context.Context, b *bot.Bot, update *models.Update) {

    b.SendMessage(ctx, &bot.SendMessageParams{

        ChatID: update.Message.Chat.ID,

        Text: "Use the /generate command to create an AlphaNumeric 12 character password\\. You can provide an argument to set the length of password," +

            "\nfor example,\n `/generate 15`   to generate a string of 15 character\\.",

        ParseMode: models.ParseModeMarkdown,

    })

}
```

Having flavored it up a bit with Markdown, the help text is ready to go.

And as before, we register it up in `main`.

```go
b.RegisterHandler(bot.HandlerTypeMessageText, "/help", bot.MatchTypeExact, helpHandler)
```

Well, let’s run it then.

```go
go run main.go
```

  
Your console should display only the following
 

```go
Running bot...
```

Try texting your bot on Telegram and everything should be working perfectly.

## Debugging  

If you are experiencing confusing console errors and various other issues, feel free to ask down below, I will try my best to respond to everyone.

If you see `Running bot…` in your console but you’re getting no responses from your bot, try adding a print in the code blocks for each command to pinpoint the problem. Again, feel free to ask for help down below. 

## Closing Credits

We did it!   

_2 done_, **7** to go. *Thanks for reading DEV!*

If you have any questions, suggestions, or tips, please feel free to comment below. I will respond as quickly as I can.
 
I truly hope you enjoyed reading all of that and gained something from it. Hopefully both, but either is good.  

In case you are curious about this series and whatever else I’ll be up to, drop a follow and a like, it’s much appreciated!