¿Qué es un Bot Externo?
Una aplicación en tu propio servidor que se comunica con AnarkWorld de forma bidireccional.
Webhooks (Entrada)
AnarkWorld envía eventos POST a tu URL cada vez que ocurre algo en los grupos donde tu bot está activo.
Gateway API (Salida)
Tu bot llama a la API REST para enviar mensajes, moderar, guardar datos o lanzar Mini-Apps.
Persistencia JSONB
Cada bot tiene su propia base de datos por grupo. Economía, rankings, inventarios — todo sin base de datos propia.
Socket Bridge
Señales de latencia cero para juegos y apps multiplayer en tiempo real usando los sockets de AnarkWorld.
Autenticación
Genera tu token en la Bot Factory (prefijo
akw_) y envíalo en cada request.
Authorization: Bearer akw_tu_token_secreto
// Node.js / Axios const axios = require('axios'); const api = axios.create({ baseURL: 'https://davcenter.servequake.com/app/api/bot-gateway', headers: { 'Authorization': `Bearer akw_tu_token_secreto`, 'Content-Type': 'application/json', 'User-Agent': 'MiBotAwesome/1.0' } }); // Prueba de conexión api.get('/emojis').then(r => console.log('✅ Conectado', r.data));
# Python / requests import requests BASE_URL = "https://davcenter.servequake.com/app/api/bot-gateway" HEADERS = { "Authorization": "Bearer akw_tu_token_secreto", "Content-Type": "application/json", "User-Agent": "MiBotAwesome/1.0" } # Prueba de conexión r = requests.get(f"{BASE_URL}/emojis", headers=HEADERS) print("✅ Conectado", r.json())
Recepción de Eventos (Webhooks)
AnarkWorld hace POST a tu Webhook URL configurada en la
Bot Factory. Debes responder con HTTP 200 en menos de 3
segundos.
cleanContent del evento new_message para parsear comandos. Este
campo ya no tiene etiquetas de mención ni emojis.
Evento: new_message
Se dispara cuando un usuario envía texto, multimedia o un comando en el grupo.
{
"event": "new_message",
"groupId": 26,
"groupName": "Gamer Pro Hub",
"membersCount":45,
"groupOwner": "CapitanZero",
"messageId": "temp-1678291002",
"senderId": 13,
"senderName": "TestUser",
"content": "Hola bot, revisa a @Isac",
"cleanContent":"Hola bot, revisa a", // ← úsalo para parsear comandos
"targetId": "50", // ← usuario @mencionado
"targetName": "Isac",
"senderRoles": [{ "id": 121, "name": "Moderador Pro" }],
"botPermissions": [{
"is_admin": false,
"can_send_messages":true,
"can_send_photos": true,
"can_use_music": true
}]
}
Eventos de Sistema
{
"event": "user_joined",
"groupId": 26,
"userId": 3,
"username":"Isac ♨️"
}
{
"event": "user_left",
"groupId": 26,
"userId": 3,
"username":"Isac ♨️"
}
{
"event": "user_kicked",
"groupId": 26,
"targetId": 102,
"adminId": 13,
"adminName":"Test"
}
{
"event": "reaction_added",
"groupId": 26,
"messageId":1450,
"emoji": "🔥",
"userId": 13
}
Mensajería
Base: https://davcenter.servequake.com/app/api/bot-gateway
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /send-message | Envía texto plano o con Rich Tags. |
| POST | /send-media | Envía imagen, video o audio (multipart/form-data). |
| POST | /react | Añade/quita una reacción (Toggle). |
| POST | /typing | Activa el estado "escribiendo..." para humanizar el bot. |
// Enviar mensaje con typing humano async function enviarConTyping(groupId, texto, parentId = null) { // 1. Activar "escribiendo..." await api.post('/typing', { groupId, isTyping: true }); await new Promise(r => setTimeout(r, 1500)); // 2. Enviar mensaje await api.post('/send-message', { groupId, content: texto, ...(parentId && { parentMessageId: parentId }) }); } // Enviar imagen const FormData = require('form-data'); const fs = require('fs'); const form = new FormData(); form.append('file', fs.createReadStream('banner.jpg')); form.append('type', 'IMAGE'); form.append('groupId', '26'); await api.post('/send-media', form, { headers: form.getHeaders() });
import time # Enviar mensaje con typing humano def enviar_con_typing(group_id, texto, parent_id=None): # 1. Activar "escribiendo..." requests.post(f"{BASE_URL}/typing", json={"groupId": group_id, "isTyping": True}, headers=HEADERS) time.sleep(1.5) # 2. Enviar mensaje payload = {"groupId": group_id, "content": texto} if parent_id: payload["parentMessageId"] = parent_id requests.post(f"{BASE_URL}/send-message", json=payload, headers=HEADERS) # Enviar imagen with open("banner.jpg", "rb") as f: requests.post(f"{BASE_URL}/send-media", files={"file": ("banner.jpg", f, "image/jpeg")}, data={"type": "IMAGE", "groupId": "26"}, headers={"Authorization": "Bearer akw_tu_token"})
Persistencia / Base de Datos
Almacenamiento JSONB ultra-rápido por bot y grupo. Sin necesidad de base de datos propia.
// --- GUARDAR / MODIFICAR --- await api.post('/storage/set', { groupId: 26, botId: 108, key: 'user_13_monedas', value: '100', operation: 'add' // suma 100 monedas }); // Registrar cooldown (fecha actual) await api.post('/storage/set', { groupId: 26, botId: 108, key: 'cooldown_user_13', operation: 'now' // guarda Date.now() }); // --- LEER --- const { data } = await api.get( '/storage/get?groupId=26&botId=108&key=user_13_monedas' ); console.log(data.value); // "600" // --- LEADERBOARD Top 10 --- const { data: ranking } = await api.get( '/storage/leaderboard?groupId=26&botId=108&key=user_13_monedas' ); // Devuelve array con nombre y foto de perfil reales
# --- GUARDAR / MODIFICAR --- requests.post(f"{BASE_URL}/storage/set", json={ "groupId": 26, "botId": 108, "key": "user_13_monedas", "value": "100", "operation": "add" }, headers=HEADERS) # Registrar cooldown requests.post(f"{BASE_URL}/storage/set", json={"groupId": 26, "botId": 108, "key": "cooldown_user_13", "operation": "now"}, headers=HEADERS) # --- LEER --- r = requests.get( f"{BASE_URL}/storage/get", params={"groupId": 26, "botId": 108, "key": "user_13_monedas"}, headers=HEADERS) print(r.json()["value"]) # "600" # --- LEADERBOARD --- ranking = requests.get( f"{BASE_URL}/storage/leaderboard", params={"groupId": 26, "botId": 108, "key": "user_13_monedas"}, headers=HEADERS).json()
Moderación y Jerarquía
El bot debe tener rango de ADMIN en el grupo para usar estos endpoints.
| Método | Endpoint | Descripción |
|---|---|---|
| POST | /roles/manage | Asigna o quita un rol. Refresca colores en tiempo real. |
| POST | /kick | Expulsa a un usuario del grupo. |
| POST | /mute | Silencia a un usuario por un tiempo (ej:
"8h", "30m"). |
| POST | /clear-chat | Borra los últimos N mensajes del grupo. |
// Asignar rol await api.post('/roles/manage', { groupId: 26, targetId: 13, roleId: 121, action: 'add' }); // Quitar rol await api.post('/roles/manage', { groupId: 26, targetId: 13, roleId: 121, action: 'remove' }); // Silenciar 8 horas await api.post('/mute', { groupId: 26, targetId: 13, duration: '8h' }); // Expulsar usuario await api.post('/kick', { groupId: 26, targetId: 13 }); // Limpiar los últimos 20 mensajes await api.post('/clear-chat', { groupId: 26, amount: 20 });
# Asignar rol requests.post(f"{BASE_URL}/roles/manage", json={"groupId": 26, "targetId": 13, "roleId": 121, "action": "add"}, headers=HEADERS) # Silenciar 8 horas requests.post(f"{BASE_URL}/mute", json={"groupId": 26, "targetId": 13, "duration": "8h"}, headers=HEADERS) # Expulsar usuario requests.post(f"{BASE_URL}/kick", json={"groupId": 26, "targetId": 13}, headers=HEADERS) # Limpiar mensajes requests.post(f"{BASE_URL}/clear-chat", json={"groupId": 26, "amount": 20}, headers=HEADERS)
Utilidades
Recursos auxiliares: emojis, música y uploads temporales.
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /emojis | Lista de emojis personalizados del servidor (.webp / .json Lottie). |
| GET | /search-music?q=nombre | Búsqueda en iTunes. Devuelve metadatos listos para el reproductor. |
| POST | /upload-media | Sube un archivo y obtiene URL interna. Se purga cada 7 días. |
Multiplayer Bridge (Real-Time)
Sincronización instantánea de baja latencia mediante Sockets. Envía datos entre jugadores y bots sin pasar por la base de datos.
Señales de Red
Las señales viajan por la memoria RAM del servidor de sockets, lo que garantiza una respuesta casi instantánea (latencia <50ms) ideal para juegos de acción.
Alcance de Sala
Toda señal enviada a un groupId será retransmitida automáticamente a todos los miembros que
tengan la Mini-App abierta en ese momento.
Bot ➔ Jugadores (Gateway)
El bot puede inyectar eventos en los juegos activos (ej: "Aparece un jefe", "Bonus de tiempo").
POST /api/bot-gateway/game-signal { "groupId": 26, "payload": { "type": "spawn_boss", "x": 500, "hp": 1000 } }
Jugador ➔ Grupo (Iframe Bridge)
Usa estos eventos dentro de tu juego para sincronizar movimientos o turnos con otros amigos.
// Emitir señal de juego await api.post('/game-signal', { groupId: 26, payload: { type: 'player_move', x: 10, y: 20, playerId: 13 } }); // En el iframe del juego — recibir la señal window.addEventListener('message', (e) => { if (e.data.type === 'REMOTE_GAME_SIGNAL') { const { x, y, playerId } = e.data.payload; moverJugador(playerId, x, y); } });
# Emitir señal de juego requests.post(f"{BASE_URL}/game-signal", json={ "groupId": 26, "payload": { "type": "player_move", "x": 10, "y": 20, "playerId": 13 } }, headers=HEADERS)
Mini-Apps y Juegos Web
Aloja aplicaciones HTML5 directamente en AnarkWorld y ábrelas como iframe en el chat.
Alojamiento y Logo
Sube un .zip con tu index.html. Si incluyes un archivo icon.png (o jpg)
en la raíz, se usará automáticamente como logo en la tarjeta del chat.
Multiplayer Bridge
Usa el sistema de Signals para enviar datos en tiempo real a otros jugadores del grupo (posiciones, turnos, ataques) con latencia cero.
Sincronización (GET)
No solo puedes guardar, también puedes pedir datos (como el récord anterior) al iniciar el juego para sincronizar el estado del jugador.
Salida Limpia
Tu app puede ordenar su propio cierre mediante un comando de salida, devolviendo al usuario al chat de forma fluida.
const BOT_ID = new URLSearchParams(window.location.search).get('botId'); // 1. PEDIR DATOS AL INICIAR (Ej: Cargar récord) window.parent.postMessage({ type: 'GET_BOT_DATA', key: 'puntos', botId: BOT_ID }, '*'); // 2. ENVIAR SEÑAL MULTIPLAYER (A otros jugadores en línea) window.parent.postMessage({ type: 'SEND_GAME_SIGNAL', payload: { type: 'move', x: 100, y: 250 } }, '*'); } // 3. ESCUCHAR RESPUESTAS Y SEÑALES REMOTAS window.addEventListener('message', (event) => { if (event.data.type === 'BOT_DATA_RESPONSE') { console.log("Récord recuperado:", event.data.value); } if (event.data.type === 'REMOTE_GAME_SIGNAL') { console.log("El jugador", event.data.senderId, "se movió"); } }); // 4. CERRAR APP function salir() { window.parent.postMessage({ type: 'CLOSE_APP' }, '*'); }
[WEBAPP:URL|Título|IconoURL] soporta un tercer parámetro
opcional para forzar un icono externo, aunque el sistema ZIP lo detecta automáticamente.
Sistema de Rich Text Tags
El lenguaje de AnarkWorld para crear interfaces ricas dentro de mensajes de texto.
| Tag | Resultado | Ejemplo |
|---|---|---|
[M:ID:Nombre] |
Mención clicable → abre perfil | [M:13:TestUser] |
[R:ID:Nombre:#HEX] |
Menciona todos con ese rol (coloreado) | [R:121:Mods:#d11f1f] |
[E:archivo] |
Emoji animado Lottie o WebP | [E:fuego.json] |
[WEBAPP:URL|Título] |
Tarjeta premium de juego/app | [WEBAPP:https://...|Jugar] |
[MUSIC_V1]{...} |
Reproductor de música incrustado | [MUSIC_V1]{"title":"Hit",...} |
Ejemplo Completo — Bot de Bienvenida
Cuando un usuario entra, simula escritura, lo saluda con mención y le asigna el rol "Novato".
const express = require('express'); const axios = require('axios'); const app = express(); app.use(express.json()); const API_BASE = 'https://davcenter.servequake.com/app/api/bot-gateway'; const TOKEN = 'akw_tu_token_generado'; const ROLE_NOVATO = 10; const api = axios.create({ baseURL: API_BASE, headers: { 'Authorization': `Bearer ${TOKEN}` } }); app.post('/webhook', async (req, res) => { res.sendStatus(200); // responder inmediatamente (< 3s) const { event, groupId, userId, username } = req.body; if (event === 'user_joined') { // 1. Simular escritura await api.post('/typing', { groupId, isTyping: true }); await new Promise(r => setTimeout(r, 1500)); // 2. Bienvenida con mención await api.post('/send-message', { groupId, content: `¡Bienvenido [M:${userId}:${username}] a la comunidad! 🎉` }); // 3. Asignar rol Novato await api.post('/roles/manage', { groupId, targetId: userId, roleId: ROLE_NOVATO, action: 'add' }); // 4. Registrar en storage (contador de miembros) await api.post('/storage/set', { groupId, botId: 108, key: 'total_bienvenidas', value: '1', operation: 'add' }); } if (event === 'new_message') { const { cleanContent, senderId, senderName, messageId } = req.body; if (cleanContent.startsWith('/ranking')) { const { data } = await api.get( `/storage/leaderboard?groupId=${groupId}&botId=108&key=user_${senderId}_xp` ); const top = data.map((u, i) => `${i + 1}. [M:${u.userId}:${u.username}] — ${u.value} XP` ).join('\n'); await api.post('/send-message', { groupId, content: `🏆 Top Jugadores:\n${top}`, parentMessageId: messageId }); } } }); app.listen(3000, () => console.log('🤖 Bot activo en :3000'));
from flask import Flask, request, jsonify import requests import threading, time app = Flask(__name__) BASE_URL = "https://davcenter.servequake.com/app/api/bot-gateway" HEADERS = {"Authorization": "Bearer akw_tu_token"} ROLE_NOVATO = 10 def procesar_evento(data): event = data.get("event") group_id = data.get("groupId") if event == "user_joined": user_id = data["userId"] username = data["username"] # 1. Typing requests.post(f"{BASE_URL}/typing", json={"groupId": group_id, "isTyping": True}, headers=HEADERS) time.sleep(1.5) # 2. Bienvenida requests.post(f"{BASE_URL}/send-message", json={ "groupId": group_id, "content": f"¡Bienvenido [M:{user_id}:{username}] 🎉!" }, headers=HEADERS) # 3. Rol Novato requests.post(f"{BASE_URL}/roles/manage", json={"groupId": group_id, "targetId": user_id, "roleId": ROLE_NOVATO, "action": "add"}, headers=HEADERS) elif event == "new_message": cmd = data.get("cleanContent", "") msg_id = data.get("messageId") sender = data.get("senderId") if cmd.startswith("/ranking"): r = requests.get(f"{BASE_URL}/storage/leaderboard", params={"groupId": group_id, "botId": 108, "key": f"user_{sender}_xp"}, headers=HEADERS).json() top = "\n".join( f"{i+1}. [M:{u['userId']}:{u['username']}] — {u['value']} XP" for i, u in enumerate(r)) requests.post(f"{BASE_URL}/send-message", json={"groupId": group_id, "content": f"🏆 Top:\n{top}", "parentMessageId": msg_id}, headers=HEADERS) @app.route("/webhook", methods=["POST"]) def webhook(): # Responder 200 inmediatamente, procesar en hilo threading.Thread(target=procesar_evento, args=(request.json(),)).start() return "", 200 if __name__ == "__main__": app.run(port=3000) print("🤖 Bot activo en :3000")
Template "The God Mode" — Referencia Total
El código definitivo para Mini-Apps. Acceso a identidad, lista de miembros, ranking, persistencia y multiplayer.
<!DOCTYPE html> <html> <body> <script> const urlParams = new URLSearchParams(window.location.search); const BOT_ID = urlParams.get('botId'); // 1. PEDIR TODO AL CARGAR window.onload = () => { ask({ type: 'GET_MY_INFO' }); // ¿Quién soy? ask({ type: 'GET_GROUP_CONTEXT' }); // ¿Quiénes están en el grupo? ask({ type: 'GET_LEADERBOARD', key: 'xp', botId: BOT_ID }); // ¿Cómo va el ranking? }; // 2. RECIBIR DATOS DE LA APP window.addEventListener('message', (event) => { const d = event.data; if (d.type === 'MY_INFO_RESPONSE') { console.log("Mi ID:", d.id, "Nombre:", d.name, "Foto:", d.avatar); } if (d.type === 'GROUP_CONTEXT_RESPONSE') { console.log("Grupo:", d.groupName, "Miembros:", d.members); // d.members es un array: [{id, name, avatar, isOnline}, ...] } if (d.type === 'LEADERBOARD_DATA') { console.log("Top 10:", d.data); } if (d.type === 'REMOTE_GAME_SIGNAL') { console.log("El jugador", d.senderId, "envió señal:", d.payload); } }); // 3. FUNCIONES DE ACCIÓN function ask(o) { window.parent.postMessage(o, '*'); } function ganarXP() { ask({ type: 'SET_BOT_DATA', key: 'xp', value: 10, operation: 'add', botId: BOT_ID }); } function emitirMovimiento(x, y) { ask({ type: 'SEND_GAME_SIGNAL', payload: { type: 'move', x, y } }); } function cerrar() { ask({ type: 'CLOSE_APP' }); } </script> </body> </html>
import requests from flask import Flask, request API_URL = "https://davcenter.servequake.com/app/api/bot-gateway" TOKEN = "akw_..." def on_message(data): text = data.get("cleanContent", "").lower() gid = data["groupId"] if text == "/play": url = "https://tuservidor.com/mini-apps/mi-juego/index.html" # Enviamos el comando para que todos puedan unirse requests.post(f"{API_URL}/send-message", json={ "groupId": gid, "content": "¡[M:{data['senderId']}:{data['senderName']}] inició una partida!\n[WEBAPP:{url}|Unirse al Juego]" }, headers={"Authorization": f"Bearer {TOKEN}"}) # Tu bot externo recibirá los datos guardados por la Mini-App # automáticamente en su base de datos de grupo.
Límites y Cuotas
Para garantizar la estabilidad del servidor para todos.
HTTP 429, reduce la velocidad de envío. El sistema aplica protección
anti-spam automática.
Códigos de Respuesta HTTP
Referencia rápida de todos los posibles códigos del Gateway.
akw_token.Bots por Nodos — Logic Lab
El sistema visual de programación sin código integrado directamente en la app AnarkWorld. Crea flujos lógicos encadenando bloques de acciones que se ejecutan cuando se dispara un comando.
Los Bots por Nodos se gestionan desde Bot Factory → Editor de Lógica. No requieren servidores externos: toda la ejecución corre directamente en AnarkWorld.
Sin servidor externo
A diferencia de los Pro Bots (Webhook), los bots de nodos corren dentro del propio servidor de AnarkWorld. No necesitas hosting, VPS ni código.
Cadena de Acciones
Cada Nodo-Comando tiene una lista de acciones que se ejecutan en orden, de arriba a abajo, con soporte para esperas, condiciones y bucles de cooldown.
Variables dinámicas
El motor sustituye variables como {user}, {id} o
{mis_puntos} en tiempo real antes de enviar cualquier mensaje.
DB integrada
Accede al mismo almacenamiento JSONB de los Pro Bots. Las claves escritas por un bot de nodos son legibles por un Pro Bot y viceversa.
Estructura de un Nodo
Internamente, cada nodo es un objeto JSON dentro del array
bot_nodes guardado en la columna de la base de datos del bot.
Así se ve un nodo en el Logic Lab:
Y así es su representación JSON interna:
{
"type": "command",
"triggers": ["/ranking", "top"],
"actions": [
// Paso 1: Leer XP del usuario de la DB
{
"type": "db_get",
"key": "user_{id}_xp",
"var_name": "mis_xp"
},
// Paso 2: Esperar medio segundo
{
"type": "wait",
"value": "0.5"
},
// Paso 3: Enviar respuesta con variable resuelta
{
"type": "send",
"value": "{user} tienes {mis_xp} XP acumulados 🏆"
}
]
}
triggers es un array de strings. El motor busca si el texto limpio
(cleanContent) contiene alguno de ellos, sin distinción de
mayúsculas/minúsculas. No necesita ser una coincidencia exacta.
Catálogo Completo de Acciones
Cada objeto dentro del array actions tiene un
type que define qué hace. Todas las acciones soportan variables
{var} en sus campos de texto.
[M:ID:Nombre], [E:emoji], [WEBAPP:url|titulo]).
{id} para el que escribió el comando o {target_id} para el
@mencionado.[WEBAPP:url|titulo] automáticamente. Soporta campo iconUrl
opcional.open_webapp que genera un mensaje de anuncio de
partida antes de mostrar la tarjeta. Perfecto para juegos grupales."0".
set, add, sub, max,
min, push, now.value y la pasa a la siguiente acción de tipo send.true_val, si es falsa ejecuta false_val. El operador puede
ser == (igual exacto) o contains (contiene).fail_msg y detiene la cadena con
break. Si el tiempo pasó, actualiza el timestamp y continúa.permissions_updated por socket para refrescar
colores en tiempo real.send pero con parentMessageId automático para anclar
la respuesta al mensaje original.{entrada} para usarla en acciones posteriores.fail_msg.cooldown_check o filter_role falla, usa break
internamente y detiene el resto de la cadena. Ninguna acción posterior se ejecuta.
Listeners — Nodos de Evento
Los Listeners son nodos especiales que se disparan
automáticamente ante eventos del sistema, sin que ningún usuario escriba un
comando. Su type es "listener" y en lugar de
triggers usan un campo event.
{
"type": "listener",
"event": "al_recibir_mensaje",
"actions": [...]
}
{user} e {id} contienen los datos del usuario recién unido. Ideal
para bienvenidas automáticas y asignación de roles iniciales.{
"type": "listener",
"event": "al_unirse_usuario",
"actions": [...]
}
{user} e {id} del saliente están disponibles.{
"type": "listener",
"event": "al_salir_usuario",
"actions": [...]
}
{target_user} y {target_id} (el expulsado), también incluye
{user} e {id} del admin que realizó la acción.{
"type": "listener",
"event": "al_ser_expulsado",
"actions": [...]
}
{user} e {id} son del que reaccionó. El emoji usado
está disponible como {emoji}.{
"type": "listener",
"event": "al_reaccionar_mensaje",
"actions": [...]
}
Nodo tipo command
{
"type": "command",
"triggers": [
"/hola", "saludar"
],
"actions": [
{ "type": "send",
"value": "¡Hola {user}!" }
]
}
Se activa cuando el texto contiene un trigger
Nodo tipo listener
{
"type": "listener",
"event": "al_unirse_usuario",
"actions": [
{ "type": "send",
"value": "Bienvenido {user}! 🎉" }
]
}
Se activa automáticamente ante un evento del sistema
{
"type": "listener",
"event": "al_unirse_usuario",
"actions": [
// 1. Esperar 1 segundo (efecto humano)
{ "type": "wait", "value": "1" },
// 2. Dar XP de bienvenida (50 XP)
{
"type": "db_set",
"key": "user_{id}_xp",
"value": "50",
"operation": "add"
},
// 3. Asignar rol "Novato" (ID: 10)
{ "type": "manage_role", "role_id": 10, "action": "add" },
// 4. Enviar mensaje de bienvenida con mención
{
"type": "send",
"value": "👋 ¡Bienvenido [M:{id}:{user}] a {group_name}!\nTienes 50 XP de regalo para empezar 🎁"
}
]
}
{
"type": "listener",
"event": "al_salir_usuario",
"actions": [
// Leer su XP antes de que se vaya (para el mensaje de despedida)
{ "type": "db_get", "key": "user_{id}_xp", "var_name": "xp" },
// Despedida con datos
{
"type": "send",
"value": "😢 {user} nos dejó con {xp} XP acumulados. ¡Hasta pronto!"
},
// Resetear su XP a 0 (borra el progreso)
{
"type": "db_set",
"key": "user_{id}_xp",
"value": "0",
"operation": "set"
}
]
}
{
"type": "listener",
"event": "al_ser_expulsado",
"actions": [
// Informar en el grupo quién expulsó y a quién
{
"type": "send",
"value": "🔨 [M:{target_id}:{target_user}] fue expulsado por [M:{id}:{user}]."
},
// Enviar DM privado al expulsado explicando la situación
{
"type": "send_dm",
"target_id": "{target_id}",
"value": "Has sido expulsado de {group_name}. Contacta con un admin si crees que fue un error."
}
]
}
al_unirse_usuario para la bienvenida y otro para el XP inicial.
Variables del Sistema
El motor sustituye estas variables en cualquier campo value, key, target_id o fail_msg antes de ejecutar la acción. Escríbelas con llaves: {nombre_var}.
db_get se añaden al contexto local con el nombre definido en var_name. Por ejemplo, si defines "var_name": "mis_monedas", podrás usar {mis_monedas} en cualquier acción posterior de esa misma cadena.
Cooldowns y Persistencia
El sistema de cooldowns usa la misma DB JSONB del storage para guardar
timestamps. La clave interna tiene el formato user_{id}_cd_{key}.
Flujo de
cooldown_check:
El JSON completo de este nodo:
{
"type": "command",
"triggers": ["/diario", "recompensa"],
"actions": [
// 1. Verificar cooldown de 24 horas
{
"type": "cooldown_check",
"key": "diario",
"seconds": 86400,
"fail_msg": "⏳ Vuelve en {rem}s para tu próxima recompensa."
},
// 2. Si pasó el cooldown: sumar monedas
{
"type": "db_set",
"key": "user_{id}_coins",
"value": "100",
"operation": "add"
},
// 3. Leer el total actualizado
{
"type": "db_get",
"key": "user_{id}_coins",
"var_name": "total_coins"
},
// 4. Confirmar al usuario
{
"type": "send",
"value": "{user} recibió 100 monedas 🪙\nTotal: {total_coins} 🏦"
}
]
}
user_13_cd_diario (usando el ID real del
usuario). Esto garantiza que cada usuario tenga su propio cooldown independiente, sin que un usuario
afecte al otro.
Flujos de Ejemplo
Nodos listos para importar o usar como referencia. Todos siguen la
estructura bot_nodes[].
[
// NODO 1: Ganar XP (con cooldown de 5 minutos)
{
"type": "command",
"triggers": ["/xp", "ganar xp"],
"actions": [
{ "type": "cooldown_check", "key": "xp", "seconds": 300, "fail_msg": "⏳ Espera {rem}s para ganar más XP." },
{ "type": "db_set", "key": "user_{id}_xp", "value": "10", "operation": "add" },
{ "type": "db_get", "key": "user_{id}_xp", "var_name": "xp" },
{ "type": "react", "value": "⭐" },
{ "type": "send", "value": "+10 XP para {user} ⭐ — Total: {xp} XP" }
]
},
// NODO 2: Consultar mi XP
{
"type": "command",
"triggers": ["/mispo", "mis puntos"],
"actions": [
{ "type": "db_get", "key": "user_{id}_xp", "var_name": "xp" },
{ "type": "send", "value": "📊 {user} tienes {xp} XP acumulados." }
]
}
]
{
"type": "command",
"triggers": [".verificar"],
"actions": [
// Solo roles 121 (Mod) o 8 (Admin) pueden ejecutar este comando
{ "type": "filter_role", "value": "121, 8" },
// Borrar el comando para mantener el chat limpio
{ "type": "delete_trigger" },
// Asignar el rol "Verificado" (ID: 55) al @mencionado
{ "type": "manage_role", "role_id": 55, "action": "add" },
// Confirmar en el chat
{ "type": "send", "value": "✅ {target_user} ha sido verificado por {user}." }
]
}
// BOT NODE: Lanzar el juego { "type": "command", "triggers": ["/jugar", "play"], "actions": [ { "type": "react", "value": "🎮" }, { "type": "wait", "value": "1" }, { "type": "start_game", "game_name": "Flappy Anark", "url": "https://davcenter.servequake.com/mini-apps/108/app_xyz/index.html", "iconUrl": "https://davcenter.servequake.com/mini-apps/108/app_xyz/icon.png" } ] } // MINI-APP (index.html): Guardar récord cuando el jugador pierde const BOT_ID = new URLSearchParams(window.location.search).get('botId'); function onGameOver(puntos) { window.parent.postMessage({ type: 'SET_BOT_DATA', key: 'puntos', value: puntos, operation: 'max', // Solo guarda si supera el récord previo botId: BOT_ID }, '*'); }
{
"type": "command",
"triggers": ["/8ball", "pregunta"],
"actions": [
// Verificar que el mensaje contiene algo (más de solo el comando)
{
"type": "if_branch",
"var": "{msg}",
"op": "==",
"target": "",
"true_val": "❌ Escribe tu pregunta después del comando.",
"false_val": "🎱 Analizando el universo..."
},
// Respuesta mágica aleatoria
{
"type": "random",
"value": "Sí definitivamente 🟢,No lo creo 🔴,Pregunta de nuevo 🔵,Mis fuentes dicen no 🟡,Sin duda 🟢"
},
// Usar el resultado del random como mensaje final
{ "type": "send", "value": "{user} preguntó: {msg}\n🎱 La bola dice: {value}" }
]
}
filter_role + cooldown_check +
db_get + if_branch en una sola cadena para crear sistemas de economía,
niveles o juegos completos sin escribir una sola línea de código backend.