Funções
Dentro dos cards você fará uso de funções e expressões para descrever a lógica do seu serviço de impacto.
Os cards permitem várias funções: enviar mensagens de texto, mídia ou botões interativos, atualizar campos de contato, integrar com serviços de terceiros ou realizar vários cálculos.
Mensagens de texto
Enviar uma mensagem de texto
Esta função envia uma mensagem de texto para o usuário.
Sintaxe
text("lorem ipsum")
Exemplo
O exemplo a seguir cria uma jornada que envia uma única mensagem de texto ao usuário. O conteúdo da mensagem diz "Hello this is my first text.".
card TextCard do
text("Hello this is my first text.")
end
Erros comuns
- Não colocar a mensagem entre aspas duplas.
- Escrever text() incorretamente como test().
Fazer uma pergunta
Esta função envia uma mensagem de texto e aguarda a resposta do usuário.
Sintaxe
ask("lorem ipsum?")
Exemplo
O exemplo a seguir cria uma jornada que pergunta ao usuário "What is your name?" e então aguarda pela resposta do usuário. Uma vez que o usuário responde, é enviada uma mensagem de agradecimento.
card QuestionCard do
ask("What is your name?")
text("Thanks for answering")
end
Pontos importantes
- O serviço de chat só executará as etapas seguintes quando uma resposta for recebida ou, caso contrário, quando a jornada expirar.
Erros comuns
- Não colocar a pergunta entre aspas duplas.
Mensagens interativas
O WhatsApp suporta dois tipos de respostas rápidas: botões e listas.
Os botões estão limitados a 3 botões. Cada botão tem um limite de 20 caracteres.
As listas estão limitadas a 10 opções. Cada opção tem um limite de 24 caracteres.
O texto principal, o cabeçalho e o rodapé de uma mensagem interativa podem ser colocados dentro dos blocos buttons
ou list
para que possam ser compostos como uma única mensagem.
Enviar uma mensagem com botões
Esta função envia uma mensagem de texto com botões ao usuário. Botões facilitam para o usuário responder. Você pode adicionar até 3 botões por mensagem.
Sintaxe
buttons([Button1, Button2, Button3]) do
text("Select an option!")
end
Você também pode usar rótulos para os botões assim:
buttons([Button1: "1️⃣", Button2: "2️⃣", Button3: "3️⃣"]) do
text("Select an option!")
end
Exemplo
O exemplo a seguir cria uma jornada que pergunta ao usuário "What would you like to drink?" e fornece duas opções para selecionar: Tea ou Coffee.
Se o usuário selecionar Tea
, ele recebe uma mensagem dizendo "Thanks for choosing tea". Se o usuário selecionar Coffee
, ele recebe a resposta "Thanks for choosing coffee".
card ButtonsCard do
buttons([Tea, Coffee]) do
text("What would you like to drink?")
header("This is the header!")
footer("This is the footer!")
end
end
card Tea do
text("Thanks for choosing tea")
end
card Coffee do
text("Thanks for choosing coffee")
end
Você pode fornecer rótulos para os botões (por exemplo, Tea 🫖
), mantendo um nome de card simples (por exemplo, Tea
).
card ButtonsCard do
buttons([Tea: "Tea 🫖", Coffee: "Coffee ☕️"]) do
text("What would you like to drink?")
header("This is the header!")
footer("This is the footer!")
end
end
Às vezes, você quer fornecer vários botões com rótulos diferentes, mas que levam ao mesmo card de destino. É possível fazer isso usando o mesmo nome de card de destino, com rótulos diferentes.
card Card do
var =
buttons(Destination: "🧁", Destination: "🎂", Destination: "🍰") do
text("Choose your favourite cake!")
end
end
card Destination do
text("You chose @var using @var.label ")
end
Isso apresentará 3 botões, cada um com um rótulo diferente, mas a resposta é tratada pelo mesmo card Destination
. Lembre-se que, mesmo que o card Destination
tenha um rótulo de exibição atribuído, qualquer rótulo definido no botão terá prioridade sobre o rótulo do card.
Pontos importantes
- Você precisa usar as funções
buttons()
etext()
. - A função
buttons()
descreve as opções disponíveis. Botões podem ter até 3 opções. Cada opção de botão tem um limite de 20 caracteres. - A função
text()
descreve a instrução para o usuário. - O cabeçalho e o rodapé são opcionais.
- Se você quiser agir com base na seleção do usuário, precisa adicionar novos cards à jornada para definir a lógica de cada opção selecionada.
Erros comuns
- Esquecer de adicionar a função
text()
dentro da funçãobuttons()
. - Esquecer de adicionar cards para cada opção de botão.
Enviar uma mensagem com lista
Esta função envia uma lista de itens para o usuário selecionar. Você pode adicionar até 10 opções em uma lista.
Sintaxe
list("lorem ipsum", [Option1, Option2, …, Option10]) do
text("lorem ipsum")
end
Exemplo
O exemplo a seguir cria uma jornada que envia ao usuário uma mensagem de lista e permite que ele escolha entre quatro opções.
O usuário é instruído a "Pick an option!". Quando clica no texto "Call to action", a lista se expande e ele vê 4 opções: Option1, Option2, Option3, Option4. Depois que o usuário faz uma seleção, recebe uma mensagem confirmando sua escolha.
card List do
list("Call to action", [Option1, Option2, Option3, Option4]) do
text("Pick an option!")
header("This is the header!")
footer("This is the footer!")
end
end
card Option1 do
text("You selected option 1")
end
card Option2 do
text("You selected option 2")
end
card Option3 do
text("You selected option 3")
end
card Option4 do
text("You selected option 4")
end
Pontos importantes
- Você precisa usar as funções
list()
etext()
. - A função
list()
descreve o texto do botão de seleção, bem como as opções disponíveis. Listas podem ter até 10 opções. Cada opção de lista tem um limite de 24 caracteres. - A função
text()
descreve a instrução para o usuário. - O cabeçalho e o rodapé são opcionais.
- Se você quiser agir com base na seleção do usuário, precisa adicionar novos cards à jornada para definir a lógica de cada opção selecionada.
Erros comuns
- Esquecer de adicionar texto para o botão de seleção.
- Esquecer de adicionar a função text() após a list() function.
- Esquecer uma vírgula entre o texto do botão de seleção e as opções.
- Esquecer de adicionar cards para cada opção de lista.
Rótulos para botões e itens de lista
Muitas vezes, em um serviço de impacto, você vai reutilizar coisas, como seu menu. Quando precisar fazer atualizações no texto, idealmente você deseja atualizá-lo uma vez e ter essa alteração aplicada em todos os lugares em que é usada. Isso é possível para botões e opções de lista.
Sintaxe
Ao definir cards, você pode adicionar um rótulo opcional para o card. Esse rótulo será usado para botões e itens de lista dos quais o card faz parte.
card ButtonCard, "lorem ipsum" do
buttons([Button1, Button3, Button3]) do
text("lorem ipsum")
end
end
Exemplo
Vamos criar uma seleção de menu básico com 3 opções: About
, Contact
e Terms of service (ToS)
. Quando o usuário clica em qualquer um dos botões, ele recebe uma mensagem curta com um único botão que o leva de volta ao Start
.
card Start do
buttons([About, Contact, ToS]) do
text("This our menu. Select an option.")
end
end
card About do
buttons([Start]) do
text("This is about us, we are truly wonderful!")
end
end
card Contact do
buttons([Start]) do
text("You can contact us via pigeon post, glhf")
end
end
card ToS do
buttons([Start]) do
text("We provide zero guarantees")
end
end
Agora, digamos que queremos alterar o texto do botão Start
. Nós o usamos três vezes, mas só queremos atualizá-lo uma vez. Podemos fazer isso adicionando o texto desejado como uma string nesse card.
Ao adicionar o rótulo "Menu" à definição do card Start
, esse rótulo agora será usado em qualquer botão ou item de lista onde o card Start
for referenciado.
card Start, "Menu" do
buttons([About, Contact, ToS])
text("This our menu. Select an option.")
end
card About do
buttons([Start]) do
text("This is about us, we are truly wonderful!")
end
end
card Contact do
buttons([Start]) do
text("You can contact us via pigeon post, glhf")
end
end
card ToS do
buttons([Start]) do
text("We provide zero guarantees")
end
end
A melhor parte da string é que você pode usar campos dinâmicos, o que é ótimo para personalização. Vamos atualizar o botão Start
para incluir o nome do perfil do usuário no WhatsApp.
card Start, "Head home @contact.whatsapp_profile_name" do
buttons([About, Contact, ToS])
text("This our menu. Select an option.")
end
card About do
buttons([Start]) do
text("This is about us, we are truly wonderful!")
end
end
card Contact do
buttons([Start]) do
text("You can contact us via pigeon post, glhf")
end
end
card ToS do
buttons([Start]) do
text("We provide zero guarantees")
end
end
Mensagens de mídia
Usar mídia melhora a experiência do usuário e pode superar lacunas de alfabetização. O WhatsApp permite cinco tipos de mídia: imagem, vídeo, áudio, documento e figurinha (sticker).
A mídia que você pretende enviar deve, idealmente, ser hospedada pela própria Turn.io; no entanto, uma URL publicamente acessível também é uma entrada válida para essas funções.
Veja como fazer upload de mídia na Turn.io para uso em jornadas baseadas em código.
Uma maneira simples de hospedar mídia em uma URL publicamente acessível é carregar seu arquivo no Google Drive e depois obter um link de download usando este site. Certifique-se de que as permissões de compartilhamento estejam definidas para Qualquer pessoa com o link pode visualizar.
Recomenda-se fazer upload da mídia na Turn.io, pois uploads de mídia em plataformas de terceiros, incluindo, mas não se limitando a Google Drive e AWS, muitas vezes não garantem que a solicitação seja atendida dentro de um tempo adequado. Isso pode levar a um tempo limite da solicitação e, por fim, o usuário não receber uma resposta.
Enviar uma imagem
Esta função envia uma imagem para o usuário.
Sintaxe
image("<URL da sua imagem>")
Exemplo
O exemplo a seguir cria uma jornada que envia ao usuário uma imagem com uma legenda dizendo "hello there!".
card MyCard do
image("https://upload.wikimedia.org/wikipedia/commons/thumb/d/db/Golden-crowned_kinglet_at_JBWR_%2811835%29.jpg/1000px-Golden-crowned_kinglet_at_JBWR_%2811835%29.jpg")
text("hello there!")
end
Pontos importantes
- Você precisa usar as funções
image()
etext()
. - A função
image()
só contém a URL. Imagens podem ter até 5MB. - A função
text()
envia uma legenda em texto. - O WhatsApp suporta apenas tipos de arquivos específicos.
Erros comuns
- Esquecer de adicionar a função text() após a função image().
Enviar um vídeo
Esta função envia um vídeo para o usuário.
Sintaxe
video("<URL do seu vídeo>")
Exemplo
O exemplo a seguir cria uma jornada que envia ao usuário um vídeo (de gato) com uma legenda dizendo "hello there!".
card MyCard do
video("https://drive.google.com/uc?export=download&id=1LCTe5S6DT1mNnYQhaoeYsk3jqSAEglVE")
text("hello there!")
end
Pontos importantes
- Você precisa usar as funções
video()
etext()
. - A função
video()
só contém a URL. Vídeos podem ter até 16MB. - A função
text()
envia uma legenda em texto. - O WhatsApp suporta apenas tipos de arquivos específicos.
Enviar um áudio
Esta função envia um áudio para o usuário.
Sintaxe
audio("<URL do seu áudio>")
Exemplo
O exemplo a seguir cria uma jornada que envia ao usuário um arquivo de áudio e uma mensagem de texto dizendo "hello there!".
card MyCard do
audio("https://drive.google.com/uc?export=download&id=1hpkdDN9noqIqvwDX0EhS2z7K2W_-khXS")
text("hello there!")
end
Pontos importantes
- Você precisa usar as funções
audio()
etext()
. - A função
audio()
só contém a URL. Arquivos de áudio podem ter até 16MB. - A função
text()
envia uma mensagem de texto separada. - O WhatsApp suporta apenas tipos de arquivos específicos.
Enviar um documento
Esta função envia um documento para o usuário.
Sintaxe
document("<URL do seu documento>", filename: "<Título do seu documento>")
Exemplo
O exemplo a seguir cria uma jornada que envia ao usuário um documento chamado "Example" com uma legenda dizendo "hello there!".
card MyCard do
document("https://drive.google.com/uc?export=download&id=17-O2zjqaI-eQdqVJ-febYBxr0E9YT_HU", filename: "Example")
text("hello there!")
end
Pontos importantes
- Você precisa usar as funções
document()
etext()
. - A função
document()
contém a URL e um parâmetro opcionalfilename
. Documentos podem ter até 100MB. - A função
text()
descreve a legenda da mensagem de mídia. - O WhatsApp suporta apenas tipos de arquivos específicos.
Mensagens de modelo (template)
Você pode enviar modelos (templates) de mensagens a partir das jornadas, desde que o modelo (template) que deseja enviar já exista e tenha sido aprovado.
Um modelo (template) de mensagem do WhatsApp é identificado pelo nome e idioma, então você precisará de ambos para enviar um modelo (template) de uma jornada.
Sintaxe
send_message_template(
"template-name",
"template-language",
["body-placeholder1", "body-placeholder2", ...],
header: ["header-placeholder"],
video: "https://link.to/video.mp4",
image: "https://link.to/image.mp4",
document: "https://link.to/document.mp4",
buttons: [Card1, Card2, ...]
)
Os parâmetros header
, video
, image
, document
e buttons
são opcionais.
Modelo (Template) com placeholders no corpo
Se o modelo (template) contiver placeholders no corpo, você pode fornecer valores para esses placeholders em uma lista, após o nome e o idioma do modelo (template).
Caso o modelo (template) não tenha placeholders no corpo, você pode passar uma lista vazia []
.
Exemplo
Vamos supor que você tenha criado o seguinte modelo (template):
- nome:
simple-reminder
- idioma:
en
- corpo:
Hi {{1}}, this a reminder to {{2}}
Veja como você poderia enviar esse modelo (template):
card SendTemplate do
send_message_template(
"simple-reminder",
"en",
["Mark", "exercise"]
)
end
Modelo (Template) com placeholders no cabeçalho
Se o modelo (template) contiver placeholders no cabeçalho, você pode fornecer parâmetros para eles, passando uma lista de valores para o argumento opcional header
.
Exemplo
Vamos supor que você tenha criado o seguinte modelo (template):
- nome:
simple-reminder
- idioma:
en
- cabeçalho:
{{1}} reminder
- corpo:
Hi {{1}}, this a reminder to {{2}}
Veja como você poderia enviar esse modelo (template):
card SendTemplate do
send_message_template(
"simple-reminder",
"en",
["Mark", "exercise"],
header: ["Training"]
)
end
Modelo (Template) com header de mídia
Se o modelo (template) tiver um cabeçalho de mídia, você pode usar os argumentos opcionais video
, image
, document
para fornecer o link da mídia desejada.
Exemplo
Suponha que você tenha criado o seguinte modelo (template):
- nome:
simple-reminder
- idioma:
en
- tipo de mídia do cabeçalho:
VIDEO
- corpo:
Hi {{1}}, this a reminder to {{2}}
Veja como enviar esse modelo (template):
card SendTemplate do
send_message_template(
"simple-reminder",
"en",
["Mark", "exercise"],
video: "https://link.to/the/video.mp4"
)
end
Suponha que você tenha criado o seguinte modelo (template):
- nome:
simple-reminder
- idioma:
en
- tipo de mídia do cabeçalho:
IMAGE
- corpo:
Hi {{1}}, this a reminder to {{2}}
Veja como enviar esse modelo (template):
card SendTemplate do
send_message_template(
"simple-reminder",
"en",
["Mark", "exercise"],
image: "https://link.to/the/image.jpg"
)
end
Suponha que você tenha criado o seguinte modelo (template):
- nome:
simple-reminder
- idioma:
en
- cabeçalho com tipo de mídia:
DOCUMENT
- corpo:
Hi {{1}}, this a reminder to {{2}}
Veja como enviar esse modelo (template):
card SendTemplate do
send_message_template(
"simple-reminder",
"en",
["Mark", "exercise"],
document: "https://link.to/the/document.pdf",
filename: "Document title"
)
end
Modelo (Template) com botões
Se o modelo (template) contiver botões, você pode usar o argumento opcional buttons
para direcionar o fluxo da jornada para diferentes cards, dependendo do botão que o usuário clicar.
Exemplo
Vamos supor que você tenha criado o seguinte modelo (template):
- nome:
updates
- idioma:
en
- corpo:
Hi {{1}}, would you like to receive updates?
- botões:
Yes, I would love to!
,No, I would not.
Veja como você poderia enviar esse modelo (template) e direcionar o fluxo da jornada dependendo do botão que o usuário clicar:
card SendTemplate do
send_message_template(
"updates",
"en",
["Mark"],
buttons: [Yes, No]
)
end
card Yes do
text("Great! You will receive updates from now on.")
end
card No do
text("Ok, you will not receive updates.")
end
É importante observar que o texto dos botões exibido no simulador provavelmente será diferente do exibido no WhatsApp. Isso ocorre porque o simulador usa os nomes dos cards que você definiu como opções para simular o texto exibido nos botões, enquanto a mensagem real usa o texto que você forneceu no modelo (template).
Pontos importantes
- O modelo (template) que você deseja enviar deve existir e ter sido aprovado.
Recebendo coordenadas de GPS
Você pode extrair coordenadas de GPS, como latitude e longitude, do local compartilhado do usuário no aplicativo móvel WhatsApp.
Sintaxe
latitude = event.message.location.latitude longitude = event.message.location.longitude
Exemplo
O exemplo a seguir cria uma jornada que pede ao usuário para compartilhar um local de GPS e extrai as coordenadas de GPS.
card AskForGpsPin, then: ParseLocation do
ask("Please send your GPS Pin using WhatsApp")
end
card ParseLocation when event.message.type == "location" do
latitude = event.message.location.latitude
longitude = event.message.location.longitude
text("You have shared the following GPS coordinates: [@latitude, @longitude]")
end
card ParseLocation do
text("You have not shared a valid GPS pin")
end
Pontos importantes
- Você precisa garantir que o tipo de mensagem sendo compartilhada seja "location" antes de tentar extrair latitude e longitude.
- Ter uma condição padrão também é necessário para garantir que, caso o usuário não envie um local de GPS, o fluxo não seja interrompido.
Fluxos do WhatsApp
Os WhatsApp Flows são formulários baseados em telefone que são ótimos para coleta de dados. Você pode integrá-los aos seus fluxos e usar os dados coletados para continuar em seu canvas ou jornada.
Sintaxe
flow = whatsapp_flow("the call to action", "the flow-id", "screen") do
text("the body of the message")
end
As funções header()
e footer()
são opcionais no bloco whatsapp_flow()
.
Se fornecidas, podem conter o texto que você deseja no cabeçalho e rodapé da mensagem do WhatsApp Flow.
Aqui está um exemplo de como usar um WhatsApp Flow em um card e depois usar os dados coletados.
card Flow do
flow =
whatsapp_flow("click!", "123456789", "SIGN_UP") do
header("this is the header")
text("this is the body")
footer("this is the footer")
end
then(ThankYou when has_text(flow.firstname))
then(Other)
end
card ThankYou do
text("Thank you! @flow.firstname @flow.lastname")
end
card Other do
text("You did not submit the flow.")
end
Os WhatsApp Flows são melhor projetados e testados atualmente em sua conta do Facebook Business Manager. A seção Getting Started da Meta para WhatsApp Flows é um ótimo ponto de partida.
O ID do seu flow e as telas disponíveis são fornecidos pelo Facebook Business Manager
A Turn.io só garante o funcionamento dos WhatsApp Flows para os dispositivos a seguir, conforme as políticas da Meta:
- Android com OS 6.0 ou superior
- iPhone com iOS 12 ou superior
Personalização
A Jornada permite criar serviços de impacto altamente personalizados. Você pode armazenar respostas de usuários momentaneamente em variáveis enquanto a conversa acontece, ou mais permanentemente no perfil do contato.
Salvar uma resposta e usá-la na conversa
Qualquer resposta do usuário pode ser armazenada momentaneamente em variáveis e usada em interações futuras. Isso proporciona uma experiência de usuário mais personalizada.
Sintaxe
Salva um resultado em uma variável
variable_name = some_function_call()
Usa uma expressão para colocar o valor da variável em uma string
text("The value is: @variable_name")
Exemplo
Vamos revisitar o exemplo de botões. Desta vez, armazenaremos as opções selecionadas e as referenciaremos em respostas futuras. Você notará que nos referimos ao valor armazenado em strings usando o símbolo @
. Isso faz uso de expressões para incluir o botão selecionado na resposta.
card Card do
button_picked = buttons([Tea, Coffee]) do
text("What do you like to have?")
end
end
card Tea, "Tea 🫖" do
text("You selected @button_picked")
end
card Coffee, "Coffee ☕️" do
text("You selected @button_picked")
end
Atualizar um campo de perfil de contato
Esta função atualiza um campo de perfil de contato com um valor definido. Armazenar um valor no perfil do contato significa que ele estará disponível quando a interação da jornada terminar e poderá ser recuperado em interações futuras. Atualizar vários campos de perfil pode ser feito passando uma lista de campos de perfil com seus valores separados por vírgulas.
O valor pode ser qualquer dado capturado enquanto o usuário interage com a Jornada. Todos os campos padrão e personalizados podem ser atualizados com essa função.
Sintaxe
Atualizando um campo de perfil
update_contact(profileFieldName: "value")
Atualizando vários campos de perfil
update_contact(profileFieldName1: "value1", profileFieldName2: "value2", profileFieldName3: "value3")
Exemplo
O exemplo a seguir cria uma jornada que pergunta ao usuário qual é seu nome, salva a resposta em uma variável chamada name
e atualiza o campo de perfil name
do contato com esse valor.
card One, then: Two do
name = ask("What is your name?")
end
card Two do
update_contact(name: "@name")
text("Your name has been updated to @contact.name")
end
Pontos importantes
- O campo de perfil do contato precisa existir previamente.
Executar outra jornada
Uma jornada pode iniciar a execução de outra jornada. Isso pode ser útil para dividir grandes serviços em várias jornadas ou extrair lógica comum em uma jornada separada.
Sintaxe
run_stack("<uuid-da-jornada-a-executar>")
Obtendo o UUID de uma jornada
Para executar uma jornada, você precisa do UUID dela. Você pode obter o UUID da jornada clicando no botão "Copy ID" ao lado dela na lista de jornadas:
Exemplo
Este exemplo mostra como chamar a jornada B a partir da jornada A. A função run_stack()
exige o UUID da jornada que você deseja executar.
Este seria o código da Jornada A:
card MyCardA do
text("Oi da Jornada A")
run_stack("d58b0319-eb3f-4884-b43b-4ef0b7c37e1d")
text("Oi de novo da Jornada A")
end
Este é o código da Jornada B. Suponhamos que seu UUID seja d58b0319-eb3f-4884-b43b-4ef0b7c37e1d
:
card MyCardB do
text("Oi da Jornada B")
end
Pontos importantes
- O fluxo retorna para a jornada que chamou assim que a jornada filha termina.
- Atualmente não há como passar parâmetros ou esperar um valor de retorno de uma jornada filha, entretanto você pode atualizar campos do perfil de contato, que estão disponíveis para ambas as jornadas (pai e filha).
Agendamento
Uma jornada pode agendar a execução de outra jornada (ou dela mesma) em um momento no futuro. Isso pode ser útil para programar mensagens que serão enviadas ao usuário em algum momento a partir de agora (por exemplo, 2 dias depois).
Sintaxe
A jornada pode ser agendada usando o argumento in
ou o argumento at
.
O argumento in
exige um tempo relativo expresso em segundos. A jornada agendada será executada após o número de segundos especificados.
schedule_stack("<uuid-da-jornada-a-agendar>", in: seconds)
O argumento at
exige uma data exata, em formato datetime. Essa data pode ser expressa usando uma de nossas expressões. A jornada agendada será executada na data e hora definidas.
Se você quiser agendar uma jornada exatamente dois dias a partir de agora:
schedule_stack("<uuid-da-jornada-a-agendar>", at: datetime_add(now(), 2, "D"))
Ou se você quiser agendar uma jornada para ser executada às 9h na próxima segunda-feira após uma certa data, poderia fazer:
base_date = date(2023, 2, 08))
schedule_stack("<uuid-da-jornada-a-agendar>", at: datetime_next("monday", "09:00", base_date))
Uma jornada que foi previamente agendada também pode ser cancelada usando a função cancel_scheduled_stacks
:
card TextCard, then: MaybeCancel do
schedule_stack("<uuid-da-jornada-a-agendar>", at: datetime_next("monday", "09:00", base_date))
schedule_stack("<uuid-da-jornada-a-agendar>", at: datetime_next("tuesday", "09:00", base_date))
keep_going = ask("Do you want to keep receiving these messages? Type STOP to cancel them")
end
card MaybeCancel when keep_going == "STOP" do
cancel_scheduled_stacks("<uuid-da-jornada-a-cancelar>")
end
card MaybeCancel do
schedule_stack("40ac549a-a883-4c26-b3a2-6eaf8f799edb", at: datetime_next("friday", "10:00"))
end
Observe que essa função cancela todas as jornadas agendadas com o UUID fornecido para aquele destinatário. Então, no exemplo acima, ambas as jornadas agendadas seriam canceladas.
Obtendo o UUID de uma jornada
Para agendar uma jornada, você precisa do UUID dela. Você pode obter o UUID da jornada clicando no botão "Copy ID" ao lado dela na lista de jornadas:
Um exemplo básico de reminder
Este exemplo mostra como agendar um reminder individual que será enviado ao usuário em 2 horas. Você precisa criar duas jornadas. A função schedule_stack()
exige o UUID da jornada que você deseja agendar.
Esta jornada agenda uma segunda jornada para ser executada 2 horas a partir de agora. Ela se refere à outra jornada usando seu UUID:
card ScheduleReminder do
text("We'll send you a reminder message in 2 hours")
schedule_stack("d58b0319-eb3f-4884-b43b-4ef0b7c37e1d", in: 2 * 60 * 60)
end
Esta é a jornada que enviará a mensagem de reminder. Suponha que seu UUID seja d58b0319-eb3f-4884-b43b-4ef0b7c37e1d
:
card SendReminder do
text("Here is your reminder")
end
Um caso de uso mais completo
Neste exemplo, permitimos que o usuário decida quando deseja receber uma mensagem de reminder. Mais uma vez, duas jornadas são necessárias.
Esta é a primeira jornada com a qual o usuário interage. Ela agenda a execução da segunda jornada referenciando seu UUID:
card FirstCard, then: NoChoice do
buttons([OneHour, TwoDays, NextSaturday])
text("When would you like to be reminded?")
end
card NoChoice do
text("Please click on one of the buttons.")
end
card OneHour, "In 1 hour" do
text("Ok, we'll remind you in 1 hour.")
schedule_stack("5ac7f564-a158-4519-9e41-faac610d59ec", in: 60 * 60)
end
card TwoDays, "In 2 days" do
text("Ok, we'll remind you in 2 days.")
schedule_stack("5ac7f564-a158-4519-9e41-faac610d59ec", in: 2 * 24 * 60 * 60)
end
card NextSaturday, "Next Saturday at noon" do
text("Ok, we'll remind you next Saturday at noon.")
schedule_stack("5ac7f564-a158-4519-9e41-faac610d59ec", at: datetime_next("saturday", "12:00"))
end
Esta é a jornada que enviará a mensagem de reminder. Suponhamos que seu UUID seja 5ac7f564-a158-4519-9e41-faac610d59ec
:
card SendReminder do
# Envia um modelo (template) para viabilizar reminders superiores a 24 horas
send_message_template("reminder_template", "pt_BR", ["O tempo acabou!"])
end
Pontos importantes
- O primeiro argumento precisa ser um UUID válido de uma jornada existente no mesmo número.
- Se uma jornada for agendada para ser executada após mais de 24 horas, ela deve começar enviando uma mensagem modelo (template). O WhatsApp exige que você inicie a conversa com um modelo (template) se a última mensagem do usuário tiver mais de 24 horas.
Quando uma jornada agendada é executada, ela interrompe qualquer outra jornada que esteja em execução naquele momento para o usuário.
Funções de IA Assistant
Nós fornecemos um conjunto abrangente de funções de IA Assistant. Essas funções dão suporte aos blocos de IA no Canvas de Jornadas, mas também podem ser usadas diretamente a partir de blocos de código.
Conectando
Você pode usar o AI Assistant interno para se conectar a fornecedores de IA como um Assistant ou pode se conectar diretamente usando um token. Os detalhes da conexão são armazenados na variável retornada e devem ser passados como argumento para quaisquer outras funções relacionadas à IA.
Escolha openai
e meta_ai
como fornecedores de IA.
Fornecedor | Chave do fornecedor |
---|---|
OpenAI | openai |
Meta Llama | meta_ai |
card Start do
# conectar usando um assistant configurado na sua conta
ai = ai_assistant("the-assistant-id")
# ou conectar diretamente com um fornecedor ('openai' ou 'meta_ai'), um identificador de modelo e um token
ai = ai_connect("openai", "gpt-4o", "the-token")
end
Encontrando o ID do assistant
Para encontrar o ID do assistant, vá para a página de detalhes do assistant e clique no botão 'Copy ID'.
Adicionando contexto
Adicione uma única mensagem, com uma função de papel (role), ao contexto usando ai_add_message(). Esse contexto será incluído nas chat completions.
ai = ai_add_message(ai, "user", "the message")
Adicione uma imagem para quando você quiser fazer uma pergunta sobre uma imagem enviada pelo usuário e limite o tamanho da resposta para 150 caracteres.
Imagens são suportadas apenas para um assistant com fornecedor "openai". Consulte OpenAI Adicionando Imagens abaixo.
Chat Completion
Chat Completion é a função para usar IA e gerar texto com base no contexto fornecido
completion = ai_chat_completion(ai)
Isso retorna um objeto complexo com um valor padrão:
%{
"error" => false,
"status" => 200,
"response" => %{
"sentences" => ["the generated.", "response."],
"__value__" => "the generated. response.",
"combined" => "the generated. response."
}
Classificação de texto
Text Classification é a função para usar IA para escolher uma das opções fornecidas.
ai_text_classification(ai, "user-input", "system-prompt", ["option 1", "option 2", "option 3"])
Isso retorna um objeto complexo, onde response é um objeto com um valor de classificação. O valor é o índice baseado em zero da opção. Se nenhuma opção corresponder, classification será -1.
%{
"error" => false,
"status" => 200,
"response" => %{
"sentences" => ["{\"classification\": 1} "],
"__value__" => 1,
"functions" => [],
"combined" => [
%{"sentence" => "{\"classification\": 1} ", "type" => "sentence"}
]
}
}
Transcrição de fala (Transcribing Speech)
A transcrição é suportada somente para um assistant com fornecedor "openai". Consulte OpenAI Transcrição de Fala abaixo.
Criação de fala (Creating Speech)
A criação de fala só é suportada para um assistant com fornecedor "openai". Consulte OpenAI Criação de Fala abaixo.
Funções OpenAI
Nós fornecemos um conjunto abrangente de funções OpenAI. Essas funções suportam os blocos de IA no Canvas de Jornadas, mas também podem ser usadas diretamente a partir de blocos de código.
OpenAI: Conectando
Você pode usar o AI Assistant interno para se conectar ao OpenAI como um Assistant ou se conectar diretamente usando um token. Os detalhes de conexão são armazenados na variável retornada e devem ser passados como argumento para quaisquer outras funções relacionadas ao OpenAI.
card Start do
# conectar usando um assistant configurado na sua conta
ai = openai_assistant()
# ou conectar diretamente com um token e identificador de modelo
ai = openai_connect("the-token", "gpt-4o")
end
OpenAI: Adicionando contexto
Adicione uma única mensagem, com um papel (role) associado, ao contexto usando openai_add_message(). Esse contexto será incluído nas chat completions.
ai = openai_add_message(ai, "user", "the message")
OpenAI: Adicionando imagens
Adicione uma imagem para quando você quiser fazer uma pergunta sobre uma imagem enviada pelo usuário e limite o tamanho da resposta para 150 caracteres.
image_url = attachment_url(event.message.image.id)
ai = openai_add_image(ai, "user", "the message", image_url, 150)
OpenAI: Chat Completion
Chat Completion é a função para usar o OpenAI para gerar texto com base no contexto fornecido
completion = openai_chat_completion(ai)
Isso retorna um objeto complexo com um valor padrão:
%{
"error" => false,
"status" => 200,
"response" => %{
"sentences" => ["the generated.", "response."],
"__value__" => "the generated. response.",
"combined" => "the generated. response."
}
OpenAI: Transcrição de fala (Transcribing Speech)
transcription = openai_transcribe(ai, "https://example.org/file.mp3", "context", "en")
Isso retorna o texto transcrito do arquivo de áudio fornecido. Para transcrever notas de voz recebidas, use a função attachment_url() para criar uma URL temporária e forneça esse valor ao parâmetro URL desta função.
OpenAI: Criação de fala (Creating Speech)
Converte texto em áudio usando a voz e o modelo fornecidos. Consulte a documentação do OpenAI para valores válidos desses parâmetros.
attachment = openai_create_speech(ai, "text to speak", "voice", "model")
Isso retorna um attachment que pode ser usado com audio("@attachment.external_id") para enviar ao WhatsApp de um usuário final.
Funções Lelapa.ai
Nós fornecemos um conjunto abrangente de funções Lelapa.ai. Essas funções podem ser usadas diretamente em blocos de código.
Para usar essas funções, você precisa ter uma conta em https://lelapa.ai/ e gerar um token lá.
Conectando
Você pode se conectar diretamente com um token da API Lelapa. Os detalhes da conexão são armazenados na variável retornada e devem ser passados como argumento para quaisquer outras funções relacionadas ao OpenAI.
card Start do
conn = lelapa_connect("the-token")
end
Classificação de Intenção (Intent Classification)
Isso se conecta à API de Classificação de Intenção da Lelapa.
Permite aos usuários classificar entradas em intenções especificadas com pouca necessidade de treinamento em vários idiomas.
result =
lelapa_intent_classification(
conn,
"Hey, how are you?",
[
["greeting", "Hello!"],
["greeting", "Hi there!"],
# how are you?
["greeting", "Habari yako?"],
["goodbye", "Goodbye!"],
["goodbye", "See you later!"],
# bye
["goodbye", "Kwaheri"],
# see you later
["goodbye", "Tutaonana baadaye"]
]
)
Isso retorna a intenção mais correspondente e a pontuação:
%{
"__value__" => "greeting",
"score" => 0.74386334,
"intent" => "greeting"
}
Reconhecimento de Entidades (Entity Recognition)
Isso expõe a API de Reconhecimento de Entidades.
result =
lelapa_entity_recognition(
conn,
"President Ramaphosa gaan loop by Emfuleni Municipality."
)
e retorna a lista de entidades extraídas:
[
%{
"entity" => "person",
"word" => "Ramaphosa",
"start" => 10,
"end" => 19
},
%{
"entity" => "location",
"word" => "Emfuleni Municipality",
"start" => 33,
"end" => 54
}
]
Análise de Sentimento (Sentiment Analysis)
Isso expõe a API de Análise de Sentimento.
result =
lelapa_sentiment_analysis(
conn,
"am happy. i am sad. hello!"
)
E retorna o seguinte valor:
%{
"__value__" => "positive",
"score" => 0.9991829991340637,
"text" => "am happy",
"label" => "positive",
"sentiments" => [
%{
"sentiment" => [%{"label" => "positive", "score" => 0.9991829991340637}],
"text" => "am happy"
},
%{
"sentiment" => [%{"label" => "negative", "score" => 0.960746169090271}],
"text" => " i am sad"
},
%{
"sentiment" => [%{"label" => "neutral", "score" => 0.8527688384056091}],
"text" => " hello"
}
]
}
O sentimento com maior pontuação é o valor padrão.
Traduzir (Translate)
Expõe a API de Tradução.
result =
lelapa_translate(
conn,
"Lo musho ubhalwe ngesiZulu.",
"zul_Latn",
"eng_Latn"
)
Que retorna a tradução do texto fornecido:
%{
"__value__" => "This sentence is written in Zulu.",
"translation" => [%{"translation_text" => "This sentence is written in Zulu."}]
}
Integração com modelos de ML hospedados no Huggingface.co
Frequentemente, a experiência do usuário pode ser aprimorada ao usar um modelo de aprendizado de máquina para inferir o significado da solicitação enviada. Uma maneira conveniente de fazer isso é usar modelos hospedados em Huggingface.co.
Aqui está um exemplo usando as funções hf_connect e hf_infer para interagir com as APIs do Huggingface.
Configurando a conexão com um modelo
No card a seguir, configuramos uma conexão com um modelo hospedado no Huggingface e armazenamos a referência dele em uma variável chamada connection
.
card Start, then: HFCard do
connection =
hf_connect(
"michellejieli/NSFW_text_classifier",
"<- insira seu token da API Huggingface aqui ->"
)
query = ask("What is your question?")
end
No exemplo acima, estamos usando um modelo de classificação NSFW, gratuito, chamado michellejieli/NSFW_text_classifier
. Você pode substituí-lo por um modelo de sua preferência. Se tiver um modelo rodando em um domínio customizado do Huggingface, você pode se referir a ele pela URL:
hf_connect(
"https://my-custom.endpoints.huggingface.cloud/the-model",
"<- insira seu token da API Huggingface aqui ->"
)
Chamando a API de Inferência com o modelo
No card a seguir, usamos a função hf_infer() para enviar a consulta para o modelo referenciado na variável connection
.
Conforme a documentação do modelo no Huggingface, a resposta do modelo tem o seguinte formato:
[
[
{
"label": "SFW",
"score": 0.9408659934997559
},
{
"label": "NSFW",
"score": 0.05913396179676056
}
]
]
Processando os resultados do modelo
O modelo, neste exemplo, retorna uma lista de resultados e precisamos escolher o resultado com a maior pontuação. O modelo pode já sempre retornar o rótulo com maior pontuação como o primeiro valor da lista, mas como não temos certeza, aplicamos uma classificação na jornada mesmo.
card HFCard, then: HFResult do
# Este modelo retorna uma lista com listas, queremos ler o primeiro item da lista
# pois ele contém a lista de pontuações para a query
all_responses = hf_infer(connection, query)[0]
# Ordenamos em ordem decrescente de score
sorted_responses = sort_by(all_responses, &(&1.score * -1))
# Pegamos o primeiro item das respostas ordenadas, ou seja, aquele com a maior pontuação
top_response = sorted_responses[0]
end
A Turn.io chama a API do Huggingface com o parâmetro wait_for_model
definido como true
. Se o modelo em questão levar algum tempo para iniciar, as primeiras interações podem enfrentar um tempo limite. Observe que o tempo limite padrão para modelos Huggingface é de 5 segundos. Isso atualmente não é configurável.
Usando os resultados para tomar uma decisão
Em seguida, usamos o resultado armazenado na variável top_response
para determinar o que fazer com a query original. Neste caso, como é apenas uma demonstração, nós apenas retornamos o label e a pontuação. Em suas aplicações, provavelmente você desejará modificar isso para fazer algo mais adequado ao seu caso de uso e requisitos.
card HFResult when top_response.label == "NSFW" do
text("Isso foi classificado como NSFW com uma pontuação de @top_response.score")
end
card HFResult when top_response.label == "SFW" do
text("Isso foi classificado como SFW com uma pontuação de @top_response.score")
end
card HFResult do
text("Huggingface retornou algo inesperado: @all_responses")
end
Atribuindo rótulos a mensagens
Ao lidar com um momento crucial em uma jornada — por exemplo, uma pergunta que recebeu uma resposta específica — pode ser útil adicionar um rótulo a uma mensagem. Isso permite incluí-la em coleções configuradas especificamente para rastrear mensagens com um ou mais rótulos aplicados.
Adicionar um rótulo pode ser feito com a função add_label().
card AddLabel do
add_label("a plain label")
add_label("a label with metadata", extra: "label assigned at @now()")
end
Se o rótulo ainda não existir, ele será criado. Em alguns casos, pode ser útil atribuir metadados adicionais sobre o rótulo. Eles podem ser adicionados como pares chave-valor como segundo argumento da função add_label(). Esses metadados são armazenados na atribuição entre a mensagem e o rótulo. Se alguém recuperar os rótulos atribuídos a uma mensagem via API, os metadados estarão disponíveis lá.
O rótulo é sempre aplicado à última mensagem recebida do usuário.
Atribuindo chats a um operador
Mesmo nos serviços mais automatizados, algumas perguntas simplesmente precisam da atenção de um humano. As Jornadas permitem que um chat seja atribuído a uma conta dentro da organização Turn.io à qual o número pertence. Assim como os rótulos, essa atribuição pode ser usada para criar coleções que exijam a atenção de um operador específico.
Atribuir um chat a um operador pode ser feito com a função assign_chat_to().
card AssignChat do
assign_chat_to("user@example.org")
end
Isso só funcionará se o endereço de e-mail pertencer a uma conta conhecida dentro da organização do número. Se o e-mail não for reconhecido, a atribuição do chat será removida.
Para remover explicitamente a atribuição de operador de um chat, use a função unassign_chat():
unassign_chat()
Log no Simulador das Jornadas
Às vezes, queremos entender e depurar o comportamento das nossas jornadas sem adicionar mensagens ao chat. O Simulador de Jornadas permite que façamos isso registrando mensagens com a função log.
Um exemplo simples em que apenas queremos adicionar texto:
card TextAndLogMessage do
text("Hello this is my first text.")
log("a simple message")
text("Hello this is my second text.")
end
Você também pode usá-lo para registrar estruturas de dados mais complexas, como o dicionário abaixo. Você também poderia registrar estruturas como @contact, disponibilizadas no chat.
card LogData do
text("@params.items.greeting!")
log("@params")
log("@contact")
end
Resultará em algo como:
- Você precisa passar uma string. Se quiser avaliar uma estrutura mais complexa como contacts, use uma expressão — ou seja, use log("@contact"), em vez de log(contact). A jornada destacará esse problema como um erro se você tentar fazer isso.
- log atualmente só funciona no simulador de Jornadas. Não afetará interações quando se usa um Stack diretamente via WhatsApp.
- Logs são temporários. Eles desaparecem a cada vez que você inicia uma nova sessão no simulador de Jornadas.
Funções avançadas
Tempo limite de interação
As jornadas têm um valor de tempo limite. O padrão é 300 segundos (equivalente a 5 minutos). Se o usuário demorar mais de 5 minutos para responder a uma solicitação de entrada (por exemplo, uma pergunta de ask()), a jornada expirará. Isso significa que não responderá conforme o esperado se o usuário retornar depois de mais de 5 minutos.
Você pode alterar o tempo limite para qualquer valor que faça sentido para você. Os valores de tempo limite precisam ser especificados em segundos.
Sintaxe
O tempo limite é definido com o parâmetro opcional interaction_timeout.
stack MyJourney, interaction_timeout: 20 do
...
end
Exemplo
O exemplo a seguir dá ao usuário 10 segundos para responder e ganhar. Se o usuário demorar mais de 10 segundos, a jornada expirou e o usuário não ganhou.
O usuário respondeu em menos de 10 segundos.
O usuário demorou mais de 10 segundos para responder e a jornada expirou.
interaction_timeout(10)
card Card, then: Win do
ask("You have 10 seconds to reply to win!")
end
card Win do
text("congrats! you replied on time")
end
Pontos importantes
- Os valores de tempo limite precisam ser especificados em segundos.
- O valor padrão de tempo limite é 300 segundos (equivalente a 5 minutos).