- Gerar link
- X
- Outros aplicativos
- Gerar link
- X
- Outros aplicativos
Como criar um Bot Telegram para atualizar uma planilha financeira no Google Sheets
Tempo de leitura: 10 minutos
Nesse post nós mostramos como automatizar uma planilha financeira pessoal usando Python. Vimos como atualizar as transações a partir de um arquivo de extrato bancário em csv. Todavia, muitas transações no nosso dia a dia não são feitas por meio de um banco. Podem ser feitas em dinheiro, ou usando cartão de crédito. E seja numa planilha como foi apresentado, ou utilizando um app de finanças pessoais, fazer esses lançamentos no momento que ocorrem pode demandar algum tempo, e se deixarmos para fazer posteriormente, isso é passível de esquecimento. E se pudéssemos fazer isso por meio do envio de uma mensagem instantânea? Neste artigo iremos mostrar como criar um bot Telegram usando Python, que recebe essa mensagem e faz o registro da transação na nossa planilha.
![]() |
Fonte: https://br.freepik.com/Pré-requisitos
|
pip install python-dotenv python-telegram-bot gspread google-auth
Iniciando a implementação
Um script para bot Telegram deve ficar executando e a determinado intervalo de tempo, checa se o usuário interagiu enviando algum comando. Se o comando enviado for válido, ou seja, estiver entre os comandos previstos no script, o bot irá responder enviando uma simples mensagem ou executando uma ação mais complexa. Neste script inicial, nosso bot, responde com uma mensagem de boas vindas ao comando /start.
finance_bot.py
from dotenv import load_dotenv import os from telegram import Update from telegram.ext import ( ApplicationBuilder, CommandHandler, ContextTypes) load_dotenv() TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN") # Handlers async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: await update.message.reply_text( ("Seja bemvindo ao Finance Bot! " "Envie /help para ver os comandos disponíveis.") ) # Os demais handlers serão inseridos aqui def main(): if not TELEGRAM_TOKEN: raise RuntimeError("Defina as variáveis de ambiente TELEGRAM_TOKEN.") app = ApplicationBuilder().token(TELEGRAM_TOKEN).build() app.add_handler( CommandHandler("start", start)) app.run_polling() if __name__ == "__main__": main()
A fim de implementar o script do bot iremos precisar do token que você anotou quando criou o seu bot seguindo os passos que foram descritos em post anterior. Recomendamos que este token e outros dados sensíveis sejam mantidos em um arquivo .env e sejam utilizados em nosso programa por meio da biblioteca dotenv, assim como explicamos nesse post.
Após essas definições iniciais, tais como os imports e a inicialização do token, partimos para a criação dos handlers, que é como chamamos as funções que devem ser executadas em resposta aos comandos.
O primeiro handler que implementamos é o start, que será executado quando enviarmos o comando /start. Não é necessário que o nome do handler seja exatamente o mesmo nome do comando. Mais adiante explicamos o porquê.
Na assinatura da função observe que temos uma palavra-chave async. Essa palavra-chave define que uma função é assíncrona, ou seja, que executa de modo concorrente, sem bloquear a thread principal. Geralmente as chamadas à API, acessos a banco de dados, entre outros, precisam desse cuidado, pois são tarefas que demandam tempo para responder.
Ainda no contexto da função start(), a chamada reply_text(), que envia uma mensagem ao usuário, é precedida de um await, que serve para pausar a execução da função até que esta chamada seja resolvida. Um await só pode ser utilizado no contexto de uma função async.
Depois dos handlers, temos a função main(), que efetivamente irá executar o bot. Ela que cria uma instância do bot, adiciona os handlers e executa o script.
Antes de qualquer coisa nós verificamos se há um token definido, depois criamos a instância do bot. Na linha seguinte adicionamos o handler que foi definido. Note que a classe CommandHandler tem dois parâmetros: uma string, que será o comando ao qual o bot irá responder, e um segundo parâmetro que é o nome da função handler. Daí o porquê de comando e função não precisarem ter o mesmo nome.
Finalmente, chamamos o método run_polling(), que inicia o bot e o mantém executando continuamente buscando atualizações do servidor Telegram. Esse método bloqueia a thread corrente até que o processo seja interrompido, teclando CTRL + C.
Execute o script digitando no terminal python finance_bot.py. Se tudo estiver certo procure o bot pelo nome no Telegram e envie /start e você deve receber a mensagem de boas vindas.
Maneiras de executar o Bot Telegram
Antes de prosseguirmos com o nosso código, precisamos falar das maneiras de manter um bot Telegram executando.
Chamar run_polling() no terminal é uma maneira de testar nosso código. Assim, é possível checar se o bot está ouvindo as mensagens e respondendo. O uso de polling é possível fazer em servidores como Render, Railway, entre outros, de uma maneira bem simples, mas tem o inconveniente de o bot sair do ar se o processo parar ou precisar reiniciar.
Uma maneira mais profissional consiste em utilizar um webhook. Esse sistema é utilizado para que dois sistemas se comuniquem, e um deles é comunicado quando um evento acontece, em vez de precisar ficar verificando periodicamente se esse evento aconteceu, tal como no polling.
Tem a vantagem de estar sempre disponível, mas precisa de um endpoint (criado com Flask ou FastAPI, por exemplo) e de um domínio HTTPS e certificado SSL. No próprio Render é possível hospedar um webhook. Já o Ngrok é um serviço que possibilita que o webhook fique rodando em nosso computador. De todo modo, assinar esses serviços no plano gratuito tem o inconveniente do serviço "dormir" devido a algum tempo de inatividade e o Telegram pode não esperar (devido o timeout) e nossa mensagem ao bot vai ficar sem resposta.
Este tema é um pouco avançado e não cabe no escopo deste artigo. Mas se houver interesse podemos apresentá-lo em um post no futuro.
Escrevendo os demais handlers
Os outros handlers do nosso bot deverão responder aos comandos /help, que exibe uma ajuda sobre os comandos disponíveis, /save, responsável por registrar um lançamento na planilha, e /last que vai exibir os últimos 5 lançamentos que foram feitos.
O código de tais handlers serão incluídos logo após o start(). Iniciamos mostrando o help_command(), o qual é semelhante ao start(), e não necessita maiores comentários.
async def help_command( update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: help_text = ( "Comandos disponíveis:\n" "/start - Iniciar o bot\n" "/help - Mostrar esta mensagem de ajuda\n" "/save - Salvar um lançamento na planilha\n" " /save valor/categoria/tipo/descrição (descrição é opcional)\n" " Tipo deve ser 'Despesa' ou 'Receita'\n" " Exemplo: /save 50,00/Alimentação/Despesa/Jantar com amigos\n" "/last - Mostrar as últimas 5 transações\n" ) await update.message.reply_text(help_text)
Antes de comentarmos o próximo handler, save_command(), precisamos falar do módulo utils.py, que tem duas funções auxiliares. Primeiro, conectar_google_sheets(), responsável por fazer a conexão com a API do Google Sheets, a qual já utilizamos nos posts anteriores, e normalizar_string(), que converte strings para um formato específico, o qual possibilita totalizações e comparações na planilha.
utils.py
import gspread import unicodedata import os from google.oauth2.service_account import Credentials def conectar_google_sheets(): scopes = [ "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive", ] creds = Credentials.from_service_account_file( "credenciais.json", scopes=scopes) client = gspread.authorize(creds) return client def normalizar_string(s: str) -> str: normalized = unicodedata.normalize('NFKD', s) folded = "".join([c for c in normalized if not unicodedata.combining(c)]) return folded.casefold()
Este módulo necessita que gspread seja instalado e importado para lidar com planilhas Google. Veja este artigo para saber mais sobre se conectar à API Google Sheets.
A função normalizar_string() precisa do pacote unicodedata, nativo do Python. Ela é reponsável por substituir caracteres acentuados e cedilha, e converter uma string em caixa baixa.
# Acrescente estes imports from datetime import date from utils import ( conectar_google_sheets, normalizar_string) # Handler save async def save_command( update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: # Verifica se o usuário forneceu argumentos if not context.args: await update.message.reply_text( ("Por favor, forneça informações a registrar separadas por /." "Digite no formato: valor/categoria/tipo/descrição." "Descrição é opcional." ) ) return # Obtém os dados fornecidos pelo usuário e separa-os dados = context.args[0].split("/") # Verifica se há pelo menos 3 partes (valor, categoria, tipo) if len(dados) <= 3: await update.message.reply_text( ("Formato inválido. Use: valor/categoria/tipo/descrição" ) ) return # Valida o valor fornecido valor = dados[0].replace(",", ".") try: float(valor) except ValueError: await update.message.reply_text( "O valor deve ser um número. Por favor, tente novamente." ) return cliente = conectar_google_sheets() planilha = cliente.open("Minhas Finanças Pessoais") sheet = planilha.worksheet("Transações") # Adiciona uma nova linha na planilha com os dados fornecidos sheet.append_row( [ date.today().strftime("%d/%m/%Y"), dados[3] if len(dados) > 3 else "", normalizar_string(dados[1]).capitalize(), float(valor), normalizar_string(dados[2]).capitalize(), ] ) # Dá feedback ao usuário await update.message.reply_text( f""" Você registrou: {dados[0]} para {dados[1]} no dia {date.today().strftime('%d/%m/%Y')}. """ )
O comando /save é o mais complexo do nosso bot, até o momento. O comando /help diz como deve ser o formato de /save e dá um exemplo: /save 50,00/Alimentação/Despesa/Jantar com amigos. Ou seja, /save tem argumentos, que são os dados a serem registrados na planilha. A fim de assegurar um mínimo de estrutura nos dados, são necessárias algumas validações, as quais são tratadas pelo handler.
Assim, o primeiro passo é verificar se foram digitados os arguntos após o /save. Depois é verificado se existem pelo menos três informações nos argumentos, visto que Descrição é opcional. Em seguida tratamos o valor numérico da transação, que deve ser um float.
Após tudo validado, a função faz a conexão com a API, e faz um apend_row() desses dados na planilha. Feito isso, o bot nos dá um feedback, avisando que o registro foi feito.
Finalmente incluimos o handler print_last_transactions(), o qual irá retornar os cinco últimos registros feitos na planilha.
async def print_last_transactions( update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: cliente = conectar_google_sheets() planilha = cliente.open("Minhas Finanças Pessoais") sheet = planilha.worksheet("Transações") registros = sheet.get_all_records() ultimos = registros[-5:] # Últimas 5 transações mensagem = "Últimas 5 transações:\n" for registro in ultimos: mensagem += ( f"{registro['Data']}: {registro['Tipo']} de " f"{registro['Valor']} em {registro['Categoria']} - " f"{registro['Descrição']}\n" ) await update.message.reply_text(mensagem)
Esta função se conecta à API, lê todos os registros da planilha em uma lista denominada registros, e pega os últimos 5. Em seguida, é montada uma string com os dados desses últimos registros e então essa string é exibida como retorno de /last.
E agora, precisamos adicionar esses handlers ao bot. Isso é feito na função main(), incluindo estas linhas logo abaixo de app.add_handler():
app.add_handler( CommandHandler("help", help_command)) app.add_handler( CommandHandler("save", save_command)) app.add_handler( CommandHandler( "last", print_last_transactions))
O código completo do bot pode ser encontrado no GitHub.
Conclusão
Este bot foi projetado com o propósito de facilitar o registro de transações no cotidiano. Alguns cuidados, no entanto, precisamos ter. O primeiro deles é não expor dados sensíveis como senhas, tokens, entre outros. Procuramos orientar o uso de dotenv para manter esses dados em variáveis de ambiente. Nossas credenciais da API Google Sheets ainda estão em um arquivo, mas em breve mostraremos como passar a tratar esses dados também no dotenv.
Outra preocupação é que, ao você executar o código, seja pelo método polling ou webhook, seja no seu próprio computador ou em um serviço de nuvem, qualquer usuário do Telegram pode acessá-lo. Existem maneiras de evitar isso e, mesmo não tendo nos referido a isso no artigo, o código está presente na versão que compartilhamos no GitHub.
Por fim, vamos continuar trabalhando nesse projeto. E a próxima funcionalidade que será implementada consiste de automação com uma IA de modo que ela faça uma análise dos dados na planilha e produza um diagnóstico financeiro.
- Gerar link
- X
- Outros aplicativos

Comentários