npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@strafe-js/strafebot

v1.3.0

Published

TypeScript library for the Strafe Chat API and Gateway

Readme


Overview

@strafe-js/strafebot is the primary all-in-one SDK for building bots on Strafe Chat. It integrates @strafe-js/rest, @strafe-js/ws, @strafe-js/collection, and @strafe-js/util into a single Client with:

  • Automatic Entity Caching: Live Collection caches for spaces, rooms, spaceRooms, and members.
  • Equinox Permission Engine: Full 6-stage hierarchical permission resolution (client.resolveMemberPermissions()).
  • Command Framework: Prefix matching, quote-aware argument tokenization, typed ArgumentsSchema validation, cooldowns, and permission/ownership guards.
  • Re-exported Ecosystem: Conveniently re-exports @strafe-js/util, @strafe-js/collection, @strafe-js/rest, and @strafe-js/ws.

Installation

Node.js 18 or newer is required.

npm install @strafe-js/strafebot
yarn add @strafe-js/strafebot
pnpm add @strafe-js/strafebot
bun add @strafe-js/strafebot

Quick Start

import { Client, command } from '@strafe-js/strafebot';

const client = new Client({
  apiBase: process.env.STRAFE_API || 'https://app.strafe.chat/api',
  gatewayUrl: process.env.STRAFE_GATEWAY || 'wss://app.strafe.chat/gateway/events',
  prefix: '!',
  owners: ['123456789012345678'],
});

client.commands.register(
  command({
    name: 'ping',
    aliases: ['latency'],
    description: 'Check if the bot is responsive',
    cooldown: 3000,
    async run(ctx) {
      await ctx.reply('Pong!');
    },
  }),
);

client.on('ready', async (info) => {
  console.log(`Logged in as ${info.user.username} (${info.user.id})`);
  for (const spaceId of client.spaces.keys()) {
    await client.fetchMembers(spaceId);
  }
});

await client.login(process.env.BOT_TOKEN!);

Client API Reference

new Client(options: ClientOptions)

| Option | Type | Default | Description | | :--- | :--- | :--- | :--- | | apiBase | string | Required | Base URL for the Strafe REST API (https://app.strafe.chat/api) | | gatewayUrl | string | Required | WebSocket URL for the Strafe Gateway (wss://app.strafe.chat/gateway/events) | | token | string | undefined | Bot token (can also be passed to client.login(token)) | | authScheme | 'Bot' \| 'Bearer' | 'Bot' | HTTP & Gateway authorization header scheme | | prefix | string \| string[] | undefined | Command prefix(es) passed to client.commands | | owners | Snowflake[] | [] | User IDs permitted to invoke botOwnerOnly commands | | autoSubscribeSpaces | boolean | true | Automatically subscribe to all spaces received in READY | | heartbeatIntervalMs | number | 45000 | Interval between Gateway Ping frames in milliseconds |

Properties & State Caches

  • client.rest: REST: Underlying HTTP REST client.
  • client.gateway: Gateway: Underlying WebSocket Gateway client.
  • client.commands: CommandHandler: Built-in command registry and dispatcher.
  • client.user: User | null: Authenticated bot user (populated on ready).
  • client.spaces: Collection<Snowflake, Space>: Cached spaces.
  • client.rooms: Collection<Snowflake, Room>: Cached rooms by room ID.
  • client.spaceRooms: Collection<Snowflake, Collection<Snowflake, Room>>: Rooms grouped by space ID.
  • client.members: Collection<Snowflake, Collection<Snowflake, SpaceMember>>: Members grouped by space ID.

Methods

  • await client.login(token?: string) (alias: client.connect(token)): Connects to the Gateway and starts listening for events.
  • await client.fetchMembers(spaceId: Snowflake): Fetches all members of a space from REST and caches them in client.members.
  • await client.fetchMember(spaceId: Snowflake, userId: Snowflake): Fetches and caches a single member with in-flight request deduplication.
  • client.getMember(spaceId: Snowflake, userId: Snowflake): Synchronously reads a cached SpaceMember.
  • client.resolveMemberPermissions(userId: Snowflake, spaceId?: Snowflake, roomId?: Snowflake): BitField: Resolves effective permissions using the 6-stage Equinox permission engine.
  • client.destroy(): Disconnects the Gateway WebSocket and removes all listeners.

Command Framework (CommandHandler, command, Context)

Typed Argument Schemas (arguments)

Commands support strongly-typed argument schemas with automatic validation, default values, range bounds (min/max), choices, and rest capture:

import { command } from '@strafe-js/strafebot';

export const slowmodeCommand = command({
  name: 'roll',
  description: 'Roll a die with custom sides and multiplier',
  guildOnly: true,
  permissions: ['SendMessages'],
  botPermissions: ['SendMessages'],
  arguments: {
    sides: { type: 'integer', min: 2, max: 1000, default: 6 },
    label: { type: 'string', rest: true, default: 'Standard Roll' },
  },
  async run(ctx) {
    const sides = ctx.arguments.get('sides'); // number
    const label = ctx.arguments.get('label'); // string
    const rolled = Math.floor(Math.random() * sides) + 1;
    await ctx.reply(`**${label}**: rolled **${rolled}** (1..${sides})`);
  },
});

Supported argument types:

  • 'string' (string): supports choices
  • 'integer' (number): whole integers, supports min/max value, choices
  • 'number' (number): floating-point numbers, supports min/max value, choices
  • 'boolean' (boolean): accepts true/false, yes/no, 1/0, y/n
  • 'user' (Snowflake): accepts <@id>, <@!id>, or raw 16..20 digit Snowflake IDs
  • 'snowflake' (Snowflake): validates 16..20 digit Snowflake IDs
  • 'attachment' (Attachment): consumes uploaded files from ctx.message.attachments

Context Helpers

Inside run(ctx):

  • ctx.arguments.get(name): Retrieve a validated schema argument.
  • ctx.args: Raw tokenized string arguments (string[]).
  • ctx.authorId, ctx.author, ctx.roomId, ctx.spaceId, ctx.member, ctx.botMember.
  • await ctx.reply(content, extra?): Reply directly to the command message.
  • await ctx.send(content, extra?): Send a message to the same room without a reply reference.
  • await ctx.react(emoji): React to the command message.
  • await ctx.typing(): Trigger typing indicator in the room.
  • ctx.hasPermissions(required) / ctx.hasBotPermissions(required): Check caller or bot permissions in the current room.
  • ctx.isBotOwner() / ctx.isSpaceOwner(): Check ownership status.

Command Guard Events

Listen on client to customize feedback when a command is blocked:

client.on('commandPermissionDenied', (ctx, cmd) => {
  void ctx.reply(`You lack permissions to run \`${cmd.name}\`.`);
});

client.on('commandBotPermissionDenied', (ctx, cmd, missing) => {
  void ctx.reply(`I am missing permissions: ${missing.toArray().join(', ')}`);
});

client.on('commandCooldown', (ctx, cmd, remainingMs) => {
  void ctx.reply(`Please wait ${(remainingMs / 1000).toFixed(1)}s before reusing \`${cmd.name}\`.`);
});

client.on('commandInvalidArguments', (ctx, cmd, err) => {
  void ctx.reply(`Invalid argument: ${err.message}`);
});

Links

License

Apache-2.0