Seu Assistente Financeiro Pessoal no Telegram: Integrando IA ao Bot do Google Sheets

Criar um assistente financeiro pessoal pode ser um grande desafio. Mas com a combinação de Python, Telegram Bot API, Google Sheets e OpenAI, você pode construir uma ferramenta capaz de analisar despesas e receitas, organizar planilhas, gerar diagnósticos financeiros automáticos usando IA e até ajudar outras pessoas a cuidar do dinheiro. 

Neste artigo, você vai aprender passo a passo como integrar uma Inteligência Artificial ao bot de Telegram criado no post anterior, a qual será capaz de atuar como um verdadeiro assistente financeiro. 

Fonte: https://br.freepik.com/

Pré-requisitos

  • Recomendamos que você já tenha criado o Bot Telegram descrito neste post
  • Bibliotecas pandasopenai
  • Conta na OpenAI
Em posts anteriores aprendemos como criar um Bot Telegram, que faz registros de transações numa planilha Google Sheets. Vamos utilizar este bot e implementar um upgrade, que adiciona a funcionalidade da ferramenta fazer um diagnóstico das nossas finanças e oferecer insights com base nesse diagnóstico. Essa nova funcionalidade do bot será implementada utilizando uma IA, mais especificamente um modelo da OpenAI.

Considerando que vamos partir do código funcionando, criado no artigo anterior, será necessário instalar as bibliotecas:

pip install openai pandas

Desde que você já possua uma conta, a fim de obter um token da API OpenAI, precisamos criá-lo no dashboard da plataforma.

Dashboard da OpenAI Platform


Clique no botão Create new secret key no canto superior direito. Opcionalmente, você pode dar um nome à sua chave para identificação (ex: "Meu Projeto BOT") e, em seguida, clique em Create key. O seu token (chave secreta) será exibido em uma janela. Copie-o imediatamente e guarde-o em um local seguro, pois, por motivos de segurança, você não conseguirá vê-lo novamente depois de fechar a janela.

A utilização da API da OpenAI geralmente envolve custos baseados no uso (pago por token/uso), e pode ser necessário adicionar um método de pagamento à sua conta para utilizar os serviços. Por exemplo, cerca de 1.000 tokens pode ter um custo $ 0.50, dependendo do modelo utilizado. Para saber mais sobre como os tokens são contados e precificados leia aqui.

Um pouco mais sobre segurança

Num post anterior introduzimos a utilização de dotenv para guardar as chaves secretas. Esse será o mecanismo utilizado também para guardar nossa chave da API da OpenAI. Neste upgrade do nosso bot vamos passar a guardar as credenciais da conta de serviço do Google Cloud que aprendemos a criar neste artigo, também no arquivo .env.

A dica consiste em criar uma chave no .env, por exemplo:

CREDENCIAIS_GOOGLE_SHEETS={"type": "service_account", "project_id": "xxxxxxxxxxxxxxxxxxxxxx", "private_key_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","private_key": "-----BEGIN PRIVATE KEY-----"}

O nome da chave pode ser qualquer um, e o valor é o conteúdo do arquivo json que você baixou quando criou a chave. Sendo que o conteúdo deve estar em forma de string json. Para isso ponha o conteúdo em uma única linha.

Agora precisamos modificar o arquivo utils.py que foi criado no post anterior.

utils.py
CREDENCIAIS = os.getenv("CREDENCIAIS_GOOGLE_SHEETS")


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)
    json_credencials = json.loads(CREDENCIAIS)
    creds = Credentials.from_service_account_info(
        json_credencials, scopes=scopes)

    client = gspread.authorize(creds)
    return client

Note que incluimos uma linha que obtém CREDENCIAIS do arquivo .env e, na função conectar_google_sheets(), substituimos a linha que define creds. Antes ela precisava ler o arquivo credenciais.json e agora utilizamos json.loads() para converter a string CREDENCIAIS em json e definimos creds para buscar as informações desse json.

O que vamos criar

Iremos incluir o comando /diagnostic ao código do Bot Telegram criado no artigo anterior, o qual será responsável por receber a planilha de transações, gerar um resumo e com base nesse resumo, a IA irá fazer, por meio de um relatório, um diagnóstico e propor algumas recomendações para economizar, reduzir despesas, etc.

O resumo financeiro

O resumo será gerado por meio da função gerar_resumo_financeiro(), que recebe como parâmetro a planilha de transações convertida em um Dataframe, o que possibilita um tratamento de dados mais facilitado.

