Desenvolvendo um Bot do Telegram em Golang para Buscar Eventos da Comunidade Devs Norte no Sympla
Tags: go, bot, programacao, devsnorte
Published: 2024-04-21 15:03:46

Este é um guia de como cria um bot para Telegram capaz de buscar e exibir eventos da Comunidade Devs Norte através da plataforma Sympla. Vamos mergulhar no processo de desenvolvimento passo a passo.

## Pré-requisitos
Antes de começarmos, assegure-se de ter o Go instalado em seu sistema. Além disso, será necessário ter uma no Telegram.

## Passo 1: Configuração do Ambiente
Crie um novo diretório para o seu projeto


![Cria e entrar na pasta](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gr74lo12uyqxaon2mrbx.png)

inicialize um módulo Go usando o seguinte comando:

```
go mod init nome-do-seu-projeto
```
Certifique-se de ter as dependências necessárias instaladas:

```
go get github.com/joho/godotenv
```

```
go get github.com/mymmrac/telego

```

## Passo 2: Configuração do Token

Agora, você precisa obter um token de autenticação do bot do Telegram para interagir com a API do Telegram. Siga estas etapas para obter o token:

1. Acesse o [BotFather ](https://t.me/botfather) no Telegram.
2. Siga as instruções para criar um novo bot. Envie o comando `/newbot` e siga as instruções para fornecer um nome e um username para o seu bot.
3. Após concluir o processo de criação do bot, o [BotFather ](https://t.me/botfather) irá fornecer um token de acesso para o seu bot.
4. Agora, crie um arquivo chamado .env no mesmo diretório do seu código e adicione o token como uma variável de ambiente.

```
TELEGRAM_BOT_TOKEN=seu_token_aqui
```
Substitua seu_token_aqui pelo token fornecido pelo BotFather.

## Passo 3: Escrevendo o Código
1. Crie um arquivo main.go e coloque nele:
2. Importações das bibliotecas necessárias.
3. Definição das estruturas de dados necessárias para lidar com a resposta da API do Sympla.
4. Implementação da função NewBot para criar uma nova instância do bot do Telegram.
5. Implementação do método Start para iniciar o bot.
6. Implementação dos métodos para registrar os comandos do bot e lidar com eventos relacionados a eventos disponíveis e encerrados.
7. Implementação da função fetchSymplaEvents para fazer requisições à API do Sympla e obter eventos futuros ou passados.
8. Implementação da função intArrayToString para converter uma lista de inteiros em uma string.
8. Implementação da função formatEventsMessage para formatar a 9ensagem a ser enviada ao usuário com base nos eventos encontrados.
10. Função main que carrega as variáveis de ambiente, cria uma instância do bot e inicia sua execução.

---
```
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "strings"

    "github.com/joho/godotenv"
    "github.com/mymmrac/telego"
    th "github.com/mymmrac/telego/telegohandler"
    tu "github.com/mymmrac/telego/telegoutil"
)
```
`package main`: Define que este arquivo faz parte do pacote principal do programa.
`Importações`: Importamos as bibliotecas necessárias para o funcionamento do bot, incluindo godotenv para lidar com variáveis de ambiente, telego para interagir com a API do Telegram e telegohandler e telegoutil para lidar com os eventos e mensagens do Telegram.

```
type Bot struct {
    bot   *telego.Bot
    bh    *th.BotHandler
    token string
}
```
`Bot`: Definimos uma estrutura `Bot` que contém o bot do Telegram, o manipulador de bot e o token de autenticação.

```
type SymplaResponse struct {
    Data []Event `json:"data"`
}
```
`SymplaResponse`: Define uma estrutura para armazenar a resposta da API do Sympla.

```
type Location struct {
    Country      string  `json:"country"`
    Address      string  `json:"address"`
    AddressAlt   string  `json:"address_alt"`
    City         string  `json:"city"`
    AddressNum   string  `json:"address_num"`
    Name         string  `json:"name"`
    Longitude    float64 `json:"lon"`
    State        string  `json:"state"`
    Neighborhood string  `json:"neighborhood"`
    ZipCode      string  `json:"zip_code"`
    Latitude     float64 `json:"lat"`
}
```
`Location`: Define uma estrutura para armazenar informações sobre a localização de um evento.

```
type Images struct {
    Original string `json:"original"`
    XS       string `json:"xs"`
    LG       string `json:"lg"`
}
```
`Images`: Define uma estrutura para armazenar URLs de imagens relacionadas a um evento.

```
type StartDateFormats struct {
    Pt string `json:"pt"`
    En string `json:"en"`
    Es string `json:"es"`
}
type EndDateFormats struct {
    Pt string `json:"pt"`
    En string `json:"en"`
    Es string `json:"es"`
}
```
`StartDateFormats` e `EndDateFormats`: Definem estruturas para armazenar formatos de data de início e fim de um evento.
```
type Event struct {
    Name             string           `json:"name"`
    Images           Images           `json:"images"`
    Location         Location         `json:"location"`
    StartDateFormats StartDateFormats `json:"start_date_formats"`
    EndDateFormats   EndDateFormats   `json:"end_date_formats"`
    URL              string           `json:"url"`
}
```
`Event`: Define uma estrutura para representar um evento, incluindo seu nome, imagens, localização, datas e URL.

```
func NewBot(token string) (*Bot, error) {
    bot, err := telego.NewBot(token, telego.WithDefaultDebugLogger())
    if err != nil {
        return nil, err
    }

    updates, err := bot.UpdatesViaLongPolling(nil)
    if err != nil {
        return nil, err
    }

    bh, err := th.NewBotHandler(bot, updates)
    if err != nil {
        return nil, err
    }

    return &Bot{
        bot:   bot,
        bh:    bh,
        token: token,
    }, nil
}
```
`NewBot`: Esta função cria uma nova instância do bot do Telegram, configura os manipuladores de atualização e retorna uma estrutura Bot inicializada.

```
func (b *Bot) Start() {
    defer b.bh.Stop()
    defer b.bot.StopLongPolling()

    b.registerCommands()

    b.bh.Start()
}
```
`Start`: Método que inicia o bot. Ele registra os comandos e inicia o manipulador de bot para receber atualizações do Telegram.

```
func (b *Bot) registerCommands() {
    b.registerBotCommand()
    b.registerEventCommands()
}
```
`registerCommands`: Método que registra todos os comandos disponíveis do bot.

```
func (b *Bot) registerBotCommand() {
	b.bh.Handle(func(bot *telego.Bot, update telego.Update) {
		infoMessage := `
👋 Bem-vindo ao Bot da Comunidade Devs Norte! 🚀

Este bot está aqui para ajudá-lo a encontrar os eventos mais recentes e emocionantes hospedados no Sympla pela nossa comunidade.

Para consultar os eventos disponíveis, basta digitar /disponiveis. E se estiver interessado nos eventos que já passaram, digite /encerrados.

Fique à vontade para explorar e participar dos eventos que mais lhe interessarem!😊
`

		_, _ = bot.SendMessage(tu.Message(
			tu.ID(update.Message.Chat.ID),
			infoMessage,
		))
	}, th.CommandEqual("start"))
}
```
`registerBotCommand`: Método que registra o comando `/start`, que envia uma mensagem de boas-vindas ao usuário quando o bot é iniciado.

```
func (b *Bot) registerEventCommands() {
    b.registerAvailableEventsCommand()
    b.registerClosedEventsCommand()
}
```
`registerEventCommands`: Método que registra os comandos relacionados aos eventos disponíveis e encerrados.
```
func (b *Bot) registerAvailableEventsCommand() {
	b.bh.Handle(func(bot *telego.Bot, update telego.Update) {
		events, err := fetchSymplaEvents("future")
		if err != nil {
			fmt.Println("Erro ao buscar eventos:", err)
			return
		}
		message := formatEventsMessage(events)
		_, _ = bot.SendMessage(tu.Message(
			tu.ID(update.Message.Chat.ID),
			message,
		))
	}, th.CommandEqual("disponíveis"))
}
```
registerAvailableEventsCommand: Método que registra o comando `/disponiveis`, que busca e exibe eventos futuros.

```
func (b *Bot) registerClosedEventsCommand() {
	b.bh.Handle(func(bot *telego.Bot, update telego.Update) {
		events, err := fetchSymplaEvents("past")
		if err != nil {
			fmt.Println("Erro ao buscar eventos:", err)
			return
		}
		message := formatEventsMessage(events)
		_, _ = bot.SendMessage(tu.Message(
			tu.ID(update.Message.Chat.ID),
			message,
		))
	}, th.CommandEqual("encerrados"))
}
```
`registerClosedEventsCommand`: Método que registra o comando `/encerrados`, que busca e exibe eventos passados.

```
func fetchSymplaEvents(eventType string) ([]Event, error) {
    // Define os IDs dos organizadores
    organizerIDs := []int{3125215, 5478152}

    // Define o serviço a ser chamado na API do Sympla com base no tipo de evento
    service := "/v4/search"
    if eventType == "past" {
        service = "/v4/events/past"
    }

    // Monta o corpo da requisição
    requestBody := fmt.Sprintf(`{
        "service": "%s",
        "params": {
            "only": "name,images,location,start_date_formats,end_date_formats,url",
            "organizer_id": %s,
            "sort": "date",
            "order_by": "desc",
            "limit": "6",
            "page": 1
        },
        "ignoreLocation": true
    }`, service, intArrayToString(organizerIDs))

    // Faz a requisição HTTP para a API do Sympla
    resp, err := http.Post("https://www.sympla.com.br/api/v1/search", "application/json", strings.NewReader(requestBody))
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    // Lê a resposta da requisição
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    // Decodifica a resposta JSON em uma estrutura SymplaResponse
    var symplaResp SymplaResponse
    if err := json.NewDecoder(bytes.NewReader(body)).Decode(&symplaResp); err != nil {
        return nil, err
    }

    // Retorna os eventos encontrados na resposta
    return symplaResp.Data, nil
}
```
`fetchSymplaEvents`: Esta função faz uma requisição para a API do Sympla para buscar eventos, com base no tipo de evento (futuro ou passado), e retorna uma lista de eventos encontrados.

```
func intArrayToString(arr []int) string {
	strArr := make([]string, len(arr))
	for i, num := range arr {
		strArr[i] = fmt.Sprint(num)
	}
	return "[" + strings. Join(strArr, ",") + "]"
}
```
`intArrayToString`: Esta função converte uma lista de inteiros em uma string formatada para ser usada na construção do corpo da requisição para a API do Sympla.

```
func formatEventsMessage(events []Event) string {
	message := "#BOT Devs Norte 🤖\n\n\n"
	if events == nil || len(events) == 0 {
		message += "Ops... Nem um evento disponivel no momento, mas não fique triste logo estaremos fazendo mais eventos! 🥺\n\n\n"
	} else {
		message += "🎉 Eventos: 🎉\n\n\n"
		for _, event := range events {
			message += fmt.Sprintf("- %s\n  Local: %s\n  Data: %s\n  URL: %s\n \n\n\n", event. Name, event.Location.City, event.StartDateFormats.Pt, event.URL)
			message += "----------------------------------------\n\n\n"
		}
	}
	return message
}
}
```
`formatEventsMessage`: Esta função formata uma mensagem a ser enviada ao usuário com base nos eventos encontrados.

```
func main() {
    // Carrega as variáveis de ambiente do arquivo .env
    if err := godotenv.Load(); err != nil {
        fmt.Println("Erro ao carregar o arquivo .env:", err)
        os.Exit(1)
    }

    // Obtém o token do bot do Telegram das variáveis de ambiente
    token := os.Getenv("TELEGRAM_BOT_TOKEN")
    if token == "" {
        fmt.Println("Token do bot do Telegram não fornecido")
        os.Exit(1)
    }

    // Cria uma nova instância do bot e inicia sua execução
    bot, err := NewBot(token)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    bot.Start()
}
```
`main`: A função main é o ponto de entrada do programa. Ela carrega as variáveis de ambiente do arquivo .env, obtém o token do bot do Telegram, cria uma instância do bot e inicia sua execução.


## Passo 5: Executar o Código
No diretório do projeto.
execute o código Go:

```
go run main. Go
```
Isso iniciará o bot do Telegram. Agora você pode acessar o bot no Telegram e interagir com ele usando os comandos `/start`, `/disponiveis` e `/encerrados`.

![Bot no Telegram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ptheaobbikprrbh8wqza.jpg)

Você pode encontrar o código completo do projeto no repositório GitHub [aqui](https://github.com/ecsistem/bot-telegram-devs-norte).

Espero que essa explicação detalhada ajude a entender como o código funciona e como cada parte contribui para o funcionamento do bot do Telegram. Se tiver mais alguma dúvida ou precisar de mais alguma explicação, estou à disposição!