Logic Script
A linguagem de programação nativa da Ayami — leve, segura e feita para automações no Discord. Escreva scripts, reaja a eventos, envie embeds e gerencie dados do servidor com código limpo e direto.
Introdução
O Logic Script é uma linguagem interpretada criada para automações dentro de servidores Discord.
Scripts são arquivos .logic que reagem a eventos do Discord —
mensagens, membros entrando, cliques em botões — usando o bloco on.
Cada servidor tem seu próprio ambiente isolado: scripts, banco de dados e configurações são completamente separados. O Logic Script roda dentro do bot em uma sandbox segura — sem acesso ao sistema operacional ou a recursos externos.
O Logic Script não substitui o Logic Builder — ele o expande. Scripts podem ser chamados a partir de fluxos visuais e disparar eventos personalizados entre si.
-- Meu primeiro script
on messageCreate(message)
if message.content == "!ola" then
message.reply("Olá! 👋")
end
end
Variáveis
Use let para variáveis mutáveis e const para constantes. O escopo é léxico — variáveis declaradas dentro de um bloco não existem fora dele.
let nome = "Ayami"
let pontos = 0
let ativo = true
const PREFIX = "!"
-- Reatribuição (só funciona com let)
pontos = pontos + 10
Tipos de dados
| Tipo | Exemplo | Descrição |
|---|---|---|
| number | 42, 3.14 | Inteiros e decimais |
| string | "texto", 'outro' | Texto entre aspas simples ou duplas |
| bool | true, false | Booleano |
| nil | nil | Ausência de valor |
| array | [1, 2, 3] | Lista ordenada |
| object | { nome: "A" } | Mapa chave → valor |
| function | function() end | Funções são valores de primeira classe |
let lista = ["maçã", "pera", "uva"]
let pessoa = { nome: "Ayami", pontos: 100 }
print(lista[0]) -- "maçã"
print(pessoa.nome) -- "Ayami"
print(type(42)) -- "number"
Operadores
| Op. | Descrição | Exemplo |
|---|---|---|
| + | Adição | 2 + 3 → 5 |
| - | Subtração | 10 - 4 → 6 |
| * | Multiplicação | 3 * 4 → 12 |
| / | Divisão | 10 / 2 → 5 |
| % | Módulo | 10 % 3 → 1 |
| .. | Concatenação | "Oi " .. "Ayami" |
| Op. | Descrição |
|---|---|
| == | Igual |
| != | Diferente |
| < / > | Menor / Maior |
| <= / >= | Menor ou igual / Maior ou igual |
| and | E lógico |
| or | OU lógico |
| not | Negação |
Controle de fluxo
if pontos >= 100 then
print("Ouro")
elseif pontos >= 50 then
print("Prata")
else
print("Bronze")
end
-- while
let i = 1
while i <= 5 do
print(i)
i = i + 1
end
-- for numérico: for var = início, fim [, passo] do
for i = 1, 10 do
print(i)
end
-- for in (iteração)
let frutas = ["maçã", "pera", "uva"]
for fruta in frutas do
print(fruta)
end
Use break para sair de um loop e continue para pular para a próxima iteração.
Funções
-- Declaração
function somar(a, b)
return a + b
end
print(somar(3, 7)) -- 10
-- Funções anônimas (valores de primeira classe)
let dobrar = function(x)
return x * 2
end
print(dobrar(5)) -- 10
Import / Export
Organize código em múltiplos arquivos. Use export para expor funções e import para usá-las em outro script.
export function addCoins(user, qtd)
db.user(user).add("coins", qtd)
return db.user(user).get("coins")
end
import { addCoins } from "economia.logic"
on command("trabalhar", message, args)
let ganho = random(10, 100)
let total = addCoins(message.author, ganho)
message.reply("💰 +" .. ganho .. " coins! Total: " .. total)
end
Eventos (on)
Scripts reagem a eventos do Discord usando o bloco on. Para comandos por prefixo, o primeiro argumento é uma string com o nome do comando.
on nomeDoEvento(param1, param2)
-- código aqui
end
-- Comando por prefixo (ex: !ping)
on command("ping", message, args)
message.reply("🏓 Pong!")
end
| Evento | Parâmetros | Disparado quando |
|---|---|---|
| messageCreate | message | Uma mensagem é enviada |
| messageDelete | message | Uma mensagem é deletada |
| messageEdit | message | Uma mensagem é editada |
| memberJoin | member | Membro entra no servidor |
| memberLeave | member | Membro sai do servidor |
| buttonClick | interaction | Botão é clicado |
| selectMenu | interaction | Opção de menu selecionada |
| command | "nome", message, args | Comando por prefixo executado |
| reactionAdd | emoji, user, message | Reação adicionada |
| reactionRemove | emoji, user, message | Reação removida |
| voiceJoin | member | Membro entra em voz |
| voiceLeave | member | Membro sai de voz |
| banAdd | user | Usuário é banido |
| banRemove | user | Ban removido |
| channelCreate | channel | Canal criado |
| channelDelete | channel | Canal deletado |
Objetos Discord
messageCreate, sendMessage() e message.reply().getUser() ou message.author.getInteraction(). Usado em buttonClick e selectMenu.opts.ephemeral: true = privado.onSubmit(fn)UI — Embed, Button, SelectMenu, Modal
Todos os métodos podem ser encadeados. Embed aceita todos os campos enviáveis pelo Discord.
let embed = new EmbedBuilder()
embed.setTitle("Título")
embed.setDescription("Descrição")
embed.setColor("#A9D6FF")
embed.setThumbnail("https://url.png")
embed.setFooter({ text: "Ayami" })
embed.addFields(
{ name: "Campo", value: "Valor", inline: true }
)
embed.setTimestamp(nil) -- nil = agora
sendMessage(message.channel, "", { embed: embed })
let btn = Button()
btn.setLabel("Clique aqui")
btn.setStyle("primary") -- primary | secondary | success | danger | link
btn.setCustomId("meu_botao")
btn.setEmoji("🎉")
let menu = SelectMenu()
menu.setCustomId("meu_menu")
menu.setPlaceholder("Escolha uma opção")
menu.addOptions(
{ label: "Opção 1", value: "op1" },
{ label: "Opção 2", value: "op2" }
)
sendMessage(message.channel, "Escolha:", {
components: [btn, menu]
})
on buttonClick(interaction)
if interaction.customId == "meu_botao" then
interaction.reply("✅ Clicou!", { ephemeral: true })
end
end
on selectMenu(interaction)
let valor = interaction.values[0]
interaction.reply("Escolheu: " .. valor)
end
on buttonClick(interaction)
if interaction.customId == "abrir_feedback" then
let modal = Modal()
modal.setTitle("Feedback")
modal.addTextInput("nota", "Nota de 1 a 5", { style: "short" })
modal.addTextInput("comentario", "Comentário", { style: "long", required: false })
modal.onSubmit(function(interaction, fields)
interaction.reply("Recebido! Nota: " .. fields.nota)
end)
interaction.showModal(modal)
end
end
Só funciona em resposta a uma interação existente (buttonClick/selectMenu, ou dentro
de .onClick() de um Button/SelectMenu temporário) — não dá pra abrir a partir
de on(command).
Banco de dados
Banco persistente por servidor. Os dados sobrevivem entre execuções e reinicializações.
-- Global (por servidor)
db.set("chave", "valor")
db.get("chave")
db.has("chave") -- → bool
db.delete("chave")
-- Por usuário
db.user(user).set("coins", 100)
db.user(user).get("coins")
db.user(user).add("coins", 50) -- soma
db.user(user).sub("coins", 10) -- subtrai
-- Por servidor (guild)
db.guild().set("config", { prefix: "!" })
db.guild().get("config")
Funções built-in
| Função | Descrição |
|---|---|
| length(s) | Tamanho da string |
| contains(s, sub) | Se contém substring |
| replace(s, de, para) | Substituir texto |
| split(s, sep) | Dividir em array |
| join(arr, sep) | Unir array em string |
| lower(s) / upper(s) | Minúsculas / maiúsculas |
| trim(s) | Remove espaços |
| tostring(v) | Converte para string |
| tonumber(s) | Converte para número |
| Função | Descrição |
|---|---|
| random(min, max) | Inteiro aleatório entre min e max |
| round(n) | Arredonda |
| floor(n) / ceil(n) | Piso / teto |
| abs(n) | Valor absoluto |
| max(a, b, …) / min(a, b, …) | Maior / menor valor |
| Função | Descrição |
|---|---|
| push(arr, v) | Adiciona ao final |
| pop(arr) | Remove e retorna o último |
| insert(arr, i, v) | Insere na posição i |
| remove(arr, i) | Remove na posição i |
| size(v) | Tamanho do array ou objeto |
| keys(obj) | Chaves do objeto |
| values(obj) | Valores do objeto |
| Função | Descrição |
|---|---|
| print(…) | Exibe no console do dashboard |
| type(v) | Tipo do valor como string |
| wait(ms) | Aguarda N ms (máx 30 000ms/exec.) |
| json.encode(v) | Objeto → JSON string |
| json.decode(s) | JSON string → objeto |
| Função | Descrição |
|---|---|
| schedule(tempo, canal, texto, opts?) | Agenda mensagem — tempo: ms, "1h" ou "9:00". Com {recorrente:true} repete |
| cancelSchedule(id) | Cancela um agendamento pendente |
| listSchedules() | Lista agendamentos pendentes do servidor |
| cooldown(chave, tempo) | true se pode agir agora (e já marca); false se em espera |
| cooldownRestante(chave) | Ms restantes do cooldown |
Sandbox & Limites
O que não existe no Logic Script: acesso ao sistema de arquivos, módulos Node.js (require), rede externa fora das APIs do Discord, acesso a outros servidores ou bibliotecas externas.
| Limite | Valor | Descrição |
|---|---|---|
| Passos por execução | 50 000 | Previne loops infinitos |
| Profundidade de recursão | 200 | Previne stack overflow |
| Total de wait() por execução | 30 000ms | Limite acumulado de espera |
| Scripts simultâneos por guild | 20 | Limite de concorrência |
| TTL dos logs | 7 dias | Logs expiram automaticamente |
Exemplos completos
on memberJoin(member)
let canal = getChannel("ID_DO_CANAL")
let guild = getGuild()
let embed = new EmbedBuilder()
embed.setTitle("Bem-vindo(a)! 🌸")
embed.setDescription("Olá, <@" .. member.id .. ">!\nVocê é o membro nº " .. guild.memberCount)
embed.setColor("#A9D6FF")
embed.setFooter({ text: "Ayami Hoshiori" })
embed.setTimestamp(nil)
canal.send("", { embed: embed })
end
on command("trabalhar", message, args)
let ganho = random(10, 100)
db.user(message.author).add("coins", ganho)
let total = db.user(message.author).get("coins")
message.reply("💰 +" .. ganho .. " coins! Saldo: " .. total)
end
on command("saldo", message, args)
let raw = db.user(message.author).get("coins")
let total = raw or 0
message.reply("💳 Saldo: **" .. total .. " coins**")
end
on command("transferir", message, args)
let valor = tonumber(args[0])
let alvo = args[1]
if valor <= 0 or alvo == nil then
message.reply("❌ Use: !transferir <valor> <@usuário>")
return
end
let saldo = db.user(message.author).get("coins") or 0
if saldo < valor then
message.reply("❌ Saldo insuficiente.")
return
end
db.user(message.author).sub("coins", valor)
db.user(alvo).add("coins", valor)
message.reply("✅ Transferência de **" .. valor .. " coins** realizada!")
end
on command("painel", message, args)
let btn1 = Button()
btn1.setLabel("🎨 Designer")
btn1.setStyle("primary")
btn1.setCustomId("cargo_designer")
let btn2 = Button()
btn2.setLabel("💻 Dev")
btn2.setStyle("secondary")
btn2.setCustomId("cargo_dev")
let embed = new EmbedBuilder()
embed.setTitle("🎭 Selecione seu cargo")
embed.setDescription("Clique no botão da sua área.")
embed.setColor("#7C8FFF")
message.reply("", { embed: embed, components: [btn1, btn2] })
end
on buttonClick(interaction)
let user = getUser()
let id = interaction.customId
if id == "cargo_designer" then
user.addRole("ID_CARGO_DESIGNER")
interaction.reply("✅ Cargo Designer adicionado!", { ephemeral: true })
elseif id == "cargo_dev" then
user.addRole("ID_CARGO_DEV")
interaction.reply("✅ Cargo Dev adicionado!", { ephemeral: true })
end
end
Pronto para começar? ⭐
Adicione a Ayami ao seu servidor e abra o editor no Dashboard para criar seus primeiros scripts.