Bot de Comandos¶
Bot con sistema de comandos estructurado y modular.
Estructura del proyecto¶
Codigo¶
src/commands/index.ts¶
import type { WhatsApp } from "@arcaelas/whatsapp";
// Tipo para mensaje (inferido de wa.Message)
type Message = InstanceType<WhatsApp["Message"]>;
export interface CommandContext {
wa: WhatsApp;
msg: Message;
cid: string;
args: string[];
text: string;
}
export interface Command {
name: string;
description: string;
aliases?: string[];
execute: (ctx: CommandContext) => Promise<void>;
}
// Registro de comandos
export const commands = new Map<string, Command>();
// Helper para registrar comando
export function register_command(cmd: Command) {
commands.set(cmd.name, cmd);
cmd.aliases?.forEach(alias => commands.set(alias, cmd));
}
// Importar y registrar comandos
import "./ping";
import "./ayuda";
import "./hora";
src/commands/ping.ts¶
import { register_command } from "./index";
register_command({
name: "ping",
description: "Verificar latencia del bot",
aliases: ["p"],
async execute({ wa, cid }) {
const start = Date.now();
await wa.Message.text(cid, "pong!");
console.log(`Latencia: ${Date.now() - start}ms`);
},
});
src/commands/ayuda.ts¶
import { commands, register_command } from "./index";
register_command({
name: "ayuda",
description: "Mostrar lista de comandos",
aliases: ["help", "h", "?"],
async execute({ wa, cid }) {
const unique_commands = new Map<string, string>();
commands.forEach((cmd) => {
if (!unique_commands.has(cmd.name)) {
unique_commands.set(cmd.name, cmd.description);
}
});
let message = "*Comandos disponibles:*\n\n";
unique_commands.forEach((desc, name) => {
message += `!${name} - ${desc}\n`;
});
await wa.Message.text(cid, message);
},
});
src/commands/hora.ts¶
import { register_command } from "./index";
register_command({
name: "hora",
description: "Mostrar hora actual",
aliases: ["time", "t"],
async execute({ wa, cid }) {
const now = new Date();
const time = now.toLocaleTimeString("es-AR", {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
const date = now.toLocaleDateString("es-AR", {
weekday: "long",
day: "numeric",
month: "long",
year: "numeric",
});
await wa.Message.text(cid, `${time}\n${date}`);
},
});
src/index.ts¶
import { WhatsApp } from "@arcaelas/whatsapp";
import { commands, CommandContext } from "./commands";
const PREFIX = "!";
async function main() {
const wa = new WhatsApp();
wa.event.on("open", () => console.log("Bot conectado"));
wa.event.on("error", (e) => console.error("Error:", e.message));
wa.event.on("message:created", async (msg) => {
// Ignorar mensajes propios
if (msg.me) return;
// Solo texto
if (msg.type !== "text") return;
const text = (await msg.content()).toString();
// Verificar prefijo
if (!text.startsWith(PREFIX)) return;
// Parsear comando y argumentos
const [command_name, ...args] = text.slice(PREFIX.length).split(" ");
const cmd = commands.get(command_name.toLowerCase());
if (!cmd) {
await wa.Message.text(msg.cid, `Comando no encontrado. Escribe ${PREFIX}ayuda`);
return;
}
// Ejecutar comando
const ctx: CommandContext = {
wa,
msg,
cid: msg.cid,
args,
text: text.slice(PREFIX.length + command_name.length + 1),
};
try {
await cmd.execute(ctx);
} catch (error) {
console.error(`Error en comando ${cmd.name}:`, error);
await wa.Message.text(msg.cid, "Ocurrio un error ejecutando el comando");
}
});
// Conectar
await wa.pair(async (data) => {
if (Buffer.isBuffer(data)) {
const fs = await import("fs");
fs.writeFileSync("qr.png", data);
console.log("Escanea qr.png");
}
});
console.log("Bot listo!");
}
main().catch(console.error);
Agregar nuevos comandos¶
Comando con argumentos¶
src/commands/decir.ts
import { register_command } from "./index";
register_command({
name: "decir",
description: "Repetir un mensaje",
aliases: ["echo", "say"],
async execute({ wa, cid, text }) {
if (!text.trim()) {
await wa.Message.text(cid, "Uso: !decir <mensaje>");
return;
}
await wa.Message.text(cid, text);
},
});
Comando con validacion¶
src/commands/dado.ts
import { register_command } from "./index";
register_command({
name: "dado",
description: "Lanzar un dado",
aliases: ["dice", "roll"],
async execute({ wa, cid, args }) {
const sides = parseInt(args[0]) || 6;
if (sides < 2 || sides > 100) {
await wa.Message.text(cid, "El dado debe tener entre 2 y 100 caras");
return;
}
const result = Math.floor(Math.random() * sides) + 1;
await wa.Message.text(cid, `Dado de ${sides} caras: ${result}`);
},
});
Comando solo para grupos¶
src/commands/grupo.ts
import { register_command } from "./index";
register_command({
name: "grupo",
description: "Informacion del grupo",
aliases: ["group", "g"],
async execute({ wa, cid }) {
// Solo en grupos
if (!cid.endsWith("@g.us")) {
await wa.Message.text(cid, "Este comando solo funciona en grupos");
return;
}
const chat = await wa.Chat.get(cid);
if (!chat) return;
const members = await chat.members(0, 1000);
await wa.Message.text(
cid,
`*${chat.name}*\n\n` +
`Miembros: ${members.length}`
);
},
});