Writing your First Custom Command
In the Dashboard you can add custom commands through the Custom Commands page. Commands are written in JavaScript and work across all platforms Manao supports at once.

Available Context
Section titled “Available Context”Manao runs custom commands in an environment that provides context and args to use:
// User infocontext.user.name // Display namecontext.user.id // Manao User ID (used for cross-platform account linking)context.user.platform // Platform ("twitch" | "kick" | "discord" | "youtube")context.user.platformID // User ID on that specific platformcontext.user.roles.isFollowercontext.user.roles.isSubscribercontext.user.roles.isVIPcontext.user.roles.isModeratorcontext.user.roles.isBroadcaster
// Generalcontext.channel // Channel namecontext.language // Bot language ("en" | "th")context.currency // Currency name
// Send messagesawait context.say("...") // Send a message in chatawait context.reply("...") // Reply with the user's name taggedawait context.whisper("...") // Send a private message (if supported by platform)
// Otherawait context.lookupUser("name") // Look up a user by name, returns id or nullcontext.emit("event", data) // Send an event to overlays
// Arguments typed by the userargs[0], args[1], ... // e.g. !hello world → args[0] = "world"Simple Replies
Section titled “Simple Replies”!hello [name] — Greet a user
const name = args[0] || `@${context.user.name}`;await context.say(`Hello ${name}!`);!roll — Roll a dice
const result = Math.ceil(Math.random() * 6);await context.say(`🎲 Rolled: ${result}`);!dish — Random food suggestion
const dishes = ["Pad Thai", "Green Curry", "Mango Sticky Rice", "Som Tum"];const dish = dishes[Math.floor(Math.random() * dishes.length)];await context.say(`🍲 You should eat: ${dish}!`);Balance Management
Section titled “Balance Management”!transfer <user> <amount> — Transfer currency to another user
const targetName = args[0];const amount = parseInt(args[1], 10);
if (!targetName || isNaN(amount) || amount <= 0) { await context.reply("Usage: !transfer <username> <amount>"); return;}
const targetId = await context.lookupUser(targetName);if (!targetId) { await context.reply(`User ${targetName} not found`); return;}
// Use context.emit to send data to overlays or other event handlerscontext.emit("transfer", { from: context.user.id, to: targetId, amount,});
await context.reply(`Transferred ${amount} ${context.currency} to @${targetName} successfully`);Mini Game
Section titled “Mini Game”!rps <rock/paper/scissors> — Rock Paper Scissors
const choices = ["rock", "paper", "scissors"];const userChoice = args[0]?.toLowerCase();
if (!choices.includes(userChoice)) { await context.reply("Usage: !rps [rock/paper/scissors]"); return;}
const botChoice = choices[Math.floor(Math.random() * choices.length)];let result = "Tie!";if ( (userChoice === "rock" && botChoice === "scissors") || (userChoice === "paper" && botChoice === "rock") || (userChoice === "scissors" && botChoice === "paper")) result = "You win! 🎉";else if (userChoice !== botChoice) result = "You lose! 😢";
await context.say(`✊✋✌️ ${context.user.name} chose ${userChoice}, bot chose ${botChoice}. ${result}`);