Pular para o conteúdo principal

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.".

A simple message example

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.

Example of a question message

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".

Example of a button message

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.

Example of a button message with labels

Pontos importantes

  • Você precisa usar as funções buttons() e text().
  • 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ção buttons().
  • 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.

Example of a list message

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() e text().
  • 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.

Example of buttons being used for a menu

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.

Example of buttons with label

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.

Updated example of buttons with labels

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.

dica

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.

cuidado

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!".

Example of a message with an image

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() e text().
  • 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!".

Example of a message with a video

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() e text().
  • 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!".

Example of a message with an audio

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() e text().
  • 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!".

Example of a message with a document

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() e text().
  • A função document() contém a URL e um parâmetro opcional filename. 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.

Example of a message with a document

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.

info

O ID do seu flow e as telas disponíveis são fornecidos pelo Facebook Business Manager

nota

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:

Botão de Copiar ID 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:

Botão de Copiar ID 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.
info

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.

FornecedorChave do fornecedor
OpenAIopenai
Meta Llamameta_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'.

Copy the assistant 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.

info

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
nota

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

Usando a API da Huggingface Infer das Jornadas

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

log_simple_text_example.png

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.

log_data_structure_dict_example.png

card LogData do
text("@params.items.greeting!")
log("@params")
log("@contact")
end

Resultará em algo como:

log_data_structure_example.png

info
  • 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. Example showing an interaction before timeout

O usuário demorou mais de 10 segundos para responder e a jornada expirou. Example showing an interaction timeout

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).