Pular para o conteúdo principal
Programa de Acesso Antecipado

Os Apps Lua da Turn.io estão atualmente em um programa de acesso antecipado e ainda não estão disponíveis ao público geral. Se você tem interesse em construir aplicativos personalizados e gostaria de participar, entre em contato com nossa equipe de suporte para discutir seu caso de uso.

Construindo Apps Lua na Turn.io

Bem-vindo ao guia do desenvolvedor para construir Apps Lua na plataforma Turn.io! Este guia irá orientá-lo em tudo que você precisa saber para criar, testar e implantar aplicativos poderosos que estendem as capacidades da Turn.io.

O que são os Apps Lua da Turn.io?

Os Apps Lua da Turn são pacotes autocontidos de código Lua que executam de forma segura dentro da plataforma Turn.io. Eles permitem que você implemente lógica de negócio personalizada que pode ser ativada por eventos da plataforma, requisições HTTP, ou diretamente de uma Jornada.

Com os Apps Lua, você pode:

  • Integrar com APIs externas e sistemas de terceiros.
  • Criar fluxos de trabalho complexos e com estado que vão além das capacidades padrão de Jornada.
  • Construir lógica de backend personalizada para seus serviços de mensagens.
  • Lidar com eventos assíncronos, como aguardar um webhook de confirmação de pagamento.

Começando: Seu Primeiro App

A melhor maneira de começar é usando nosso template oficial de app, que inclui uma estrutura básica de app, um framework de testes e um script de empacotamento.

  1. Baixe o Template: Obtenha uma cópia do diretório lua_app_template.

  2. Estrutura do Projeto: Seu novo diretório de app ficará assim:

    my_app/
    ├── my_app.lua # Seu arquivo principal da aplicação
    ├── my_app-0.1.0-0.rockspec # Para gerenciar dependências de teste
    ├── package.sh # Script para empacotar seu app para upload
    ├── turn.lua # Um mock das APIs da Turn.io para teste local
    └── spec/
    └── my_app_spec.lua # Seu arquivo de teste
  3. Instale as Ferramentas de Teste: Você precisará do Lua e LuaRocks para executar testes localmente.

    # No macOS com Homebrew
    brew install lua luarocks

    # No Debian/Ubuntu
    sudo apt-get install lua5.3 luarocks
  4. Instale as Dependências de Teste: Navegue até o diretório do seu app e execute:

    luarocks install --only-deps my_app-0.1.0-0.rockspec

    Isso instalará o Busted (um framework de testes) e outros auxiliares.

Agora você está pronto para começar a desenvolver seu app!

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.

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 de evento (ex: "install", "http_request").
  • data: Uma tabela contendo dados específicos daquele evento.
local App = {}
local turn = require("turn")

