Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@mundobobba/bot-sdk

Official SDK for building bots on the Mundo Bobba platform.

npm version License: MIT

Installation

npm install @mundobobba/bot-sdk

System requirements (for music bots)

  • yt-dlp installed and available in PATH
  • FFmpeg installed and available in PATH
  • A cookies.txt file from YouTube in your bot's working directory

Quick start

import { MundobobbaBot } from '@mundobobba/bot-sdk';

const bot = new MundobobbaBot({
  token: process.env.BOT_TOKEN!,
  prefix: '!',
});

bot.on('ready', () => {
  console.log(`Bot online as ${bot.botName}`);
});

bot.textCommand('ping', (ctx) => {
  ctx.reply('Pong!');
});

Getting your token

  1. Go to mundobobba.com and sign in
  2. Navigate to Messages > Bots
  3. Create a new bot and copy the generated token
  4. Invite the bot to a conversation using the invite link

Text commands

Register commands that respond to messages starting with your prefix.

const bot = new MundobobbaBot({ token: '...', prefix: '!' });

// Simple reply
bot.textCommand('hello', (ctx) => {
  ctx.reply(`Hello, ${ctx.invokedBy.name}!`);
});

// Access command arguments
bot.textCommand('echo', (ctx) => {
  ctx.reply(ctx.args || 'Nothing to echo.');
});

// Built-in commands: !ping, !uptime, !help
bot.useBuiltins();

Command context

Every command handler receives a CommandContext with:

Property Type Description
command string Command name
args string Arguments after the command
convId number Conversation ID
invokedBy { id, name } User who invoked
reply(message) function Public reply (everyone sees)
replyEphemeral(message) function Reply only the invoker sees
deferReply() function Show "thinking..." indicator
followUp(message) function Additional message in same block

Embeds

Send rich cards with title, description, color, fields, images, and more.

bot.textCommand('stats', async (ctx) => {
  const user = await bot.getUser(ctx.invokedBy.id);

  ctx.reply({
    embed: {
      title: user.name,
      description: `Level ${user.level}`,
      color: '#5865F2',
      thumbnail: `https://habbo.com/avatar/${user.name}`,
      fields: [
        { name: 'Coins', value: user.coin.toLocaleString(), inline: true },
        { name: 'XP', value: user.xp.toLocaleString(), inline: true },
      ],
      footer: { text: 'Mundo Bobba' },
      timestamp: new Date().toISOString(),
    },
  });
});

Buttons

Add interactive buttons to your messages.

bot.textCommand('confirm', (ctx) => {
  ctx.reply({
    content: 'Are you sure?',
    components: [{
      type: 'action_row',
      components: [
        { type: 'button', style: 'success', label: 'Yes', customId: 'yes' },
        { type: 'button', style: 'danger', label: 'No', customId: 'no' },
        { type: 'button', style: 'link', label: 'Docs', url: 'https://mundobobba.com' },
      ],
    }],
  });
});

bot.on('interaction', (ctx) => {
  if (ctx.customId === 'yes') ctx.reply('Confirmed!');
  if (ctx.customId === 'no') ctx.replyEphemeral('Cancelled.');
});

Button styles

Style Appearance
'primary' Blue (default action)
'secondary' Gray (secondary action)
'success' Green (confirm)
'danger' Red (destructive)
'link' Opens a URL (no interaction event)

Select menus

Add dropdown menus to your messages.

bot.textCommand('class', (ctx) => {
  ctx.reply({
    embed: { title: 'Choose your class', color: '#FF9900' },
    components: [{
      type: 'action_row',
      components: [{
        type: 'select',
        customId: 'class_select',
        placeholder: 'Select a class...',
        options: [
          { label: 'Warrior', value: 'warrior', emoji: '⚔️' },
          { label: 'Mage', value: 'mage', emoji: '🧙' },
          { label: 'Archer', value: 'archer', emoji: '🏹' },
        ],
      }],
    }],
  });
});

bot.on('interaction', (ctx) => {
  if (ctx.customId === 'class_select') {
    ctx.reply(`You chose: ${ctx.values[0]}`);
  }
});

Deferred replies

Show a "thinking..." indicator while processing.

bot.textCommand('weather', async (ctx) => {
  const deferred = ctx.deferReply();

  const data = await fetchWeatherAPI(ctx.args);

  deferred.editReply({
    embed: {
      title: `Weather in ${data.city}`,
      description: `${data.temp} - ${data.condition}`,
      color: '#87CEEB',
    },
  });
});

Ephemeral messages

Send messages only the invoker can see.

bot.textCommand('secret', (ctx) => {
  ctx.replyEphemeral(`Only you can see this, ${ctx.invokedBy.name}!`);
});

