Automate bonk.io without opening a browser.

bonktools is a TypeScript library that speaks the game’s protocol directly. Create rooms, control teams, detect AFK players and keep a room running 24 hours a day.

npm install bonktools

Get startedRead the documentation

Version 0.1.4 · MIT · Node.js 20.18 or newer

bot.ts
import { createRoom } from 'bonktools';

const room = await createRoom({
  auth: { type: 'registered', username, password },
  desiredState: {
    roomName: 'My Room', password: '',
    maxPlayers: 6, mode: 'b', rounds: 3,
  },
});

room.lockTeams();       // only the host moves players
room.enableAntiAfk();   // 12 s idle = AFK

room.on('player-afk', (id) => room.kickPlayer(id));
room.on('chat-message', ({ message }) => {
  if (message === '!ping') room.chat('Pong!');
});

console.log(room.shareLink); // https://bonk.io/123456abcde

Features

  • Direct protocol connection. Socket.IO v2 and the bonk.io TLS chain are already handled. No Puppeteer, no virtual display, no fragile UI automation. Protocol
  • 24/7 rooms that recover. BonkSession recreates rooms that go down, with exponential backoff, creation throttling and a reconcile every 60 s. 24/7 sessions
  • Typed events. Over 30 events with typed payloads, and the room state always available in room.state. Events
  • Team control. lockTeams() stops players from switching on their own: only the host moves players. Anyone who joins later already sees the room locked. Teams and lock
  • Anti-AFK. Detects 12 s without moving and without chatting, reading movement from the WebRTC input frames. Anti-AFK
  • Spectators see the match. Whoever joins while a match is running gets the match state right away, without restarting it for everyone. Matches

How it works

You choose the level of abstraction: a raw socket, a room with state and events, or a room pool that keeps itself alive. Each room is a lightweight connection; there is no need to render the game just to host a room.

Your code
    │
BonkSession     room pool · shared login · throttle · 60 s reconcile
BonkRoom        state · typed events · teams · lock · anti-AFK · reconnection
BonkTransport   Socket.IO v2 (EIO=3) · TLS · timesync
    │
bonk.io         + PeerJS/WebRTC for the players' input frames
  • Failures under control. Transient drops are recreated. Bans, full rooms and exhausted retries are terminal and notify you.
  • Documented protocol. The reverse engineering is recorded in detail, with packet formats and pitfalls. See the protocol

Examples

Create and join rooms

typescript
import { createRoom, joinRoom } from 'bonktools';

// Create a room (the bot becomes the host)
const room = await createRoom({
  auth: { type: 'registered', username, password },
  desiredState: { roomName: 'HERMES', password: '', maxPlayers: 6, mode: 'b', rounds: 3 },
  hidden: false,
});
console.log('Link:', room.shareLink);

// Join an existing room
const guest = await joinRoom('https://bonk.io/123456abcde', {
  auth: { type: 'guest', guestName: 'BonkBot' },
  role: 'spectator',
});

Events and state

typescript
room.on('player-join', (p) => room.chat(`Welcome, ${p.userName}!`));
room.on('team-change', (p) => console.log(`player ${p.id} → team ${p.team}`));

room.on('game-start', () => console.log('match started'));
room.on('game-end',   () => console.log('match ended'));

room.on('room-dead',    (reason) => console.error('room went down:', reason.kind));
room.on('room-rebuilt', (link)   => console.log('room recreated:', link));

// state is always at hand
room.state.players.forEach((p) => console.log(p.userName, p.team));

Teams and anti-AFK

typescript
room.lockTeams();                              // nobody switches teams on their own
room.enableAntiAfk({ thresholdMs: 12_000 });   // 12 s without moving or chatting

room.on('player-afk', (id) => {
  room.chat(`${room.state.players.get(id)?.userName} is AFK!`);
  room.kickPlayer(id);
});
room.on('player-back', () => room.chat('is back!'));

room.setTeam(id, 3);    // the host can still move players (3 = blue)
room.isAfk(id);         // one-off check

24/7 session

typescript
import { BonkSession } from 'bonktools';

const session = new BonkSession({
  auth: { type: 'registered', username, password },
  throttle: { capacity: 1, refillPerSec: 0.3 },
});
await session.getToken();

session.on('room-added', (localId) => {
  const { room } = session.rooms.get(localId)!;
  console.log('room active:', room.shareLink);
});
session.on('room-dead-terminal', ({ localId, reason }) => console.error(localId, reason));

// declarative: recreates the room by itself if it goes down
await session.startFromConfig({
  rooms: [{ id: 'main', name: 'ATLAS', maxPlayers: 6, mode: 'b', rounds: 3 }],
  throttle: { maxConcurrentRooms: 1, roomCreationDelayMs: 3000, roomCreationJitterMs: 2000 },
});

Example: a 24/7 football room

The repository includes a complete reference bot built only with the library. It builds the teams, picks captains, rotates the champion and handles AFK players. Use it as a base for yours.

  • Always equal teams. Based on maxTeamSize: never 2v1, 3v1 or 3v4.
  • Captains pick. When the spectators can fill the teams, the captain types the number of the player they want; the last player joins on their own.
  • The winner stays blue. The loser goes to the end of the queue and the first in the queue becomes the red captain.
  • AFK without fuss. !afk toggles, AFK players are never picked, and someone who is playing cannot declare themselves AFK.
  • Locked teams. Only the bot moves players.

How the pick works

Blue

  • Ana captain

Red

empty

Spectators (queue)

empty

1. Ana joins

The first player goes to blue and becomes captain. The solo match starts right away.

1 / 8 · maxTeamSize = 2

Commands

CommandWho can use itWhat it does
!afkany playerToggles AFK: goes to spectators and can’t be picked. During a match, whoever is playing can’t go AFK.
2 (just the number)captain whose turn it isPicks player number 2 from the list, no command needed (30 s; with no answer, the first is picked).
!win blue · !win redplayer on the fieldReports the winner and triggers the team rotation.
!start · !stopplayer on the fieldStarts or stops the match (!stop holds the restart until the next !start).

Room documentation · Pick rules

Quality

Game rooms show bugs that unit tests miss, so every feature goes through three layers of validation:

  • 123 tests in the library. Room, teams, lock, anti-AFK, codec and reconnection.
  • 32 tests in the 24/7 room, with fuzzing. Over 600 random sequences of joins, leaves, AFK and picks, checking the team proportions on every match.
  • Real players. Real browsers playing; we even measure whether the disc actually moves on screen.

Versions

  1. 0.1.0 Foundation: transport, room with state and events, 24/7 session.
  2. 0.1.1 Per-player balance (BALANCE_SET) reflected in the state.
  3. 0.1.2 A spectator who joins while a match is running now sees the game.
  4. 0.1.3 Anti-AFK and the WebRTC handshake, with the peer-input event.
  5. 0.1.4 lockTeams() and unlockTeams(), plus the gs.tl and team color fixes.

Get started in 3 steps

Requires Node.js 20.18 or newer and a bonk.io account.

  1. Install.
    bash
    npm install bonktools
  2. Write the bot.
    bot.ts
    import { createRoom } from 'bonktools';
    
    const room = await createRoom({
      auth: { type: 'registered', username: process.env.BONK_USER!, password: process.env.BONK_PASS! },
      desiredState: { roomName: 'Hello, bonk!', password: '', maxPlayers: 6, mode: 'b', rounds: 3 },
    });
    console.log(room.shareLink);
  3. Run it.
    bash
    BONK_USER=your_user BONK_PASS=your_password npx tsx bot.ts
    # → https://bonk.io/123456abcde

Read the full guide