const graph = (steps) => ({ nodes: steps.map(([type, config = {}, label], i) => ({ id: `n${i}`, type, config, label: label || type, x: 80 + (i % 3) * 280, y: 80 + Math.floor(i / 3) * 130, })), edges: steps .slice(1) .map((_, i) => ({ from: `n${i}`, to: `n${i + 1}`, port: "out" })), }); export const templates = [ { id: "stats", name: "Estatísticas", description: "Informações e pessoas online na descrição do canal 21.", color: "green", graph: graph([ ["timer", { seconds: 30 }, "Atualizar a cada 30 segundos"], ["serverinfo", {}, "Estado do servidor"], ["clients", {}, "Pessoas online"], [ "sort", { list: "clients", field: "client_lastconnected", direction: "asc" }, "Ordenar por tempo ligado", ], ["limit", { count: 5 }, "Top 5 pessoas"], [ "template", { text: "[b]Informações do servidor[/b]\nOnline: {{server.virtualserver_clientsonline - server.virtualserver_queryclientsonline}} / {{server.virtualserver_maxclients}}\nCanais: {{server.virtualserver_channelsonline}}\n\n[b]Pessoas online[/b]\n{{top}}\n\nAtualizado: {{time}}", }, "Informações e pessoas", ], [ "channelEdit", { channelId: 21, description: "{{text}}" }, "Atualizar descrição do canal", ], ]), }, { id: "welcome", name: "Boas-vindas", description: "Envia uma mensagem privada quando alguém entra no servidor.", color: "cyan", graph: graph([ [ "event", { eventName: "notifycliententerview", filters: { client_type: "0" } }, "Pessoa entrou", ], [ "message", { mode: "client", target: "{{event.clid}}", text: "Bem-vindo, {{event.client_nickname}}!", }, "Mensagem de boas-vindas", ], ]), }, { id: "afk", name: "Mover inativos", description: "Filtra clientes inativos e move cada um para o canal escolhido.", color: "orange", graph: (() => { const g = graph([ ["timer", { seconds: 60 }, "Verificar inatividade"], ["clients", {}, "Listar pessoas"], [ "filter", { list: "clients", field: "client_idle_time", operator: "greater", value: "300000", storeAs: "idle", }, "Inativos há 5 minutos", ], ["foreach", { list: "temp.idle", limit: 100 }, "Para cada pessoa"], [ "move", { clientId: "{{item.clid}}", channelId: 21 }, "Mover para AFK", ], ]); g.edges.at(-1).port = "each"; return g; })(), }, { id: "command", name: "Comando de informações", description: "Responde a !info por mensagem privada; requer eventos SSH.", color: "purple", graph: graph([ ["command", { command: "!info", channelId: "" }, "Comando !info"], ["serverinfo", {}, "Estado do servidor"], [ "message", { mode: "client", target: "{{event.clid}}", text: "Online: {{server.virtualserver_clientsonline - server.virtualserver_queryclientsonline}} / {{server.virtualserver_maxclients}}", }, "Responder ao utilizador", ], ]), }, { id: "counter", name: "Contador de entradas", description: "Mantém uma variável persistente com o número de entradas.", color: "green", graph: graph([ [ "event", { eventName: "notifycliententerview", filters: { client_type: "0" } }, "Entrada no servidor", ], [ "variable", { name: "entradas", scope: "persistent", operation: "increment", value: "1", }, "Incrementar contador", ], ["log", { text: "Entradas: {{variables.entradas}}" }, "Registar total"], ]), }, { id: "custom", name: "Personalizado", description: "Começa com um trigger manual e constrói o teu flow.", color: "purple", graph: graph([["manual", {}, "Início manual"]]), }, ]; export function templateGraph(id = "stats") { const template = templates.find((t) => t.id === id); if (!template) throw new Error("Template desconhecido."); return structuredClone(template.graph); }