Modals / Forms

Open a form dialog and handle submissions.

bot.textCommand('feedback', (ctx) => {
  bot.showModal(ctx.convId, ctx.invokedBy.id, {
    customId: 'feedback_form',
    title: 'Send Feedback',
    fields: [
      { type: 'text', customId: 'subject', label: 'Subject', required: true },
      { type: 'textarea', customId: 'body', label: 'Message', maxLength: 500 },
    ],
  });
});

bot.on('modalSubmit', (ctx) => {
  if (ctx.customId === 'feedback_form') {
    ctx.reply(`Thanks for your feedback, ${ctx.invokedBy.name}!`);
    console.log(ctx.values); // { subject: '...', body: '...' }
  }
});

Collectors

Wait for messages or interactions with a timeout.

// Wait for a message
bot.textCommand('ask', async (ctx) => {
  ctx.reply('What is your favorite color? (15s)');

  const messages = await bot.awaitMessages(ctx.convId, {
    filter: (m) => m.senderId === ctx.invokedBy.id,
    timeout: 15_000,
    max: 1,
  });

  if (messages.length > 0) {
    ctx.followUp(`You said: ${messages[0].content}`);
  } else {
    ctx.followUp('Time is up!');
  }
});

// Wait for a button click
const clicks = await bot.awaitInteraction(ctx.convId, {
  filter: (i) => i.customId === 'confirm',
  timeout: 30_000,
  max: 1,
});

Paginated embeds

Navigate between pages using buttons.

import { PaginatedEmbed } from '@mundobobba/bot-sdk';

bot.textCommand('rules', (ctx) => {
  const pages = [
    { title: 'Rules — General', description: '1. Be respectful\n2. No spam', color: '#5865F2' },
    { title: 'Rules — Chat', description: '3. Use correct channels\n4. No flood', color: '#5865F2' },
    { title: 'Rules — Trade', description: '5. No scam\n6. Fair prices', color: '#5865F2' },
  ];

  const paginator = new PaginatedEmbed(pages);
  paginator.send(ctx);

  // Handle page navigation
  bot.on('interaction', (i) => paginator.handleInteraction(i));
});

Confirm dialogs

Ask yes/no questions with a Promise.

import { ConfirmDialog } from '@mundobobba/bot-sdk';

bot.textCommand('reset', async (ctx) => {
  const confirmed = await ConfirmDialog.ask(ctx, {
    title: 'Confirm Reset',
    description: 'Are you sure you want to reset your data?',
    color: '#FF0000',
  });

  if (confirmed) ctx.followUp('Data has been reset.');
  else ctx.followUp('Cancelled.');
});

// Required: handle confirm dialog interactions
bot.on('interaction', (ctx) => ConfirmDialog.handleInteraction(ctx.customId));

Presence

Set the bot's status and activity.

bot.setPresence({
  status: 'online',
  activity: { type: 'listening', name: 'Lo-fi Hip Hop' },
});
Status Description
'online' Green indicator
'idle' Yellow indicator
'dnd' Red indicator
Activity type Display
'playing' Playing [name]
'listening' Listening to [name]
'watching' Watching [name]
'streaming' Streaming [name]

Event listeners

// Any chat message in conversations with the bot
bot.on('message', (msg) => {
  console.log(`${msg.senderName}: ${msg.content}`);
});

// User reacted to a message
bot.on('reaction', (data) => {
  console.log(`${data.userName} reacted with ${data.emoji}`);
});

// User joined a conversation
bot.on('memberJoin', (data) => {
  bot.sendMessage(data.convId, `Welcome, ${data.userName}!`);
});

// User left a conversation
bot.on('memberLeave', (data) => {
  bot.sendMessage(data.convId, `${data.userName} left.`);
});

// Someone mentioned @BotName
bot.on('mention', (data) => {
  bot.sendMessage(data.convId, `You called, ${data.userName}?`);
});

// A message was edited
bot.on('messageEdit', (data) => {
  console.log(`Message ${data.messageId} was edited.`);
});

// A message was deleted
bot.on('messageDelete', (data) => {
  console.log(`Message ${data.messageId} was deleted.`);
});

Platform API

Query platform data directly from your bot.

// Get user info
const user = await bot.getUser(userId);
console.log(user.name, user.level, user.coin);

// Get user permissions
const perms = await bot.getPermissions(userId);
if (!perms.isAdmin) ctx.replyEphemeral('No permission.');

// Get conversation info
const conv = await bot.getConversation(convId);
console.log(conv.name, conv.memberCount, conv.members);

