Conceitos Fundamentais
A Função on_event
A função on_event no seu arquivo .lua principal é o coração da sua aplicação. É o ponto de entrada único que recebe e roteia todos os eventos da plataforma Turn.io. Eventos comuns incluem:
"install": Seu app está sendo instalado."uninstall": Seu app está sendo desinstalado."upgrade": Seu app está sendo atualizado de uma versão mais antiga. Veja Atualizações de Apps para detalhes."downgrade": Seu app está sendo revertido para uma versão mais antiga. Veja Atualizações de Apps para detalhes."config_changed": A configuração do seu app foi atualizada. Este é um bom momento para reavaliar configurações ou subscrições."contact_fields_changed": Um contato foi atualizado, e um ou mais dos campos alterados são campos aos quais seu app se inscreveu. A tabeladataconteráuuid(o UUID do contato) echanges(uma lista de tuplas{campo, valor_antigo, valor_novo})."http_request": A URL de webhook pública do seu app recebeu uma requisição."journey_event": Uma Jornada chamou seu app.
Sua função receberá quatro argumentos:
app: Uma tabela contendo a configuração da instância do seu app, incluindo seu UUID único.number: Uma tabela com informações sobre o número onde o app está instalado.event: Uma string identificando o tipo do evento.data: Uma tabela contendo dados específicos daquele evento.
Uma funcionalidade central dos apps é a subscrição a alterações em campos de contato específicos. Você pode definir essas subscrições usando a nova API turn.app, geralmente em resposta aos eventos install e config_changed.
local App = {}
local turn = require("turn")
function App.on_event(app, number, event, data)
if event == "install" or event == "config_changed" then
-- Na instalação ou alteração de configuração, (re)defina os campos de contato que queremos observar.
-- Isso usa a nova API `turn.app`.
turn.app.set_contact_subscriptions({"name", "surname", "age"})
return true
elseif event == "contact_fields_changed" then
-- Reage a campos específicos e subscritos que foram atualizados em um contato.
local contact_uuid = data.uuid
local changes = data.changes -- Uma tabela de tuplas {campo, valor_antigo, valor_novo}
local _updated_contact = data.contact -- O contato completo com todos os campos, incluindo os alterados
for _, change in ipairs(changes) do
local field, old_val, new_val = change[1], change[2], change[3]
turn.logger.info(
"Contato " .. contact_uuid .. " campo '" .. field .. "' alterado de '" ..
tostring(old_val) .. "' para '" .. tostring(new_val) .. "'"
)
end
return true
elseif event == "http_request" then
-- Lida com uma requisição HTTP recebida para o endpoint único do seu app
return true, { status = 200, body = "Olá do meu app!" }
else
-- É uma boa prática lidar com eventos não tratados como `uninstall` ou o `contact_changed` geral.
turn.logger.warning("Evento não tratado recebido: " .. event)
return false
end
end
return App
Integrando com Jornadas
Uma das funcionalidades mais poderosas dos Apps Lua é sua capacidade de integrar diretamente com Jornadas usando o bloco app(). Isso permite adicionar lógica personalizada, cálculos ou chamadas de API bem no meio de uma conversa.
Chamando um App a partir de uma Jornada
Na sua Jornada você pode chamar uma função de app assim:
card GetWeather do
# Chama a função 'get_forecast' no 'weather_app'
weather_data = app("weather_app", "get_forecast", ["Cape Town"])
# O resultado está disponível na variável 'weather_data'
text("O clima em Cape Town é: @(weather_data.result.temperature)°C")
end
Isso aciona um journey_event no seu app Lua.
Lidando com um journey_event
Seu app deve lidar com o journey_event e pode controlar o fluxo da Jornada pelo seu valor de retorno.
Fluxo Síncrono: continue
Para operações que completam instantaneamente, retorne "continue" junto com o resultado. A Jornada prosseguirá para o próximo bloco sem pausar.
- Casos de uso: Validação de dados, cálculos simples, formatação de texto.
- Assinatura de retorno:
return "continue", result_table
-- Na sua função on_event
elseif event == "journey_event" and data.function_name == "add" then
local sum = tonumber(data.args[1]) + tonumber(data.args[2])
-- A Jornada continua imediatamente com o resultado
return "continue", { value = sum }
end
Fluxo Assíncrono: wait e turn.leases
Para tarefas de longa duração, como aguardar um webhook de confirmação de pagamento, você pode dizer à Jornada para pausar retornando "wait".
A Jornada permanecerá pausada até que seu app explicitamente a retome enviando dados para seu lease. Um lease é uma retenção temporária do estado da Jornada, identificado pelo chat_uuid.
- Casos de uso: Aguardar webhooks, aprovações humanas ou atrasos temporizados.
- Assinatura de retorno:
return "wait"
Exemplo de Fluxo de Trabalho: Aguardando um Webhook de Pagamento
-
Jornada Inicia e Aguarda: A Jornada chama seu app, que inicia um pagamento e diz à Jornada para aguardar.
-- manipulador de journey_event
if data.function_name == "waitForPayment" then
-- O app pode chamar uma API de pagamento externa aqui
-- ...
-- Agora, diga à Jornada para pausar
return "wait"
end -
Webhook Externo Chega: Mais tarde, seu provedor de pagamento envia um webhook para o endpoint HTTP do seu app. O manipulador
http_requestdo seu app o analisa. -
App Retoma a Jornada: Dentro do manipulador
http_request, você usaturn.leases.send_input()com ochat_uuidoriginal para retomar a Jornada correta e entregar o resultado.-- manipulador de http_request
-- Assuma que você obteve o chat_uuid dos metadados do webhook
local chat_uuid = webhook_payload.metadata.chat_uuid
local result_data = {
payment_confirmed = true,
transaction_id = "txn_123"
}
turn.leases.send_input(chat_uuid, result_data)
A Jornada recebe os result_data e automaticamente retoma a execução.
Entendendo os Dados de journey_event
O parâmetro data no seu manipulador journey_event contém contexto importante:
elseif event == "journey_event" then
-- data contém:
-- data.function_name - A função sendo chamada (ex: "calculate", "fetch_data")
-- data.args - Array de argumentos passados da Jornada (já avaliados)
-- data.chat_uuid - UUID do chat (pode ser nil no simulador)
local function_name = data.function_name
local args = data.args or {}
local chat_uuid = data.chat_uuid -- Armazene isso para operações assíncronas!
if function_name == "process_order" then
local order_id = args[1]
local amount = args[2]
-- Processa e retorna resultado
return "continue", { order_id = order_id, status = "processed" }
end
end
Tratamento de Erros em journey_event
O tratamento adequado de erros garante que sua Jornada possa lidar graciosamente com problemas esperados e inesperados:
elseif event == "journey_event" then
local function_name = data.function_name
if function_name == "validate_input" then
local input = data.args[1]
-- Erro de lógica de negócio (esperado, Jornada continua)
if not input or input == "" then
return "continue", { valid = false, error = "Entrada não pode estar vazia" }
end
-- Caso de sucesso
return "continue", { valid = true, processed_input = string.upper(input) }
elseif function_name == "external_api_call" then
-- Erro de sistema (inesperado, Jornada deve lidar com caminho de erro)
local response, status = turn.http.request({
url = "https://api.example.com/data",
method = "GET"
})
if status ~= 200 then
return "error", "Chamada de API falhou com status: " .. status
end
return "continue", turn.json.decode(response)
else
-- Função desconhecida
return "continue", {
success = false,
message = "Função desconhecida: " .. function_name
}
end
end