# Outros imports aqui
import panda as pd

# Algum código aqui

def gerar_resumo_financeiro(df: pd.DataFrame) -> dict:
    """
    Espera colunas: ['Data','Descrição','Categoria','Tipo','Valor', ...]
    Tipo é 'Receita' ou 'Despesa' (case-insensitive)
    """
    df = df.copy()
    # garantir colunas mínimas
    required = ['Data', 'Descrição', 'Categoria', 'Tipo', 'Valor']
    # tentar mapear colunas sem acento
    # assume que o usuário usou cabeçalhos corretos
    for col in required:
        if col not in df.columns:
            raise ValueError(
                f"A coluna obrigatória '{col}' não foi encontrada na planilha."
                )

    # converter tipos
    df['Valor'] = pd.to_numeric(df['Valor'], errors='coerce').fillna(0.0)
    df['Tipo'] = df['Tipo'].astype(str).str.strip().str.capitalize()
    # garantir datas
    try:
        df['Data'] = pd.to_datetime(df['Data'], dayfirst=True, errors='coerce')
    except Exception:
        df['Data'] = pd.to_datetime(df['Data'], errors='coerce')

    # calcular métricas básicas
    receitas = df.loc[df['Tipo'] == 'Receita', 'Valor'].sum()
    despesas = df.loc[df['Tipo'] == 'Despesa', 'Valor'].sum()
    saldo = receitas - despesas
    taxa_poupanca_pct = (saldo / receitas * 100) if receitas != 0 else 0.0

    # distribuição por categoria (somente despesas)
    despesas_por_cat = (
        df.loc[df['Tipo'] == 'Despesa']
        .groupby('Categoria')['Valor']
        .sum()
        .sort_values(ascending=False)
        .to_dict()
    )

    # dívida detectada: categoria chamada 'Dívidas' ou 'Dividas'
    dividas = 0.0
    for key in ['Dívidas', 'Dividas', 'Divida', 'Dívida']:
        if key in df['Categoria'].unique():
            dividas += df.loc[
                (df['Categoria'] == key) & (df['Tipo'] == 'Despesa'), 'Valor'
                ].sum()

    resumo = {
        "receitas": round(float(receitas), 2),
        "despesas": round(float(despesas), 2),
        "saldo": round(float(saldo), 2),
        "taxa_poupanca_pct": round(float(taxa_poupanca_pct), 2),
        "despesas_por_categoria":
            {str(k): float(v) for k, v in despesas_por_cat.items()},
        "dividas": round(float(dividas), 2),
        "periodo_inicio": str(df['Data'].min()) if
            not df['Data'].isnull().all() else None,
        "periodo_fim": str(df['Data'].max()) if
            not df['Data'].isnull().all() else None,
    }
    return resumo

Antes de criar o resumo, a função checa se a planilha tem as colunas obrigatórias. Não importa a ordem, se estão em maiúsculas ou não ou se possuem acentuação. 

Em seguida é feita a conversão de tipos, tanto os numéricos quanto as strings, e a formatação adequada das datas. Tudo isso para assegurar a criação do resumo, visto que, por exemplo, será necessário totalizar cada categoria, e isso exige que cada uma delas tenha denominação padronizada na planilha inteira.

Finalmente, a função cria e retorna o resumo na forma de um dict.

O prompt

O prompt que será enviado ao modelo é montado na função montar_prompt_para_openai(). A função recebe o resumo criado na função anterior e o envia no prompt. Note que o prompt é bastante específico, detalhando o que se quer da IA. No entanto não é um texto fechado, você sempre pode e deve melhorá-lo.

def montar_prompt_para_openai(resumo: dict) -> str:
    # Template em PT-BR para o modelo receber e gerar diagnóstico
    prompt = f"""
    Você é um especialista em finanças pessoais. Analise o resumo financeiro
    abaixo e gere um relatório de diagnóstico completo, claro e motivador.
    Divida o relatório em: Visão geral, Principais pontos de atenção,
    Oportunidades de economia, Plano de ação (3 a 5 passos) e Recomendação
    de produtos/contas para reserva de emergência.
    Seja prático e dê números concretos (valores em reais e percentuais).

    Resumo financeiro (auto-gerado):
    - Período: {resumo.get('periodo_inicio')} até {resumo.get('periodo_fim')}
    - Receitas totais: R$ {resumo.get('receitas'):.2f}
    - Despesas totais: R$ {resumo.get('despesas'):.2f}
    - Saldo: R$ {resumo.get('saldo'):.2f}
    - Taxa de poupança (% sobre a receita):
        {resumo.get('taxa_poupanca_pct'):.2f}%
    - Dívidas identificadas (valor): R$ {resumo.get('dividas'):.2f}
    - Distribuição das maiores categorias de despesa:
        {resumo.get('despesas_por_categoria')}

    Dê recomendações específicas com valores (ex.: "reduza X na categoria Y,
    isso economiza R$ Z por mês") e proponha metas (ex.: reserva de emergência
    equivalente a N meses de despesas).
    """
    return prompt