// Get user profile (full)
const profile = await bot.api('getUserProfile', { name: 'PlayerOne' });

Scheduled messages

Send messages at a specific time.

await bot.schedule(convId, 'Good morning!', new Date('2026-04-06T08:00:00Z'));

await bot.schedule(convId, {
  embed: { title: 'Reminder', description: 'Meeting starts now!', color: '#FF0000' },
}, new Date(Date.now() + 60_000)); // 1 minute from now

Music bot

Build a music bot with audio streaming.

import { MundobobbaBot, AudioStreamer, YouTubeResolver, QueueService } from '@mundobobba/bot-sdk';

const TOKEN = process.env.BOT_TOKEN!;
const bot = new MundobobbaBot({ token: TOKEN, prefix: '!' });
const streamer = new AudioStreamer({ token: TOKEN });
const youtube = new YouTubeResolver();
const queue = new QueueService();

bot.textCommand('play', async (ctx) => {
  const result = await youtube.search(ctx.args);
  if (!result) { ctx.reply('Not found.'); return; }

  if (streamer.isStreaming(ctx.convId)) {
    queue.add(ctx.convId, { ...result, addedBy: ctx.invokedBy.name });
    ctx.reply(`Added to queue: ${result.title}`);
    return;
  }

  ctx.reply(`Now playing: ${result.title}`);
  bot.broadcastMusicPlaying(ctx.convId, result);

  await streamer.start(ctx.convId, result.url, (convId) => {
    const next = queue.shift(convId);
    if (!next) { bot.broadcastMusicStopped(convId); return; }
    bot.broadcastMusicPlaying(convId, next);
    streamer.start(convId, next.url, () => {});
  });
});

bot.textCommand('stop', (ctx) => {
  streamer.stop(ctx.convId);
  queue.clear(ctx.convId);
  bot.broadcastMusicStopped(ctx.convId);
  ctx.reply('Stopped.');
});

Rate limiter

Prevent command spam.

import { RateLimiter } from '@mundobobba/bot-sdk';

// Per user (default)
const limiter = new RateLimiter();

// Per conversation
const limiter = new RateLimiter({ mode: 'conversation' });

// Custom cooldowns
const limiter = new RateLimiter({
  cooldowns: { play: 5000, skip: 2000 },
});

bot.textCommand('play', (ctx) => {
  const result = limiter.check(ctx.invokedBy.id, 'play');
  if (!result.allowed) {
    ctx.replyEphemeral(`Wait ${result.remainingSec}s.`);
    return;
  }
  // ... handle command
});

Edit and delete messages

// Edit a bot message
bot.editMessage('bot_123', convId, 'Updated content');

// Delete a bot message
bot.deleteMessage('bot_123', convId);

Slash commands

Register commands that appear in the platform's command picker.

bot.on('ready', () => {
  bot.registerCommands([
    {
      name: 'play',
      description: 'Play a song',
      options: [{ name: 'query', description: 'Song name or URL', required: true }],
    },
    { name: 'stop', description: 'Stop playback' },
  ]);
});

bot.on('command', (ctx) => {
  if (ctx.command === 'play') {
    // Handle slash command
  }
});

API Reference

MundobobbaBot

Method Description
textCommand(name, handler) Register a text command
useBuiltins() Register ping, uptime, help
registerCommands(commands) Register slash commands
sendMessage(convId, message) Send public message
sendEphemeral(convId, message, userId) Send ephemeral message
editMessage(msgId, convId, message) Edit bot message
deleteMessage(msgId, convId) Delete bot message
showModal(convId, userId, modal) Open modal dialog
setPresence(presence) Set bot status
broadcastMusicPlaying(convId, info) Broadcast now playing
broadcastMusicStopped(convId) Broadcast music stopped
broadcastQueueUpdated(convId, queue) Update queue UI
api(method, params) Call platform API
getUser(userId) Get user info
getConversation(convId) Get conversation info
getPermissions(userId) Get user permissions
schedule(convId, message, date) Schedule a message
awaitMessages(convId, options) Wait for messages
awaitInteraction(convId, options) Wait for interactions

Events

Event Payload Description
ready Bot connected
command CommandContext Slash command invoked
message ChatMessage Any chat message
interaction ComponentInteraction Button/select interaction
reaction ReactionEvent Message reaction
memberJoin MemberEvent User joined
memberLeave MemberEvent User left
mention { userId, userName, convId, content } Bot was mentioned
modalSubmit ModalSubmitEvent Modal form submitted
messageEdit { messageId, content, convId } Message edited
messageDelete { messageId, convId } Message deleted
disconnect WebSocket disconnected
error Error WebSocket error

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages