Redeye

REFERÊNCIA

Redeye Script

Linguagem dinâmica e tipada-em-runtime para criar comandos personalizados, cronjobs e webhooks no Discord. Sintaxe semelhante a Kotlin/JavaScript, sandbox isolado por servidor, sem build step.

v1sandboxserver-side

01 · Sintaxe

Sintaxe básica

Scripts são executados em um sandbox isolado a cada invocação. Variáveis declaradas dentro do script só vivem durante aquela execução; pra persistir use storage.

Variáveis

var · const
var nome = "Redeye"     // mutável
const pi = 3.14         // constante

nome = "Outro"          // ok
pi = 3                  // erro em runtime

Apenas as palavras-chave var e const existem; val e let não são reconhecidos.

Condicionais

if · else
if user.name == "Victor" {
    reply "Olá, criador!"
} else if user.isBot {
    reply "Você é um bot."
} else {
    reply "Olá, {user.name}!"
}

Funções definidas pelo usuário

fun
fun dobro(n) {
    return n * 2
}

reply "O dobro de 5 é {dobro(5)}"

Limites profundidade máxima de chamada é 64 e o total de passos por execução é 50.000.

Null coalescing

??
var target = args[0] ?? user
reply "Alvo: {target.name}"

O operador ?? retorna o lado direito se o esquerdo for null.

Atalho de chamada

bare-call

Funções de efeito colateral como reply aceitam sintaxe sem parênteses no nível do statement:

reply "Olá, mundo"          // bare-call
reply("Olá, mundo")         // chamada explícita - equivalente

02 · Tipos

Tipos primitivos

Seis tipos exposto à linguagem. typeof() também pode retornar "function" para funções definidas pelo usuário e "proxy" para handlers nativos como storage.

Tipo Exemplo Descrição
number 42, 3.14, -1.5 Inteiros e decimais (IEEE 754)
string "texto" Texto Unicode entre aspas duplas
boolean true, false Verdadeiro ou falso
null null Ausência de valor
list list(1, 2, 3) Lista ordenada (criar com a função list)
object { nome: "x", n: 20 } Objeto com chaves string e valores arbitrários

String interpolation

{expr}
var nome = "Victor"
reply "Olá, {nome}!"        // Olá, Victor!
reply "1 + 2 = {1 + 2}"     // 1 + 2 = 3