function App.on_event(app, number, event, data)
if event == "install" then
-- Realiza configuração quando o app é instalado
turn.logger.info("Meu app foi instalado!")
return true -- Retorna true em caso de sucesso
elseif event == "uninstall" then
-- Realiza limpeza quando o app é desinstalado
turn.logger.info("Meu app foi desinstalado.")
return true
elseif event == "contact_changed" then
-- Reage a um contato sendo atualizado
local contact_uuid = data.uuid
turn.logger.info("Contato " .. contact_uuid .. " foi atualizado.")
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 desconhecidos
turn.logger.warning("Evento desconhecido 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

  1. 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
  2. Webhook Externo Chega: Mais tarde, seu provedor de pagamento envia um webhook para o endpoint HTTP do seu app. O manipulador http_request do seu app o analisa.

  3. App Retoma a Jornada: Dentro do manipulador http_request, você usa turn.leases.send_input() com o chat_uuid original 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.

Referência da API

A tabela global turn fornece acesso em sandbox às funcionalidades da plataforma.

  • turn.assets

    Carrega arquivos estáticos (como templates ou imagens) de uma pasta assets/ no arquivo .zip do seu app.

    • list(directory_path): Lista arquivos em um diretório.
      • directory_path (string, opcional): O caminho dentro de assets/.
    • exists(asset_path): Verifica se um arquivo existe.
      • asset_path (string): O caminho completo para o ativo.
    • load(asset_path): Carrega o conteúdo de um ativo.
      • asset_path (string): O caminho completo para o ativo.
    local journey_files = turn.assets.list("journeys")
    for _, filename in ipairs(journey_files) do
    local content = turn.assets.load("journeys/" .. filename)
    turn.logger.info("Template de jornada carregado: " .. filename)
    end
  • turn.configuration

    Gerencia as configurações do seu app.

    • get_config(): Retorna uma tabela de toda a configuração.
    • get_config_value(key): Retorna o valor para uma chave específica.
      • key (string): A chave de configuração.
    • update_config(updates): Mescla uma tabela de atualizações na configuração.
      • updates (table): Pares chave-valor para atualizar.
    • set_config(new_config): Substitui toda a configuração.
      • new_config (table): A nova tabela de configuração.
    local api_key = turn.configuration.get_config_value("api_key")
    if not api_key then
    turn.logger.error("A chave da API não está configurada!")
    return false
    end
    turn.configuration.update_config({ last_synced_at = os.time() })
  • turn.contacts

    Encontra contatos e gerencia seus campos personalizados.

    • find(query): Encontra um contato.
      • query (table): Pares chave-valor para buscar por (ex: { msisdn = "+27..." }).
    • update_contact_details(contact, details): Atualiza os campos de um contato.
      • contact (table): O objeto contato de find().
      • details (table): Pares chave-valor de campos para atualizar.
    • create_contact_field(field_def): Cria um novo campo personalizado no esquema.
      • field_def (table): Uma tabela com chaves type, name e display.
    local contact, found = turn.contacts.find({ msisdn = "+27820000000" })
    if found then
    turn.contacts.update_contact_details(contact, { loyalty_id = "LTY-12345" })
    end
  • turn.google

    Autentica com APIs do Google.

    • get_access_token(service_account_json, scopes): Obtém um token OAuth2.
      • service_account_json (string): O conteúdo JSON do arquivo de conta de serviço.
      • scopes (table, opcional): Uma lista de escopos da API do Google.
    local ok, token = turn.google.get_access_token(sa_json)
    if ok then
    -- Use token na requisição turn.http
    end
  • turn.http

    Faz requisições HTTP externas.

    • request(options): Envia uma requisição HTTP.
      • options (table): Uma tabela com url, method, headers (table) e body (string).
    local body, status = turn.http.request({
    url = "https://api.example.com/v1/events",
    method = "POST",
    headers = { ["Content-Type"] = "application/json" },
    body = turn.json.encode({ message = "Olá" })
    })
  • turn.journeys

    Gerencia Jornadas programaticamente.

    • create(journey_def): Cria uma nova Jornada.
      • journey_def (table): Uma tabela com name, notebook e enabled.
    • update(journey_uuid, updates): Atualiza uma Jornada existente.
      • journey_uuid (string): O UUID da jornada para atualizar.
      • updates (table): Uma tabela com name, notebook ou enabled.
    • delete(journey_def): Deleta uma Jornada pelo nome.
      • journey_def (table): Uma tabela com o name da jornada.
    • list(): Retorna uma lista de todas as Jornadas.
    local journey, ok = turn.journeys.create({
    name = "Integração de Novo Usuário",
    notebook = turn.assets.load("journeys/onboarding.md"),
    enabled = true
    })
  • turn.json

    Codifica e decodifica dados JSON.

    • encode(data, options): Codifica uma tabela Lua em uma string JSON.
      • data (table): A tabela Lua para codificar.
      • options (table, opcional): ex: { indent = true }.
    • decode(json_string): Decodifica uma string JSON em uma tabela Lua.
      • json_string (string): A string para decodificar.
    local my_table = { name = "João Silva", age = 30 }
    local json_string = turn.json.encode(my_table)
  • turn.leases

    Usado para retomar Jornadas em espera. Veja a seção de fluxo assíncrono para um exemplo detalhado.

    • send_input(chat_uuid, input_data): Envia dados para uma Jornada pausada, retomando-a.
      • chat_uuid (string): O UUID do chat cuja Jornada está aguardando.
      • input_data (any): Os dados para enviar como resultado do bloco app().
    turn.leases.send_input(chat_uuid, { payment_status = "confirmed" })
  • turn.logger

    Escreve logs que são visíveis na UI da Turn.io para depuração.

    • debug(message), info(message), warning(message), error(message)
      • message (string): A mensagem de log.
    turn.logger.error("Falha ao conectar com o banco de dados: " .. err_msg)
  • turn.media

    Salva dados binários (como imagens ou documentos) como mídia que pode ser reutilizada em mensagens.

    • save(media_data): Salva dados binários como um item de mídia.
      • media_data (table): Uma tabela com data (string binária), filename (string) e content_type (string).
    local qr_table, ok = turn.qrcode.generate({ data = "..." })
    if ok then
    local saved, media_info = turn.media.save(qr_table)
    end
  • turn.qrcode

    Gera imagens de código QR.

    • generate(options): Cria um PNG de código QR.
      • options (table): Uma tabela com data (string) e chaves opcionais como filename, color, image_data.
    local qr_table, ok = turn.qrcode.generate({
    data = "https://www.turn.io/",
    color = "#8654CD" -- Roxo da Turn.io!
    })

Testando Seu App

O template de app vem com uma configuração completa de testes usando o framework Busted.

  • APIs Mock: O arquivo turn.lua fornece uma versão simulada das APIs da Turn.io, para que você possa testar a lógica do seu app de forma isolada.
  • Escrevendo Testes: Escreva seus testes no diretório spec/. Você pode usar spies para verificar se seu app chamou as APIs corretas da Turn.io.
  • Executando Testes:
    # Execute todos os testes do diretório raiz do seu app
    busted -C .

Empacotamento para Implantação

Quando seu app estiver pronto, você precisa empacotá-lo em um arquivo .zip para fazer o upload.

  • Use o Script: O script package.sh do template automatiza isso para você. Ele executará seus testes e então criará o arquivo ZIP em um diretório dist/.
    ./package.sh --name my_app --version 1.0.0
  • Empacotamento Manual: Se preferir, você pode compactar os arquivos você mesmo. É crucial incluir apenas os arquivos fonte da sua aplicação (.lua) e quaisquer ativos. Não inclua arquivos de teste ou o arquivo mock turn.lua.

Fornecendo Documentação do App na UI

Para garantir uma ótima experiência do usuário, você pode fornecer documentação que aparecerá diretamente na UI da Turn.io. Lide com o evento get_app_info_markdown e retorne uma string Markdown.

Este é o lugar perfeito para explicar o que seu app faz, listar suas funcionalidades e fornecer instruções de configuração ou exemplos de endpoint de API.

elseif event == "get_app_info_markdown" then
return [[
# Meu App Incrível

Este app se integra com o serviço externo `XYZ`.

## Configuração

Para usar este app, forneça sua `XYZ_API_KEY` na configuração abaixo.

## Endpoint de Webhook

Envie requisições `POST` do seu serviço para a seguinte URL:
`/apps/]] .. app.uuid .. [[/webhook`
]]
end