summaryrefslogtreecommitdiffstats
path: root/README.md
blob: aad82cf10566122ddcd7a404181e858feb8d9389 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# cepis-telegram

Bot de Telegram para consulta de CEP, sobre a API pública da
[cep.is](https://cep.is) — base oficial dos Correios (DNE), atualizada a cada
quinzena.

Escrito em Go, usa o cliente oficial [`cep.is/go`](https://cep.is/go) e recebe
as mensagens por *long polling*: não precisa de porta aberta, de hostname
público nem de segredo de webhook.

## O que ele faz

| onde | como se usa |
| --- | --- |
| conversa privada | manda o CEP direto: `01001000` ou `01001-000` |
| grupos e canais | `/cep 01001000` — e também `/cep@seubot 01001000` |
| qualquer conversa | `@seubot 01001000` (modo inline, sem precisar adicionar o bot) |

Também responde `/start` e `/help`.

A resposta traz o endereço formatado e, quando o CEP tem coordenada, distingue
os dois casos: com precisão de logradouro sai um link de mapa; quando o CEP é
geral do município, sai um aviso com o raio da área em vez de um pino, porque o
ponto é o centroide da cidade e pode errar dezenas de quilómetros.

## Quando o CEP não existe

Um CEP que falha quase nunca é um número aleatório — é alguém que sabe a cidade
e chutou o resto, quase sempre no `-000` que 957 municípios nunca receberam.
Dizer só "não encontrei" desperdiça a viagem, então o bot diz de que cidade o
número seria:

```
🔍 CEP não encontrado na base dos Correios (DNE).
Ele cairia em Areal/RJ, que tem 118 CEPs, de 25845-001 a 25849-899.
A faixa é esparsa: nem todo número entre os dois é um CEP válido.
Confira os oito dígitos — a base é atualizada a cada quinze dias.
```

Continua sendo um erro, e a resposta é escrita para não ser confundida com um
acerto: não é um endereço, e os dois extremos não são uma lista de CEPs
válidos — os 118 de Areal estão espalhados por quase cinco mil números, que é
para isso que a contagem vai junto. O bot também nunca sugere um CEP de dentro
da faixa.

A cidade vem no próprio 404 da API, no objeto `faixa`. Quando o CEP cai num
intervalo que os Correios não deram a município nenhum, o objeto não vem e a
resposta é a de sempre, sem a linha do meio.

Por enquanto isso custa um GET a mais, e só no caminho do 404: o `cep.is/go`
v0.1.0 joga fora o corpo da resposta 404, e é de lá que a faixa é lida
(`internal/lookup`). Qualquer problema nessa segunda consulta — lentidão, JSON
quebrado, faixa sem município — cai de volta na resposta de sempre. Some quando
a biblioteca expuser o campo.

## Criando o bot

O username e o token são criados por você no
[@BotFather](https://t.me/BotFather) e pertencem a você — este repositório não
contém nem nunca deve conter um token.

1. `/newbot` no @BotFather, escolha nome e username. Guarde o token.
2. `/setinline` no @BotFather, para o mesmo bot, com um texto de placeholder
   (por exemplo `01001000`). Sem isso o modo inline fica desligado.
3. Opcional, `/setcommands`:

   ```
   cep - consulta um CEP
   help - como usar
   ```

O *privacy mode* pode continuar ligado (é o padrão). Em grupos o bot só recebe
os comandos endereçados a ele, que é exatamente o que ele precisa.

## Rodando

```sh
export TELEGRAM_BOT_TOKEN=... # o token do @BotFather
make build
./bin/cepis-telegram
```

Em container:

```sh
docker build -t cepis-telegram .
docker run --rm -e TELEGRAM_BOT_TOKEN=... cepis-telegram
```

Só uma instância por token: o Telegram entrega cada update uma única vez, e dois
processos com o mesmo token disputam os mesmos updates.

## Configuração

Tudo vem do ambiente.

| variável | padrão | o que faz |
| --- | --- | --- |
| `TELEGRAM_BOT_TOKEN` | — | obrigatória; o processo aborta na partida sem ela |
| `CEPIS_BASE_URL` | `https://cep.is` | aponta as consultas para outra instância da API |
| `TELEGRAM_API_BASE_URL` | `https://api.telegram.org` | aponta o bot para outro host da Bot API |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn` ou `error` |

Os logs saem em JSON no stdout. O token nunca aparece neles, e o conteúdo das
mensagens dos usuários não é registrado.

Há um limite por usuário de 5 consultas em rajada e uma a cada 3 segundos
depois disso.

## Desenvolvimento

```sh
make            # lista os alvos
make test       # sobe um Telegram falso e uma cep.is falsa, em processo
make lint       # gofmt -l + go vet, não escreve nada
make format     # aplica o gofmt
make check      # lint + test + build
```

Para testar contra uma cep.is local (o repositório da API sobe uma com
`just up`):

```sh
CEPIS_BASE_URL=http://localhost:8080 TELEGRAM_BOT_TOKEN=... ./bin/cepis-telegram
```

## Licença

ISC. Veja [LICENSE](LICENSE).