Use {expressão} dentro de strings pra interpolar valores. Pra um literal {, escape com \{.

Objetos

literal
var perfil = {
    nome: "Victor",
    nivel: 10,
    vip: true
}

reply "{perfil.nome} é nível {perfil.nivel}"
perfil["nivel"] = 11    // index assignment também funciona

Sintaxe o separador é : (não =). Não existe literal de array com [] - use list(...).

03 · Operadores

Operadores

Operador Descrição Exemplo
+ Soma ou concatenação 1 + 2, "a" + "b"
- Subtração 10 - 3
* Multiplicação 4 * 5
/ Divisão 10 / 3
% Resto 10 % 3
== Igualdade x == 5
!= Diferença x != 5
< > <= >= Comparação x > 10
&& E lógico (curto-circuito) x > 0 && x < 10
|| Ou lógico (curto-circuito) x == 0 || x == 1
! Negação !flag
?? Null coalescing valor ?? "padrão"

Atenção operadores & e | sozinhos são erro de lexer. Use sempre && / ||.

04 · Strings

Métodos de string

Acessíveis via dot/call em qualquer valor string.

Método Tipo Descrição
.length number Quantidade de caracteres
.upper() string Caixa alta
.lower() string Caixa baixa
.trim() string Remove espaços nas pontas
.contains(s) boolean Se contém a substring
.startsWith(s) boolean Se começa com
.endsWith(s) boolean Se termina com
.replace(de, pra) string Substitui todas as ocorrências
.substring(ini) string Recorte do índice até o fim
.substring(ini, fim) string Recorte de ini (incl) a fim (excl)
.split(sep) list Divide em lista de strings
.split(sep, limit) list Divide com limite de partes

Limite strings são limitadas a 1 MB de tamanho durante a execução.

05 · Listas

Métodos de lista

Método Tipo Descrição
.length number Quantidade de itens
.isEmpty boolean Se está vazia
.first any Primeiro item (null se vazia)
.last any Último item (null se vazia)
.push(item) void Adiciona ao final
.pop() any Remove e retorna o último

Acesso e atribuição por índice

var nums = list(10, 20, 30)
reply nums[0]               // 10
nums[1] = 99                // index assignment
reply nums.length           // 3

Limite listas são limitadas a 10.000 elementos.

06 · Built-ins

Funções built-in

Saída

Função Descrição
reply(msg) Envia uma mensagem (string ou embed). Limite 5 por execução.

Matemática

Função Descrição
random() Decimal aleatório no intervalo [0.0, 1.0)
random(max) Inteiro aleatório no intervalo [0, max) - max é exclusivo
random(min, max) Inteiro aleatório no intervalo [min, max] - ambos inclusivos
floor(n) Arredonda pra baixo
ceil(n) Arredonda pra cima
round(n) Arredonda pro mais próximo
abs(n) Valor absoluto
min(a, b, ...) Menor valor (variádica)
max(a, b, ...) Maior valor (variádica)

Conversão e utilidades

Função Descrição
number(val) Converte para number
string(val) Converte para string
typeof(val) Retorna o tipo como string
len(val) Tamanho de string, lista ou objeto
list(a, b, ...) Cria uma lista (variádica)
now() Timestamp atual em milissegundos (epoch)

07 · Contexto

Objetos de contexto

Esses objetos estão disponíveis automaticamente em qualquer script invocado por comando.

user

quem executou
Propriedade Tipo Descrição
user.name string Nome de exibição
user.id string ID do usuário
user.asMention string Menção (@user)
user.avatar string URL do avatar
user.isBot boolean Se é um bot
user.balance number Saldo de iris
user.aboutMe string Texto do about me
user.hasRole(nome) boolean Se tem o cargo (busca por nome)

guild

servidor atual
Propriedade Tipo Descrição
guild.name string Nome do servidor
guild.id string ID do servidor
guild.memberCount number Quantidade de membros
guild.getMemberById(id) user / null Busca um membro pelo ID
guild.getRoleById(id) role / null Busca um cargo pelo ID
guild.addRoleToMember(uid, rid) void Adiciona o cargo (conta no limite de Discord calls)
guild.removeRoleFromMember(uid, rid) void Remove o cargo (conta no limite de Discord calls)

channel

canal atual
Propriedade Tipo Descrição
channel.name string Nome do canal
channel.id string ID do canal
channel.asMention string Menção (#canal)

role

objeto retornado por getRoleById
Propriedade Tipo Descrição
role.id string ID do cargo
role.name string Nome
role.asMention string Menção (@cargo)
role.color string Cor hexadecimal

08 · Args

Argumentos do comando

Os argumentos são acessados via args, uma lista posicional. Tipagem é definida nas opções do script (no painel) - user, number, string... - e o bot já entrega o valor já resolvido.

// Comando configurado com 2 opções: target (user) e amount (number)
var alvo = args[0] ?? user      // padrão: quem executou
var qtd = args[1] ?? 1

reply "{alvo.asMention} recebeu {qtd} pontos!"

09 · Storage

Storage persistente

Key-value persistente compartilhado entre todos os scripts do mesmo servidor. Aceita string, number, boolean, list e object como valor.

Método Descrição
storage.get(key) Lê um valor (null se não existir)
storage.set(key, value) Salva um valor
storage.delete(key) Remove um valor

Exemplo - perfil persistente

var dados = storage.get(user.id)

if dados == null {
    storage.set(user.id, { pontos: 0, nivel: 1 })
    reply "Conta criada!"
} else {
    dados.pontos = dados.pontos + 1
    storage.set(user.id, dados)
    reply "Você tem {dados.pontos} pontos!"
}

Limites plano free: até 500 chaves por servidor; cada valor pode ter no máximo 4 KB serializado. Tiers Premium aumentam ambos.

10 · Secrets

Secrets (chaves de API)

Valores criptografados configurados no painel e injetados na execução. Apenas leitura - scripts não podem sobrescrever.

var apiKey = secrets.get("MINHA_API_KEY")
var res = fetch("https://api.exemplo.com/dados?key={apiKey}")
reply "Resposta: {res.status}"

11 · Embeds

Embeds

Mensagens ricas com layout do Discord. O bloco embed { ... } é uma builder call - pode ser passado direto pra reply.

reply embed {
    title "Título do embed"
    description "Uma descrição legal"
    color "#dc2626"
    thumbnail user.avatar

    field("Campo 1", "Valor 1", true)
    field("Campo 2", "Valor 2", true)

    footer "Redeye Bot"
    footerIcon user.avatar
}
Propriedade Descrição
title Título do embed
description Descrição
color Cor em hex (#RRGGBB)
url Link no título
thumbnail URL de imagem pequena
image URL de imagem grande
footer Texto do rodapé
footerIcon URL do ícone do rodapé
author Nome do autor
authorIcon URL do ícone do autor
authorUrl Link do autor
field(nome, valor, inline?) Adiciona um campo

12 · HTTP

HTTP (fetch)

Requisições GET para APIs externas. Apenas HTTPS. JSON é parseado automaticamente; resposta não-JSON volta como string.

var res = fetch("https://api.exemplo.com/dados")

// res já vem parseado se for JSON
reply "Resultado: {res.campo}"
Restrição Valor
Método GET (somente)
Protocolo HTTPS (somente)
Hosts bloqueados localhost, IPs privados, link-local
Timeout de conexão 3 segundos
Timeout total 5 segundos
Tamanho máximo da resposta 256 KB
Requests por execução 3

13 · Cargos

Manipulação de cargos

Adicionar e remover cargos é uma chamada à API do Discord, contabilizada num contador compartilhado de 10 chamadas por execução.

var role = guild.getRoleById("123456789")
if role != null && !user.hasRole(role.name) {
    guild.addRoleToMember(user.id, role.id)
    reply "{role.asMention} adicionado a {user.asMention}"
}

Permissões o bot precisa ter cargo acima do alvo e a permissão Manage Roles pra que a operação funcione.

14 · Cronjobs

Cronjobs (premium)

Scripts que rodam automaticamente em intervalos definidos. Disponível em servidores com tier Macula (10), Retina (50) ou Sclera (200 cronjobs por servidor).

Aceita dois formatos de schedule:

Formato Exemplo Descrição
Intervalo simples 5m, 1h, 1d Roda a cada N minutos, horas ou dias
Cron parcial */15 * * * * Sintaxe cron - aqui, a cada 15 minutos

Sandbox cronjobs não têm user, channel nem args disponíveis. Pra postar mensagens, você configura um canal-alvo ou usa um webhook.

15 · Limites

Limites de execução

Aplicados por execução pra manter o sandbox justo. Atingir qualquer limite aborta o script com erro.

Recurso Limite
Replies por execução 5
Fetch requests por execução 3
Discord API calls por execução 10
Profundidade de chamada de função 64
Passos de execução 50.000
Tamanho máximo de string 1 MB
Tamanho máximo de lista 10.000 elementos
Resposta HTTP máxima 256 KB
Timeout de conexão (fetch) 3 segundos
Timeout total (fetch) 5 segundos
Storage: chaves (free) 500
Storage: tamanho por valor 4 KB

16 · Exemplos

Exemplos completos

Cara ou coroa

if random(2) == 0 {
    reply "Cara!"
} else {
    reply "Coroa!"
}

Contador persistente

var target = args[0] ?? user
var dados = storage.get(target.id) ?? { count: 0 }

dados.count = dados.count + 1
storage.set(target.id, dados)

reply "{target.name} agora tem {dados.count} pontos."

API externa

var piada = fetch("https://v2.jokeapi.dev/joke/Any?type=single&lang=pt")
reply piada.joke

Embed com dados do usuário

var target = args[0] ?? user

reply embed {
    title target.name
    description target.aboutMe ?? "Sem about me"
    color "#dc2626"
    thumbnail target.avatar
    field("Saldo", "{target.balance} iris", true)
    field("Bot?", "{target.isBot}", true)
}

Sistema de nível simples

var perfil = storage.get("nivel_{user.id}") ?? { xp: 0, nivel: 1 }

perfil.xp = perfil.xp + random(5, 15)

if perfil.xp >= perfil.nivel * 100 {
    perfil.nivel = perfil.nivel + 1
    perfil.xp = 0
    reply "Subiu para nível {perfil.nivel}!"
} else {
    reply "XP: {perfil.xp}/{perfil.nivel * 100}"
}

storage.set("nivel_{user.id}", perfil)

Configure scripts, cronjobs, secrets e webhooks no painel web. Pra contexto sobre os sistemas do bot que os scripts podem orquestrar (economia, broker, empresas), veja o guia.