Skip to content

State

import asyncio

from waha.events import OnMessageEvent
from waha.pipeline import State, StateManager

# Dicionário onde a chave é o ID do chat e os valores são os alimentos adicionados
carrinho_usuarios: dict[str, list[str]] = {}

async def adicionar_carne(chat_id: str):
    """Adiciona carne ao carrinho do usuário e retorna ao menu inicial."""
    carrinho_usuarios[chat_id] = carrinho_usuarios.get(chat_id, [])
    carrinho_usuarios[chat_id].append("carne")
    return InitialState()

async def adicionar_arroz(chat_id: str):
    """Adiciona arroz ao carrinho do usuário e retorna ao menu inicial."""
    carrinho_usuarios[chat_id] = carrinho_usuarios.get(chat_id, [])
    carrinho_usuarios[chat_id].append("arroz")
    return InitialState()

async def finalizar_compra(chat_id: str):
    """Finaliza a compra, limpando o carrinho do usuário e retornando uma mensagem de confirmação."""
    carrinho_usuarios.pop(chat_id, None)  # Remove os itens do carrinho
    return CompraFinalizadaState()

class ComidaState(State):
    """Estado onde o usuário pode escolher alimentos para adicionar ao carrinho."""
    message = """Opções:

1. 🍖 Carne
2. 🌾 Arroz
3. 🗂️ Voltar ao menu
"""

    message_invalid_option = None  # Nenhuma mensagem personalizada para opções inválidas

    handle_options = {
        "1": adicionar_carne,
        "2": adicionar_arroz,
        "3": lambda _: InitialState(),  # Retorna ao menu inicial
    }

class InitialState(State):
    """Estado inicial onde o usuário pode acessar o menu principal."""
    message = """Mercado do seu Zé

1. 🥘 Comidas
2. 🛒 Ver carrinho
"""

    handle_options = {
        "1": ComidaState(),  # Vai para o menu de comidas
        "2": lambda chat_id: VerCarrinhoState(chat_id),  # Vai para a tela de visualização do carrinho
    }

class VerCarrinhoState(State):
    """Estado onde o usuário pode visualizar os itens no carrinho e finalizar a compra."""
    def __init__(self, chat_id: str):
        self.chat_id = chat_id
        carrinho = carrinho_usuarios.get(chat_id, [])
        self.message = (
            "\U0001F6D2 Seu carrinho:\n" + "\n".join(f"- {item}" for item in carrinho)
            if carrinho
            else "\U0001F6D2 Seu carrinho está vazio.\n"
        )
        self.message += "\n1. ✅ Finalizar compra\n0. 🔙 Voltar ao menu"

    handle_options = {
        "1": finalizar_compra,  # Finaliza a compra
        "0": lambda chat_id: InitialState(),  # Retorna ao menu inicial
    }

class CompraFinalizadaState(State):
    """Estado exibido quando o usuário finaliza a compra."""
    message = "✅ Compra finalizada! Obrigado por comprar no Mercado do seu Zé.\n\n0. 🔙 Voltar ao menu"

    handle_options = {
        "0": InitialState(),  # Volta ao menu inicial
    }

# Inicializa o evento para capturar mensagens dos usuários
on_message = OnMessageEvent()

# Adiciona um gerenciador de estados ao evento
on_message.add_listeners(
    StateManager(
        initial_state=InitialState(),
        wait_response=120,  # Tempo de espera de 120 segundos antes de resetar para o estado inicial
    )
)

async def main():
    """Inicia o sistema de mensagens e mantém a aplicação rodando."""
    await on_message.start()
    while True:
        await asyncio.sleep(1)

# Executa o loop principal da aplicação
asyncio.run(main())

Explicação do Funcionamento dos Estados (State Machine)

O código implementa uma máquina de estados para um sistema de mensagens automatizado, permitindo que os usuários naveguem entre diferentes menus e realizem ações. Essa abordagem garante um fluxo estruturado e previsível dentro da aplicação.

Conceito de Máquina de Estados

Uma máquina de estados é um modelo computacional que gerencia o comportamento de um sistema através de estados e transições. Cada estado define um conjunto específico de opções disponíveis para o usuário e as transições ocorrem conforme as escolhas feitas.

Estados Definidos no Código

InitialState

  • Estado inicial onde o usuário pode escolher entre acessar o menu de comidas ou visualizar o carrinho.
  • Opções disponíveis:
  • 1 → Navega para ComidaState
  • 2 → Navega para VerCarrinhoState

ComidaState

  • Estado onde o usuário pode selecionar itens para adicionar ao carrinho.
  • Opções disponíveis:
  • 1 → Adiciona carne ao carrinho e retorna ao estado inicial.
  • 2 → Adiciona arroz ao carrinho e retorna ao estado inicial.
  • 3 → Retorna ao InitialState.

VerCarrinhoState

  • Exibe os itens atualmente no carrinho do usuário.
  • Opções disponíveis:
  • 1 → Finaliza a compra e esvazia o carrinho.
  • 0 → Retorna ao InitialState.

CompraFinalizadaState

  • Estado exibido após a finalização da compra.
  • Opções disponíveis:
  • 0 → Retorna ao InitialState.

Fluxo de Navegação

  1. O usuário inicia no InitialState.
  2. Escolhendo 1, ele vai para ComidaState, onde pode adicionar itens ao carrinho.
  3. Escolhendo 2, ele vai para VerCarrinhoState, onde pode visualizar o carrinho e finalizar a compra.
  4. Se optar por finalizar a compra (1 dentro do carrinho), o estado muda para CompraFinalizadaState, e o carrinho é esvaziado.
  5. Em qualquer momento, o usuário pode retornar ao menu inicial escolhendo a opção correspondente.

Como o Gerenciamento de Estados Funciona

O gerenciamento dos estados é feito através da classe StateManager, que controla as transições e define regras para a navegação do usuário. Além disso, há um tempo limite de resposta para evitar que usuários fiquem presos em um estado por muito tempo.

Principais características do StateManager: - Define o estado inicial (InitialState). - Mantém o estado atual do usuário até que uma opção seja escolhida. - Se o usuário não interagir dentro do tempo limite (120 segundos no código), o estado é resetado para InitialState. - Realiza a transição para o próximo estado com base na escolha do usuário.

Benefícios do Uso de Máquina de Estados

  • Organização: O fluxo é bem definido, tornando o código mais legível e modular.
  • Facilidade de manutenção: Caso seja necessário adicionar novas funcionalidades, basta criar novos estados e definir as transições.
  • Controle: Evita que o usuário faça escolhas inválidas ou quebre o fluxo da aplicação.
  • Escalabilidade: Pode ser expandido para incluir novos menus, opções e funcionalidades sem comprometer a estrutura existente.