Repository navigation
chatbot
Home > Chatbot
Important
Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.
Nível: intermediário
Em uma frase: o módulo de chat do RAL liga a sua aplicação a aplicativos de mensagem (Telegram, Discord, Slack, Teams e gateways de WhatsApp) com um robô só: as mensagens de todos os canais chegam no mesmo formato, os comandos e as respostas são escritos uma vez, e o que um canal não sabe mostrar vira texto.
Você vai precisar: RAL 2.0 com o pacote RALModuleChatPackage (e o
RALModuleChatDsgn para a IDE) e o pacote de cada canal, como o
RALModuleChatTelegramPackage (Telegram). Para receber por webhook,
um servidor RAL acessível pela internet com HTTPS (HTTPS).
| componente | paleta | para |
|---|---|---|
TRALChatBot |
RAL - Modules | o robô: canais, comandos, eventos, sessões |
TRALChatWebhookModule |
RAL - Modules | recebe os webhooks dos canais em qualquer servidor RAL |
TRALChatStore |
RAL - Modules | guarda as sessões das conversas num arquivo que sobrevive a queda de energia |
TRALChatTelegram |
RAL - Modules | o canal do Telegram (Telegram) |
TRALChatGateway |
RAL - Modules | gateways REST: Evolution API, WPPConnect, Z-API ou outro (gateways) |
TRALChatDiscord |
RAL - Modules | o canal do Discord (Discord) |
TRALChatSlack |
RAL - Modules | o canal do Slack (Slack) |
TRALChatTeams |
RAL - Modules | o canal do Microsoft Teams (Teams) |
TRALChatFlow |
RAL - Modules | conversa em etapas montada no Object Inspector (conversa em etapas) |
TRALChatDBStore |
RAL - Modules | as sessões num banco, para várias instâncias (conversa em etapas) |
uses RALChatMessage, RALChatChannel, RALChatBot, RALChatWebhook, RALChatTelegram;
FBot := TRALChatBot.Create(Self);
FBot.OnMessage := ChegouMensagem;
FBot.OnCallback := EscolheuBotao;
FBot.OnError := DeuErro;
FTelegram := TRALChatTelegram.Create(Self);
FTelegram.TokenSource := tsEnvironment; // o token vem da variável de ambiente
FTelegram.TokenName := 'TELEGRAM_TOKEN';
FTelegram.Bot := FBot;
FBot.Channels.Add.Channel := FTelegram;
FWebhook := TRALChatWebhookModule.Create(Self);
FWebhook.Bot := FBot;
FWebhook.Server := FServidor; // qualquer servidor RAL: rota /telegramCada canal ganha uma rota com o seu ChannelName (/telegram, por exemplo). Os
plugins do servidor (lista de endereços, limite de requisições) valem para ela como
para qualquer rota.
procedure TForm1.ChegouMensagem(Sender: TObject; AChannel: TRALChatChannel;
AMessage: TRALChatMessage);
var
vResp: TRALChatMessage;
begin
if AMessage.Kind <> ckText then
Exit;
vResp := TRALChatMessage.Create;
try
vResp.Text := 'Quer ver o seu pedido?';
vResp.AddButton('sim', 'Sim');
vResp.AddButton('nao', 'Não');
FBot.Reply(AMessage, vResp); // o robô copia: a mensagem continua sua
finally
vResp.Free;
end;
end;
procedure TForm1.EscolheuBotao(Sender: TObject; AChannel: TRALChatChannel;
AMessage: TRALChatMessage);
begin
if AMessage.CallbackData = 'sim' then
FBot.Reply(AMessage, 'Pedido 1234: saiu para entrega.');
end;-
Replyresponde na mesma conversa;Send(canal, mensagem)manda para qualquer conversa (ChatId);SendWaitespera e devolve o id da mensagem enviada;Edit,DeleteeSendTyping("digitando...") onde o canal permite. - Tudo que sai entra numa fila por canal, com novas tentativas e os limites de cada
plataforma (
Ratesdo canal: total, por conversa e por par). O resultado chega emOnSentou, depois das tentativas, emOnSendFailed. - A mensagem traz texto, botões (
Buttons), teclado (AddKeyboardRow), listas, mídia (Media: imagem, áudio, vídeo, documento), localização, contato eReplyTo. ComRaw(eKeepRawno canal) você vê o que a plataforma mandou.
Em vez de um if grande no OnMessage, cadastre os comandos em Commands (pelo
editor da IDE ou no código):
vCmd := FBot.Commands.Add;
vCmd.Name := 'inicio';
vCmd.Match := cmmCommand; // /start
vCmd.Pattern := 'start';
vCmd.OnExecute := ComandoInicio;
procedure TForm1.ComandoInicio(Sender: TObject; AChannel: TRALChatChannel;
AMessage: TRALChatMessage; var AHandled: Boolean);
begin
FBot.Reply(AMessage, 'Olá! Mande o número do pedido.');
end;Match diz como o texto é comparado: comando com barra (cmmCommand), a palavra
inteira (cmmKeyword), começo (cmmPrefix), contém (cmmContains), expressão
regular (cmmRegex) ou o id de um botão (cmmCallback). Channels limita o comando a
alguns canais. Mensagem que nenhum comando pega vai para OnMessage (ou OnCallback).
Nem todo canal tem botões, listas ou edição. Supports(canal, ccButtons) pergunta;
com Fallback = cfDegrade (o padrão) o robô transforma os botões numa lista numerada,
e a resposta "2" volta como se a pessoa tivesse clicado no segundo botão. Com cfRaise
o envio falha com ERALChatError.
O robô guarda um estado por conversa (o passo de um cadastro, o carrinho):
FBot.Sessions.SetValue(AMessage.ChannelName, AMessage.ChatId, 'passo', 'cpf');
vPasso := FBot.Sessions.GetValue(AMessage.ChannelName, AMessage.ChatId, 'passo');- A sessão expira depois de
SessionTimeoutsegundos sem mensagem (30 minutos). - Sem nada configurado ela fica na memória. Com
PersistFile(ou umTRALChatStoreemStore) cada mudança é gravada no disco antes de seguir: reiniciar o programa no meio de uma conversa não perde o passo.
- O pedido é conferido pela assinatura ou pelo segredo do canal antes de qualquer outra coisa; o que não confere recebe 401 e não chega ao robô.
- A plataforma recebe 200 na hora e as mensagens vão para uma fila;
Workersthreads rodam os seus eventos, e as mensagens de uma mesma conversa chegam sempre em ordem. Os eventos rodam nessas threads, não na thread principal: para mexer na tela useTThread.Queue. - Uma mensagem reenviada pela plataforma (a mesma
EventIddentro deDedupWindow) não chega duas vezes. - Fila cheia (
MaxQueue) responde 503: a plataforma tenta de novo mais tarde.
O token do canal nunca aparece em log nem em mensagem de erro. Ele pode vir de três
lugares (TokenSource): da propriedade Token (fica no .dfm/.lfm: evite em
repositório), de uma variável de ambiente (tsEnvironment, com o nome em TokenName)
ou da primeira linha de um arquivo (tsFile).
| sintoma | causa | o que fazer |
|---|---|---|
| o webhook responde 401 | segredo ou assinatura diferente do configurado na plataforma | confira o segredo do canal e registre o webhook de novo |
| "access violation" ao mexer num controle da tela no evento | o evento roda numa thread do módulo | use TThread.Queue
|
| mensagem respondida duas vezes | duas instâncias do programa recebendo o mesmo canal | uma instância por canal, ou webhook em uma só |
| botões chegam como lista numerada | o canal não tem botões | é o Fallback; use Supports para escolher outro texto |
Começar
Servidor
- Rotas e parâmetros
- Rotas avançadas: tipos, grupos, versões 🚧
- Ler o pedido e responder
- Objetos no corpo 🚧
- Resposta em fluxo 🚧
- Plugins 🚧
- Serviços (contêiner de dependências) 🚧
- Barramento de eventos 🚧
- Autenticação
- Banco de dados (DBWare)
- Servir site e arquivos
- JSON-RPC 2.0 🚧
- Servidor MCP (IA) 🚧
-
Protocol Buffers 🚧
- gRPC 🚧
- WebSocket 🚧
- Hubs (WebSocket) 🚧
- Swagger e Postman
- Servidor SOAP 🚧
- WS-Security 🚧
- HTTPS
- Criptografia ponto a ponto
- Segurança para produção
- Colocar no ar
- Desempenho
- Testes em memória 🚧
MQTT 🚧
- O que é e quando usar 🚧
- Cliente MQTT 🚧
- Broker MQTT 🚧
- MQTT-SN 🚧
- Ponte REST 🚧
- Segurança 🚧
- Rede ruim 🚧
E-mail 🚧
- Mensagens 🚧
- Enviar 🚧
- Receber 🚧
- Ler a caixa postal 🚧
- Verificar o que chega 🚧
- Servir caixas postais 🚧
- S/MIME 🚧
- OpenPGP 🚧
Chatbot 🚧
- O robô e os canais 🚧
- Telegram 🚧
- Gateways REST (WhatsApp não oficial) 🚧
- Discord 🚧
- Slack 🚧
- Microsoft Teams 🚧
- Conversa em etapas 🚧
Cliente
Motores
- Qual motor escolher?
- Compatibilidade
- Recursos
- QUIC
- Motor NgHttp2 (HTTP/2) 🚧
- Motor XSocket 🚧
- Motor AdvancedHTTP (experimental) 🚧
Avançado
Ajuda
- Problemas comuns
- Atualizar o Indy
- Várias versões do Delphi
- Vídeos e guias
- Módulos de terceiros
- Licenças
- Contribuir
- Roadmap e changelog
🚧 = em desenvolvimento, pode mudar