A função retorna então esse prompt detalhado, o qual contém informações do resumo.

O comando /diagnostic

O primeiro passo na função diagnostic_command() é conectar ao Google Sheets e ler a planilha alvo no formato de um list de dict. Em seguida, por meio de pd.DataFrame(), converter esse list em um DataFrame.

async def diagnostic_command(
        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()
    df = pd.DataFrame(registros)
    try:
        resumo = gerar_resumo_financeiro(df)
    except Exception as e:
        logger.exception("Erro no resumo financeiro")
        await update.message.reply_text(f"Erro ao processar os dados: {e}")
        return
    prompt = montar_prompt_para_openai(resumo)
    # chamada básica para OpenAI (Chat Completions)
    try:
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "developer",
                 "content":
                     """Você é um assistente especialista
                     em finanças pessoais."""},
                {"role": "user", "content": prompt}
            ],
            max_tokens=800,
            temperature=0.3)
        texto_relatorio = response.choices[0].message.content.strip()
    except Exception as e:
        logger.exception(f"Erro chamando OpenAI {e}")
        await update.message.reply_text(
            f"Erro ao gerar o relatório via OpenAI {e}")
        texto_relatorio = (
            "Relatório automático (fallback):\n\n"
            f"Receitas: R$ {resumo['receitas']}\n"
            f"Despesas: R$ {resumo['despesas']}\n"
            f"Saldo: R$ {resumo['saldo']}\n"
            f"Taxa poupança: {resumo['taxa_poupanca_pct']}%\n"
        )

    # enviar resultados
    # primeiro um resumo curto
    resumo_msg = (
        f"✅ Diagnóstico pronto!\n\n"
        f"Receitas: R$ {resumo['receitas']:.2f}\n"
        f"Despesas: R$ {resumo['despesas']:.2f}\n"
        f"Saldo: R$ {resumo['saldo']:.2f}\n"
        f"Taxa de poupança: {resumo['taxa_poupanca_pct']:.2f}%\n\n"
        "Relatório completo em anexo (PDF) e abaixo em texto."
        )
    await update.message.reply_text(resumo_msg)
    await update.message.reply_text(texto_relatorio)

A seguir é chamada a função gerar_resumo_financeiro() passando o DataFrame como argumento. Esta função retorna o resumo, o qual é passado para a função montar_prompt_para_openai(). Esta função retorna o prompt montado.

Com o prompt disponível, é chamada a função client.chat.completions.create(). Esta função é uma chamada à API da OpenAI, que pode interagir com vários modelos da família GPT, bastando especificá-lo no parâmetro model. Além do parâmetro model, é obrigatório o envio de messages, que é uma lista das mensagens da conversa. Messages deverá ter um item de developer e outro de user, no qual passamos o prompt que foi montado antes. O parâmetro max_tokens é usado para controlar os custos das respostas. Por sua vez, temperature determina a aleatoriedade da resposta. Quanto menor o valor, mais focada é a resposta. Seu valor deve estar em 0 e 2.

Para finalizar, o handler do comando /diagnostic é registrado no bot.

Código completo disponível no GitHub.

Conclusão

Agora temos um bot pessoal no Telegram, que facilita o trabalho de lançar as transações na planilha e se integra a uma Inteligência Artificial para, com base nessa planilha, fazer uma análise desses dados e fornecer recomendações a fim de aprimorarmos a getão das nossas finanças.

Esse código é apenas um ponto de partida. Uma das melhorias que podemos implementar é pedir à nossa IA fazer o lançamento na planilha a partir de uma mensagem não estruturada. Por exemplo, podemos ter o comando /register paguei 150,00 num almoço e 30,00 em cervejas. E a partir disso modelo infere categorias, descrições, valores, etc.

Comentários