From bcb444cdc8adb86875d43b2c46857093dcf14a3b Mon Sep 17 00:00:00 2001 From: Coder-soft Date: Tue, 16 Dec 2025 15:33:50 +0500 Subject: [PATCH 1/2] Remove legacy docs HTML files - Delete legacy docs HTML assets - Files affected: api-reference.html, getting-started.html, index.html, plugins.html, security.html, websocket.html, styles.css --- docs/api-reference.html | 1088 ------------------------------------- docs/getting-started.html | 192 ------- docs/index.html | 144 ----- docs/plugins.html | 667 ----------------------- docs/security.html | 201 ------- docs/styles.css | 498 ----------------- docs/websocket.html | 946 -------------------------------- 7 files changed, 3736 deletions(-) delete mode 100644 docs/api-reference.html delete mode 100644 docs/getting-started.html delete mode 100644 docs/index.html delete mode 100644 docs/plugins.html delete mode 100644 docs/security.html delete mode 100644 docs/styles.css delete mode 100644 docs/websocket.html diff --git a/docs/api-reference.html b/docs/api-reference.html deleted file mode 100644 index ee21465..0000000 --- a/docs/api-reference.html +++ /dev/null @@ -1,1088 +0,0 @@ - - - - - - - API Reference - Holo Bridge - - - - - -
- -
- -
-
- - -
-

API Reference

-

Complete REST API documentation for Holo Bridge. All endpoints require authentication via the - X-API-Key header. -

- -
-

Authentication

-

All API requests require the X-API-Key header with your configured API key.

-
curl -H "X-API-Key: your_api_key" http://localhost:3000/api/guilds
- -

Response Format

-

All responses follow a consistent format:

-
// Success
-{
-    "success": true,
-    "data": { ... }
-}
-
-// Error
-{
-    "success": false,
-    "error": "Error message",
-    "code": "ERROR_CODE"
-}
-
- -
-

Guilds

- -
-

GET /api/guilds

-

List all guilds the bot is in.

-
curl -H "X-API-Key: your_key" http://localhost:3000/api/guilds
-
- -
-

GET /api/guilds/:guildId

-

Get details of a specific guild.

-
curl -H "X-API-Key: your_key" http://localhost:3000/api/guilds/123456789
-
- -
-

GET /api/guilds/:guildId/channels

-

Get all channels in a guild.

-
- -
-

GET /api/guilds/:guildId/roles

-

Get all roles in a guild.

-
- -
-

GET /api/guilds/:guildId/emojis

-

Get all emojis in a guild.

-
- -
-

GET /api/guilds/:guildId/bans

-

Get all bans in a guild.

-
- -
-

GET /api/guilds/:guildId/invites

-

Get all invites in a guild.

-
-
- -
-

Channels

- -
-

GET /api/channels/:channelId

-

Get a channel by ID.

-
- -
-

POST /api/guilds/:guildId/channels

-

Create a new channel in a guild.

-
Request Body
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeRequiredDescription
namestringYesChannel name (1-100 chars)
typestringYestext, voice, category, announcement, stage, forum
topicstringNoChannel topic (max 1024 chars)
parentIdstringNoCategory ID
positionnumberNoChannel position
nsfwbooleanNoNSFW flag
rateLimitPerUsernumberNoSlowmode (0-21600 seconds)
bitratenumberNoVoice channel bitrate
userLimitnumberNoVoice channel user limit (0-99)
-
- -
-

PATCH /api/channels/:channelId

-

Edit a channel. All fields are optional.

-
- -
-

DELETE /api/channels/:channelId

-

Delete a channel.

-
- -
-

Threads

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodEndpointDescription
POST/api/channels/:channelId/threadsCreate a thread
GET/api/channels/:channelId/threadsGet all threads
POST/api/channels/:channelId/archiveArchive a thread
DELETE/api/channels/:channelId/archiveUnarchive a thread
POST/api/channels/:channelId/lockLock a thread
DELETE/api/channels/:channelId/lockUnlock a thread
-
- -
-

POST /api/channels/:channelId/clone

-

Clone a channel. Optionally provide a name in the request body.

-
- -
-

GET /api/channels/:channelId/webhooks

-

Get all webhooks for a channel.

-
-
- -
-

Messages

- -
-

GET /api/channels/:channelId/messages

-

Get messages from a channel.

-
Query Parameters
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterTypeDescription
limitnumberNumber of messages (1-100, default: 50)
beforestringGet messages before this ID
afterstringGet messages after this ID
aroundstringGet messages around this ID
-
- -
-

GET /api/channels/:channelId/messages/pinned

-

Get pinned messages in a channel.

-
- -
-

GET /api/channels/:channelId/messages/:messageId

-

Get a specific message by ID.

-
- -
-

POST /api/channels/:channelId/messages

-

Send a message to a channel.

-
Request Body
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeRequiredDescription
contentstringNo*Message content (max 2000 chars)
embedsarrayNo*Array of embed objects
replyTostringNoMessage ID to reply to
ttsbooleanNoText-to-speech
-

*Either content or at least one embed is required.

-
- -
-

PATCH /api/channels/:channelId/messages/:messageId

-

Edit a message.

-
- -
-

DELETE /api/channels/:channelId/messages/:messageId

-

Delete a message.

-
- -
-

POST /api/channels/:channelId/messages/bulk-delete

-

Bulk delete messages. Send messageIds array in body.

-
- -
-

Reactions

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodEndpointDescription
POST/api/channels/:channelId/messages/:messageId/reactions/:emojiAdd reaction
DELETE/api/channels/:channelId/messages/:messageId/reactions/:emojiRemove reaction
DELETE/api/channels/:channelId/messages/:messageId/reactionsRemove all reactions
GET/api/channels/:channelId/messages/:messageId/reactions/:emoji/users - Get reaction users
-
- -
-

Pins

- - - - - - - - - - - - - - - - - - - - -
MethodEndpointDescription
POST/api/channels/:channelId/messages/:messageId/pinPin a message
DELETE/api/channels/:channelId/messages/:messageId/pinUnpin a message
-
- -
-

POST /api/channels/:channelId/messages/:messageId/crosspost

-

Crosspost a message (for announcement channels).

-
-
- -
-

Members

- -
-

GET /api/guilds/:guildId/members

-

List all members in a guild.

-
Query Parameters
- - - - - - - - - - - - - - - -
ParameterTypeDescription
limitnumberMax members to return (default: 1000)
-
- -
-

GET /api/guilds/:guildId/members/search

-

Search members by username/nickname.

-
Query Parameters
- - - - - - - - - - - - - - - - - - - - - - - -
ParameterTypeRequiredDescription
qstringYesSearch query
limitnumberNoMax results (default: 20)
-
- -
-

GET /api/guilds/:guildId/members/:userId

-

Get a specific member.

-
- -
-

Moderation

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodEndpointDescription
POST/api/guilds/:guildId/members/:userId/kickKick member
POST/api/guilds/:guildId/members/:userId/banBan member
DELETE/api/guilds/:guildId/bans/:userIdUnban user
POST/api/guilds/:guildId/members/:userId/timeoutTimeout member
DELETE/api/guilds/:guildId/members/:userId/timeoutRemove timeout
- -
Ban Request Body
- - - - - - - - - - - - - - - - - - - - -
FieldTypeDescription
reasonstringBan reason (max 512 chars)
deleteMessageSecondsnumberSeconds of messages to delete (0-604800)
- -
Timeout Request Body
- - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeRequiredDescription
durationnumberYesDuration in milliseconds
reasonstringNoTimeout reason
-
- -
-

Member Roles

- - - - - - - - - - - - - - - - - - - - -
MethodEndpointDescription
PATCH/api/guilds/:guildId/members/:userId/nicknameSet nickname
PATCH/api/guilds/:guildId/members/:userId/rolesModify roles
- -
Modify Roles Request Body
- - - - - - - - - - - - - - - - - - - - -
FieldTypeDescription
addstring[]Role IDs to add
removestring[]Role IDs to remove
-
-
- -
-

Roles

- -
-

GET /api/guilds/:guildId/roles

-

Get all roles in a guild.

-
- -
-

GET /api/guilds/:guildId/roles/search?name=RoleName

-

Search for a role by name.

-
- -
-

GET /api/guilds/:guildId/roles/:roleId

-

Get a specific role.

-
- -
-

POST /api/guilds/:guildId/roles

-

Create a new role.

-
Request Body
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeDescription
namestringRole name (max 100 chars)
colornumberColor integer (0-16777215)
hoistbooleanDisplay separately
mentionablebooleanAllow mentions
permissionsstringPermission bitfield
-
- -
-

PATCH /api/guilds/:guildId/roles/:roleId

-

Edit a role. Same fields as create, plus position.

-
- -
-

DELETE /api/guilds/:guildId/roles/:roleId

-

Delete a role.

-
- -
-

GET /api/guilds/:guildId/roles/:roleId/members

-

Get all member IDs with a specific role.

-
- -
-

PATCH /api/guilds/:guildId/roles/:roleId/permissions

-

Set role permissions. Send permissions (bitfield string) in body.

-
-
- - -
-

Stickers

-
-

GET /api/guilds/:guildId/stickers

-

List all stickers in a guild.

-
-
-

GET /api/guilds/:guildId/stickers/:stickerId

-

Get a specific sticker.

-
-
-

POST /api/guilds/:guildId/stickers

-

Create a new sticker.

-
-
-

PATCH /api/guilds/:guildId/stickers/:stickerId

-

Edit a sticker.

-
-
-

DELETE /api/guilds/:guildId/stickers/:stickerId

-

Delete a sticker.

-
-
- -
-

Scheduled Events

-
-

GET /api/guilds/:guildId/scheduled-events

-

List all scheduled events.

-
-
-

GET /api/guilds/:guildId/scheduled-events/:eventId

-

Get a specific event.

-
-
-

POST /api/guilds/:guildId/scheduled-events

-

Create a new event.

-
-
-

PATCH /api/guilds/:guildId/scheduled-events/:eventId

-

Edit an event.

-
-
-

DELETE /api/guilds/:guildId/scheduled-events/:eventId

-

Delete an event.

-
-
- -
-

AutoMod

-
-

GET /api/guilds/:guildId/auto-moderation/rules

-

List all auto-moderation rules.

-
-
-

GET /api/guilds/:guildId/auto-moderation/rules/:ruleId

-

Get a specific rule.

-
-
-

POST /api/guilds/:guildId/auto-moderation/rules

-

Create a new rule.

-
-
-

PATCH /api/guilds/:guildId/auto-moderation/rules/:ruleId

-

Edit a rule.

-
-
-

DELETE /api/guilds/:guildId/auto-moderation/rules/:ruleId

-

Delete a rule.

-
-
- -
-

Other Resources

-
-

Stage Instances

-

Endpoints: /api/stage-instances (GET, POST, PATCH, DELETE)

-
-
-

Invites

-

Endpoints: /api/invites/:code (GET, DELETE)

-
-
-

Webhooks

-

Endpoints: /api/webhooks/:webhookId (GET, PATCH, DELETE)

-
-
-

Emojis

-

Endpoints: /api/guilds/:guildId/emojis (GET, POST, PATCH, DELETE)

-
-
- -
-

Application Commands (Slash Commands)

-

Manage Discord Application Commands (Slash Commands) for your bot. Commands can be global - (available in all guilds) or guild-specific.

- -
-

GET /api/commands

-

List all global application commands.

-
curl -H "X-API-Key: your_key" http://localhost:3000/api/commands
-
- -
-

GET /api/commands/:commandId

-

Get a specific global command by ID.

-
- -
-

POST /api/commands -

-

Create a new global application command.

-
Request Body
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeRequiredDescription
namestringYesCommand name (1-32 chars, lowercase)
descriptionstringYesCommand description (1-100 chars)
typenumberNo1=CHAT_INPUT (default), 2=USER, 3=MESSAGE
optionsarrayNoCommand options (max 25)
default_member_permissionsstringNoPermission bitfield
dm_permissionbooleanNoAllow in DMs
nsfwbooleanNoAge-restricted command
-
Example
-
curl -X POST -H "X-API-Key: your_key" -H "Content-Type: application/json" \
-  -d '{"name": "hello", "description": "Says hello"}' \
-  http://localhost:3000/api/commands
-
- -
-

PATCH /api/commands/:commandId

-

Edit a global application command. All fields optional.

-
- -
-

DELETE /api/commands/:commandId

-

Delete a global application command.

-
- -

Guild-Specific Commands

-

Guild commands are only available in the specified guild. They update instantly (unlike global - commands which can take up to an hour).

- -
-

GET /api/guilds/:guildId/commands

-

List all commands for a specific guild.

-
- -
-

GET /api/guilds/:guildId/commands/:commandId

-

Get a specific guild command.

-
- -
-

POST /api/guilds/:guildId/commands

-

Create a guild-specific command. Same request body as global commands.

-
- -
-

PATCH /api/guilds/:guildId/commands/:commandId

-

Edit a guild-specific command.

-
- -
-

DELETE /api/guilds/:guildId/commands/:commandId

-

Delete a guild-specific command.

-
-
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/getting-started.html b/docs/getting-started.html deleted file mode 100644 index 98773f4..0000000 --- a/docs/getting-started.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - - Getting Started - Holo Bridge - - - - - -
- -
- -
-
-

Getting Started

-

This guide will walk you through installing, configuring, and running Holo Bridge.

- -

Prerequisites

-
    -
  • Node.js 18 or higher
  • -
  • npm or yarn package manager
  • -
  • A Discord bot token
  • -
- -

Installation

-

1. Clone the Repository

- git clone https://github.com/coder-soft/holobridge.git - cd holobridge - -

2. Install Dependencies

-
npm install
- -

Configuration

-

Environment Variables

-

Copy .env.example to .env and fill in your values:

-
# Discord Bot Token (required)
-# Get this from https://discord.com/developers/applications
-DISCORD_TOKEN=your_discord_bot_token_here
-
-# API Configuration
-PORT=3000
-API_KEY=your_secure_api_key_here
-
-# Optional: Enable debug logging
-DEBUG=false
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VariableRequiredDescription
DISCORD_TOKENYesYour Discord bot token
API_KEYYesAPI key for authenticating requests
PORTNoServer port (default: 3000)
DEBUGNoEnable debug logging (default: false)
- -

Running the Server

-

Development Mode

-
npm run dev
- -

Production Mode

-
npm run build
-npm start
- -

Discord Bot Setup

-

Follow these steps to create a Discord bot and invite it to your server:

- -
    -
  1. Go to the Discord Developer - Portal
  2. -
  3. Click "New Application" and give it a name
  4. -
  5. Go to the "Bot" section in the left sidebar
  6. -
  7. Click "Add Bot" and confirm
  8. -
  9. Enable the following Privileged Gateway Intents: -
      -
    • Presence Intent
    • -
    • Server Members Intent
    • -
    • Message Content Intent
    • -
    -
  10. -
  11. Click "Reset Token" to get your bot token and copy it to your .env file
  12. -
- -

Inviting the Bot

-
    -
  1. Go to the "OAuth2" → "URL Generator" section
  2. -
  3. Select the following scopes: -
      -
    • bot
    • -
    • applications.commands
    • -
    -
  4. -
  5. Select the permissions your bot needs (Administrator for full access)
  6. -
  7. Copy the generated URL and open it in your browser
  8. -
  9. Select a server and authorize the bot
  10. -
- -
- âš ī¸ Important: Keep your bot token and API key secret. Never commit them to version - control. -
- -

Testing the API

-

Once the server is running, you can test the API with curl:

-
# List all guilds
-curl -H "X-API-Key: your_api_key" http://localhost:3000/api/guilds
- -

Next Steps

- -
-
- - - - - - - \ No newline at end of file diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 947d9cc..0000000 --- a/docs/index.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - Holo Bridge - Documentation - - - - - -
- -
- -
-
-
-

Holo Bridge

-

A type-safe TypeScript bridge between websites and Discord bots. Provides a REST API and WebSocket - interface for full Discord bot capabilities.

- -
- -
-
-

🌐 REST API

-

Full REST API for all Discord operations including guilds, channels, messages, members, and - roles.

-
-
-

⚡ WebSocket Events

-

Real-time event streaming via Socket.IO. Subscribe to guilds and receive Discord events - instantly.

-
-
-

🔒 Type-Safe

-

Built with TypeScript and Zod validation. Every request and response is fully typed.

-
-
-

đŸ“Ļ Full Coverage

-

Guilds, Channels, Roles, Members, Bans, Timeouts, Messages, Reactions, Pins, and more.

-
-
-

🔌 Plugin System

-

Extend functionality with plugins. Add routes, listen to events, and communicate between plugins. -

-
-
- -

Quick Links

- - -

Features Overview

-

Discord Operations

-
    -
  • Guilds - List guilds, get details, channels, roles, emojis, bans, invites
  • -
  • Channels - Create, edit, delete channels and threads
  • -
  • Messages - Send, edit, delete, bulk delete, reactions, pins
  • -
  • Members - List, search, kick, ban, timeout, role management
  • -
  • Roles - Create, edit, delete, permissions management
  • -
- -

Real-Time Events

-
    -
  • Message Events - messageCreate, messageUpdate, messageDelete
  • -
  • Member Events - guildMemberAdd, guildMemberRemove, guildMemberUpdate
  • -
  • Channel Events - channelCreate, channelUpdate, channelDelete
  • -
  • Role Events - roleCreate, roleUpdate, roleDelete
  • -
  • Voice Events - voiceStateUpdate
  • -
  • Guild Events - guildBanAdd, guildBanRemove
  • -
-
-
- - - - - - - \ No newline at end of file diff --git a/docs/plugins.html b/docs/plugins.html deleted file mode 100644 index f4daa59..0000000 --- a/docs/plugins.html +++ /dev/null @@ -1,667 +0,0 @@ - - - - - - - Plugin System - Holo Bridge - - - - - -
- -
- -
-
-

Plugin System

-

Extend HoloBridge with custom functionality using the powerful plugin system. Plugins can add REST API - endpoints, listen to Discord events, and communicate with other plugins via a typed event bus.

- -
- â„šī¸ Note: Plugins are JavaScript/ESM modules placed in the plugins/ - directory. They are automatically loaded on startup. -
- -

Quick Start

-

Create a file in the plugins/ directory with a .js or .mjs - extension:

- -

plugins/my-plugin.js

-
export default {
-    metadata: {
-        name: 'my-plugin',
-        version: '1.0.0',
-        author: 'Your Name',
-        description: 'A sample HoloBridge plugin'
-    },
-
-    // Optional: Register REST endpoints
-    routes: (router, ctx) => {
-        router.get('/status', (req, res) => {
-            res.json({ status: 'ok', plugin: 'my-plugin' });
-        });
-    },
-
-    // Optional: Subscribe to events
-    events: (on, ctx) => [
-        on.onDiscord('messageCreate', (msg) => {
-            ctx.logger.info('New message:', msg.content);
-        }),
-    ],
-
-    // Optional: Called when plugin loads
-    onLoad: (ctx) => {
-        ctx.logger.info('Plugin loaded!');
-    },
-
-    // Optional: Called when plugin unloads
-    onUnload: () => {
-        console.log('Plugin unloaded!');
-    }
-};
- -

Plugin Structure

- -

Plugin Metadata

-

Every plugin must export an object with a metadata property:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyTypeRequiredDescription
namestringYesUnique plugin identifier (used in routes and logs)
versionstringYesSemantic version (e.g., "1.0.0")
authorstringNoPlugin author name
descriptionstringNoShort description of the plugin
- -

Lifecycle Hooks

- - - - - - - - - - - - - - - - - - - - -
HookWhen CalledUse Case
onLoad(ctx)After plugin is loadedInitialize state, setup connections
onUnload()Before plugin is unloadedCleanup resources, close connections
- -

Plugin Context

-

The ctx object provides access to core HoloBridge services:

-
interface PluginContext {
-    client: Client;              // Discord.js client
-    io: SocketIOServer;          // Socket.IO server
-    config: Config;              // Application configuration
-    app: Application;            // Express application
-    eventBus: PluginEventBus;    // Event bus for inter-plugin communication
-    logger: PluginLogger;        // Logging utility
-    log: (message: string) => void;  // Legacy logger
-    getPlugin: (name: string) => PluginMetadata | undefined;
-    listPlugins: () => string[];
-}
- -

Logger

-

Use the built-in logger for consistent output:

-
ctx.logger.info('Information message');
-ctx.logger.warn('Warning message');
-ctx.logger.error('Error message');
-ctx.logger.debug('Debug message (only in debug mode)');
- -
- -

REST API Routes

-

Plugins can register REST API endpoints that are automatically mounted at - /api/plugins/{plugin-name}/: -

- -
routes: (router, ctx) => {
-    // GET /api/plugins/my-plugin/status
-    router.get('/status', (req, res) => {
-        res.json({ status: 'ok' });
-    });
-
-    // POST /api/plugins/my-plugin/action
-    router.post('/action', (req, res) => {
-        const { userId, action } = req.body;
-        // Handle the action
-        res.json({ success: true });
-    });
-
-    // Routes support all HTTP methods
-    router.put('/update', handler);
-    router.patch('/modify', handler);
-    router.delete('/remove', handler);
-
-    // Add middleware
-    router.use(myMiddleware);
-}
- -
- â„šī¸ Automatic Error Handling: Plugin routes are automatically wrapped with error - handling. Errors are caught and returned as JSON responses. -
- -
- -

Event Bus

-

HoloBridge provides a typed event bus for inter-plugin communication with three event categories:

- -
-
-

🎮 Discord Events

-

Events forwarded from the Discord gateway (prefixed with discord:)

-
-
-

🔌 Plugin Events

-

Lifecycle events like plugin load/unload (prefixed with plugin:)

-
-
-

✨ Custom Events

-

Events emitted by plugins for inter-plugin communication (prefixed with custom:)

-
-
- -

Subscribing to Events

-

Use the events hook to subscribe to events. Return an array of subscriptions for automatic - cleanup:

- -
events: (on, ctx) => [
-    // Subscribe to Discord events
-    on.onDiscord('messageCreate', (message) => {
-        ctx.logger.info('New message:', message.content);
-    }),
-
-    on.onDiscord('guildMemberAdd', (member) => {
-        ctx.logger.info('New member joined:', member.user.username);
-    }),
-
-    // Subscribe to custom events from other plugins
-    on.onCustom('moderation:user-warned', (data) => {
-        ctx.logger.info(`User ${data.userId} was warned`);
-    }),
-
-    // Subscribe to plugin lifecycle events
-    on.onPluginLoaded((data) => {
-        ctx.logger.info(`Plugin loaded: ${data.name} v${data.version}`);
-    }),
-
-    on.onPluginUnloaded((data) => {
-        ctx.logger.info(`Plugin unloaded: ${data.name}`);
-    }),
-]
- -

Emitting Custom Events

-

Plugins can emit custom events for other plugins to consume:

-
events: (on, ctx) => {
-    // Emit a custom event
-    on.emit('my-plugin:action-performed', {
-        userId: '123456789',
-        action: 'ban',
-        reason: 'Spam'
-    });
-
-    return [];
-}
- -

Available Discord Events

-

All standard Discord.js events are available. Common events include:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDataDescription
messageCreateSerialized MessageNew message sent
messageUpdateSerialized MessageMessage edited
messageDeleteMessage infoMessage deleted
guildMemberAddSerialized MemberMember joined
guildMemberRemoveSerialized MemberMember left/kicked
guildMemberUpdateSerialized MemberMember updated
channelCreateSerialized ChannelChannel created
channelUpdateSerialized ChannelChannel updated
channelDeleteChannel infoChannel deleted
roleCreateSerialized RoleRole created
roleUpdateSerialized RoleRole updated
roleDeleteRole infoRole deleted
voiceStateUpdateVoice state dataVoice state changed
guildBanAddBan infoMember banned
guildBanRemoveBan infoMember unbanned
threadCreateSerialized ThreadThread created
threadUpdateSerialized ThreadThread updated
threadDeleteThread infoThread deleted
- -

Plugin Lifecycle Events

- - - - - - - - - - - - - - - - - - - - - - - - - -
EventDataDescription
plugin:loaded{ name, version }A plugin was loaded
plugin:unloaded{ name }A plugin was unloaded
plugin:error{ name, error }A plugin encountered an error
- -
- -

Direct Event Bus Access

-

For advanced use cases, access the event bus directly via ctx.eventBus:

- -
onLoad: (ctx) => {
-    const { eventBus } = ctx;
-
-    // Subscribe to any event
-    const subscription = eventBus.subscribe('custom:my-event', (data) => {
-        console.log('Received:', data);
-    });
-
-    // Subscribe once
-    eventBus.subscribeOnce('discord:ready', () => {
-        console.log('Bot is ready!');
-    });
-
-    // Emit Discord events (typically done by core)
-    eventBus.emitDiscord('messageCreate', messageData);
-
-    // Emit custom events
-    eventBus.emitCustom('my-plugin:action', { key: 'value' });
-
-    // Emit plugin lifecycle events
-    eventBus.emitPlugin('plugin:error', { name: 'my-plugin', error: new Error('Oops') });
-
-    // Get listener counts (for debugging)
-    console.log(eventBus.getListenerCounts());
-}
- -

Event Bus Methods

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
onDiscord(event, handler)Subscribe to a Discord event
onCustom(event, handler)Subscribe to a custom event
onPlugin(event, handler)Subscribe to a plugin lifecycle event
emitDiscord(event, data)Emit a Discord event
emitCustom(event, data)Emit a custom event
emitPlugin(event, data)Emit a plugin lifecycle event
subscribe(event, handler)Subscribe to any event (returns subscription object)
subscribeOnce(event, handler)Subscribe once and automatically unsubscribe
unsubscribeAll(subscriptions)Unsubscribe from multiple events at once
- -
- -

Inter-Plugin Communication

-

Plugins can discover and interact with other loaded plugins:

- -
onLoad: (ctx) => {
-    // List all loaded plugins
-    const plugins = ctx.listPlugins();
-    ctx.logger.info('Loaded plugins:', plugins);
-
-    // Get another plugin's metadata
-    const modPlugin = ctx.getPlugin('moderation');
-    if (modPlugin) {
-        ctx.logger.info(`Moderation plugin v${modPlugin.version} is loaded`);
-    }
-}
- -

Example: Plugin Communication Pattern

-
// Plugin A: moderation.js
-export default {
-    metadata: { name: 'moderation', version: '1.0.0' },
-    
-    routes: (router, ctx) => {
-        router.post('/warn', (req, res) => {
-            const { userId, reason } = req.body;
-            
-            // Emit event for other plugins
-            ctx.eventBus.emitCustom('moderation:user-warned', {
-                userId,
-                reason,
-                timestamp: Date.now()
-            });
-            
-            res.json({ success: true });
-        });
-    }
-};
-
-// Plugin B: logging.js
-export default {
-    metadata: { name: 'logging', version: '1.0.0' },
-    
-    events: (on, ctx) => [
-        // Listen for moderation events
-        on.onCustom('moderation:user-warned', (data) => {
-            ctx.logger.info(`[AUDIT] User ${data.userId} warned: ${data.reason}`);
-            // Log to database, send to webhook, etc.
-        }),
-    ]
-};
- -
- -

Complete Plugin Example

-

Here's a complete example of a plugin that demonstrates all features:

- -
// plugins/welcome.js
-export default {
-    metadata: {
-        name: 'welcome',
-        version: '1.0.0',
-        author: 'HoloBridge',
-        description: 'Welcome new members with customizable messages'
-    },
-
-    // Configuration stored in memory (use a database in production)
-    _config: {
-        enabled: true,
-        channelId: null,
-        message: 'Welcome to the server, {user}!'
-    },
-
-    routes: (router, ctx) => {
-        // GET /api/plugins/welcome/config
-        router.get('/config', (req, res) => {
-            res.json({
-                success: true,
-                data: this._config
-            });
-        });
-
-        // PATCH /api/plugins/welcome/config
-        router.patch('/config', (req, res) => {
-            const { enabled, channelId, message } = req.body;
-            
-            if (enabled !== undefined) this._config.enabled = enabled;
-            if (channelId !== undefined) this._config.channelId = channelId;
-            if (message !== undefined) this._config.message = message;
-
-            res.json({ success: true, data: this._config });
-        });
-    },
-
-    events: (on, ctx) => [
-        on.onDiscord('guildMemberAdd', async (member) => {
-            if (!this._config.enabled || !this._config.channelId) return;
-
-            try {
-                const channel = await ctx.client.channels.fetch(this._config.channelId);
-                if (channel?.isTextBased()) {
-                    const message = this._config.message
-                        .replace('{user}', `<@${member.user.id}>`)
-                        .replace('{username}', member.user.username)
-                        .replace('{server}', member.guild.name);
-                    
-                    await channel.send(message);
-                    ctx.logger.info(`Welcomed ${member.user.username}`);
-                }
-            } catch (error) {
-                ctx.logger.error('Failed to send welcome message:', error);
-            }
-        }),
-    ],
-
-    onLoad: (ctx) => {
-        ctx.logger.info('Welcome plugin loaded!');
-        ctx.logger.info(`Routes available at /api/plugins/welcome/`);
-    },
-
-    onUnload: () => {
-        console.log('[welcome] Plugin unloaded');
-    }
-};
- -
- -

Best Practices

- -
-

✅ Do:

-
    -
  • Return event subscriptions from the events hook for automatic cleanup
  • -
  • Use ctx.logger for consistent, prefixed logging
  • -
  • Handle errors gracefully in event handlers
  • -
  • Use semantic versioning for your plugin version
  • -
  • Namespace custom events with your plugin name (e.g., my-plugin:event-name)
  • -
-
- -
-

âš ī¸ Avoid:

-
    -
  • Blocking the event loop with synchronous operations
  • -
  • Storing sensitive data in plugin state without encryption
  • -
  • Using the deprecated onEvent hook (use events instead)
  • -
  • Creating memory leaks by not cleaning up resources in onUnload
  • -
-
- -

Next Steps

- -
-
- - - - - - - - \ No newline at end of file diff --git a/docs/security.html b/docs/security.html deleted file mode 100644 index 6ce8909..0000000 --- a/docs/security.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - Security & API Scopes - HoloBridge - - - - - -
- -
- -
-
-

Security & API Scopes

-

HoloBridge provides granular access control through API scopes, allowing you to create API keys with - limited permissions.

- -

API Key Configuration

- -

Single Key (Simple)

-

For basic setups, use a single API key in .env:

-
API_KEY=your_secure_api_key
-

This key has admin scope (full access).

- -

Multiple Keys with Scopes

-

For production, define multiple keys with specific permissions using the API_KEYS - environment variable:

-
API_KEYS=[
-  {"id":"dashboard","name":"Web Dashboard","key":"dash_xxx","scopes":["read:guilds","read:members"]},
-  {"id":"bot","name":"Chat Bot","key":"bot_xxx","scopes":["read:messages","write:messages"]},
-  {"id":"admin","name":"Admin Panel","key":"admin_xxx","scopes":["admin"]}
-]
- -

Available Scopes

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ScopePermissions
read:guildsList guilds, get guild details
read:channelsList channels, get channel info
read:membersList members, get member details
read:messagesRead message history
write:messagesSend, edit, delete messages
write:membersKick, ban, timeout members
write:channelsCreate, edit, delete channels
write:rolesCreate, edit, delete roles
eventsSubscribe to WebSocket events
adminFull access (bypasses all checks)
- -

Rate Limiting

-

HoloBridge includes built-in rate limiting to protect against abuse.

- -

Configuration

-
RATE_LIMIT_ENABLED=true
-RATE_LIMIT_WINDOW_MS=60000  # 1 minute window
-RATE_LIMIT_MAX=100          # 100 requests per window
- -

Response Headers

-

All API responses include rate limit headers:

- - - - - - - - - - - - - - - - - - - - - -
HeaderDescription
X-RateLimit-LimitMaximum requests per window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when limit resets
- -

Rate Limited Response

-

When the limit is exceeded, you'll receive:

-
{
-  "success": false,
-  "error": "Too many requests",
-  "code": "RATE_LIMITED",
-  "retryAfter": 45
-}
- -
- âš ī¸ Best Practices: -
    -
  • Use scoped keys — give each integration only the permissions it needs
  • -
  • Rotate keys regularly — update API keys periodically
  • -
  • Keep admin keys secure — only use admin scope for trusted applications
  • -
  • Monitor usage — watch rate limit headers to identify issues
  • -
-
- -

Next Steps

- -
-
- - - - - - - \ No newline at end of file diff --git a/docs/styles.css b/docs/styles.css deleted file mode 100644 index ae2318b..0000000 --- a/docs/styles.css +++ /dev/null @@ -1,498 +0,0 @@ -/* Holo Bridge - Documentation Styles */ -/* Simple design with dark/light theme support */ - -:root { - /* Light theme (default) */ - --bg-primary: #ffffff; - --bg-secondary: #f5f5f5; - --bg-tertiary: #e8e8e8; - --text-primary: #1a1a1a; - --text-secondary: #555555; - --text-muted: #888888; - --border-color: #dddddd; - --accent: #5865f2; - --accent-hover: #4752c4; - --success: #3ba55c; - --warning: #faa61a; - --error: #ed4245; - --code-bg: #f0f0f0; - --table-stripe: #fafafa; -} - -[data-theme="dark"] { - --bg-primary: #1a1a1a; - --bg-secondary: #252525; - --bg-tertiary: #333333; - --text-primary: #ffffff; - --text-secondary: #b0b0b0; - --text-muted: #777777; - --border-color: #444444; - --accent: #5865f2; - --accent-hover: #7289da; - --success: #3ba55c; - --warning: #faa61a; - --error: #ed4245; - --code-bg: #2d2d2d; - --table-stripe: #222222; -} - -@media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) { - --bg-primary: #1a1a1a; - --bg-secondary: #252525; - --bg-tertiary: #333333; - --text-primary: #ffffff; - --text-secondary: #b0b0b0; - --text-muted: #777777; - --border-color: #444444; - --accent: #5865f2; - --accent-hover: #7289da; - --success: #3ba55c; - --warning: #faa61a; - --error: #ed4245; - --code-bg: #2d2d2d; - --table-stripe: #222222; - } -} - -/* Reset and base styles */ -*, *::before, *::after { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -html { - scroll-behavior: smooth; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; - background-color: var(--bg-primary); - color: var(--text-primary); - line-height: 1.6; - min-height: 100vh; -} - -/* Layout */ -.container { - max-width: 1200px; - margin: 0 auto; - padding: 0 24px; -} - -/* Header */ -header { - background-color: var(--bg-secondary); - border-bottom: 1px solid var(--border-color); - padding: 16px 0; - position: sticky; - top: 0; - z-index: 100; -} - -.header-content { - display: flex; - justify-content: space-between; - align-items: center; -} - -.logo { - font-size: 1.5rem; - font-weight: 700; - color: var(--accent); - text-decoration: none; -} - -nav { - display: flex; - gap: 24px; - align-items: center; -} - -nav a { - color: var(--text-secondary); - text-decoration: none; - font-weight: 500; - transition: color 0.2s ease; -} - -nav a:hover, -nav a.active { - color: var(--accent); -} - -/* Theme toggle */ -.theme-toggle { - background-color: var(--bg-tertiary); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 8px 16px; - cursor: pointer; - color: var(--text-primary); - font-size: 0.875rem; - font-weight: 500; - transition: all 0.2s ease; -} - -.theme-toggle:hover { - background-color: var(--accent); - color: #ffffff; - border-color: var(--accent); -} - -/* Main content */ -main { - padding: 48px 0; -} - -/* Hero section */ -.hero { - text-align: center; - padding: 64px 0; -} - -.hero h1 { - font-size: 3rem; - font-weight: 800; - margin-bottom: 16px; - color: var(--text-primary); -} - -.hero p { - font-size: 1.25rem; - color: var(--text-secondary); - max-width: 600px; - margin: 0 auto 32px; -} - -/* Buttons - squircle style */ -.btn { - display: inline-block; - padding: 12px 24px; - border-radius: 12px; - text-decoration: none; - font-weight: 600; - font-size: 1rem; - cursor: pointer; - transition: all 0.2s ease; - border: none; -} - -.btn-primary { - background-color: var(--accent); - color: #ffffff; -} - -.btn-primary:hover { - background-color: var(--accent-hover); -} - -.btn-secondary { - background-color: var(--bg-tertiary); - color: var(--text-primary); - border: 1px solid var(--border-color); -} - -.btn-secondary:hover { - background-color: var(--bg-secondary); - border-color: var(--accent); -} - -.btn-group { - display: flex; - gap: 16px; - justify-content: center; - flex-wrap: wrap; -} - -/* Features grid */ -.features { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 24px; - padding: 48px 0; -} - -.feature-card { - background-color: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 24px; -} - -.feature-card h3 { - font-size: 1.25rem; - margin-bottom: 12px; - color: var(--text-primary); -} - -.feature-card p { - color: var(--text-secondary); -} - -/* Section headings */ -h1, h2, h3, h4, h5, h6 { - color: var(--text-primary); - line-height: 1.3; -} - -h1 { font-size: 2.5rem; margin-bottom: 24px; } -h2 { font-size: 2rem; margin: 48px 0 24px; } -h3 { font-size: 1.5rem; margin: 32px 0 16px; } -h4 { font-size: 1.25rem; margin: 24px 0 12px; } - -/* Prose content */ -p { - margin-bottom: 16px; - color: var(--text-secondary); -} - -a { - color: var(--accent); - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -ul, ol { - margin: 16px 0; - padding-left: 24px; - color: var(--text-secondary); -} - -li { - margin-bottom: 8px; -} - -/* Code blocks */ -code { - font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; - background-color: var(--code-bg); - padding: 2px 6px; - border-radius: 4px; - font-size: 0.9em; -} - -pre { - background-color: var(--code-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 16px; - overflow-x: auto; - margin: 16px 0; -} - -pre code { - background: none; - padding: 0; -} - -/* Tables */ -table { - width: 100%; - border-collapse: collapse; - margin: 24px 0; - font-size: 0.95rem; -} - -th, td { - text-align: left; - padding: 12px 16px; - border-bottom: 1px solid var(--border-color); -} - -th { - background-color: var(--bg-secondary); - font-weight: 600; - color: var(--text-primary); -} - -tbody tr:nth-child(even) { - background-color: var(--table-stripe); -} - -/* Method badges */ -.method { - display: inline-block; - padding: 4px 8px; - border-radius: 6px; - font-size: 0.75rem; - font-weight: 700; - font-family: monospace; - text-transform: uppercase; -} - -.method-get { background-color: var(--success); color: #fff; } -.method-post { background-color: var(--accent); color: #fff; } -.method-patch { background-color: var(--warning); color: #000; } -.method-delete { background-color: var(--error); color: #fff; } - -/* Endpoint path */ -.endpoint { - font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; - font-size: 0.9rem; - color: var(--text-primary); -} - -/* API section */ -.api-section { - background-color: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 24px; - margin: 24px 0; -} - -.api-section h4 { - margin-top: 0; -} - -/* Cards grid */ -.cards { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 16px; - margin: 24px 0; -} - -.card { - display: block; - background-color: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 20px; - text-decoration: none; - transition: all 0.2s ease; -} - -.card:hover { - border-color: var(--accent); - text-decoration: none; -} - -.card h4 { - color: var(--text-primary); - margin: 0 0 8px; -} - -.card p { - color: var(--text-secondary); - margin: 0; - font-size: 0.9rem; -} - -/* Alert boxes */ -.alert { - padding: 16px; - border-radius: 12px; - margin: 16px 0; - border: 1px solid var(--border-color); -} - -.alert-info { - background-color: var(--bg-secondary); - border-left: 4px solid var(--accent); -} - -.alert-warning { - background-color: var(--bg-secondary); - border-left: 4px solid var(--warning); -} - -.alert-success { - background-color: var(--bg-secondary); - border-left: 4px solid var(--success); -} - -/* Footer */ -footer { - background-color: var(--bg-secondary); - border-top: 1px solid var(--border-color); - padding: 24px 0; - margin-top: 64px; - text-align: center; -} - -footer p { - color: var(--text-muted); - margin: 0; -} - -/* Sidebar layout for docs */ -.docs-layout { - display: grid; - grid-template-columns: 250px 1fr; - gap: 48px; -} - -.sidebar { - position: sticky; - top: 80px; - height: fit-content; -} - -.sidebar ul { - list-style: none; - padding: 0; -} - -.sidebar li { - margin: 0; -} - -.sidebar a { - display: block; - padding: 8px 12px; - color: var(--text-secondary); - border-radius: 8px; - transition: all 0.2s ease; -} - -.sidebar a:hover, -.sidebar a.active { - background-color: var(--bg-tertiary); - color: var(--accent); - text-decoration: none; -} - -.sidebar h4 { - color: var(--text-muted); - font-size: 0.75rem; - text-transform: uppercase; - letter-spacing: 0.05em; - margin: 24px 0 8px; - padding: 0 12px; -} - -.sidebar h4:first-child { - margin-top: 0; -} - -/* Responsive */ -@media (max-width: 768px) { - .docs-layout { - grid-template-columns: 1fr; - } - - .sidebar { - position: static; - border-bottom: 1px solid var(--border-color); - padding-bottom: 24px; - margin-bottom: 24px; - } - - nav { - gap: 12px; - } - - .hero h1 { - font-size: 2rem; - } - - h1 { font-size: 2rem; } - h2 { font-size: 1.5rem; } - h3 { font-size: 1.25rem; } -} diff --git a/docs/websocket.html b/docs/websocket.html deleted file mode 100644 index 05b0a16..0000000 --- a/docs/websocket.html +++ /dev/null @@ -1,946 +0,0 @@ - - - - - - - WebSocket Events - Holo Bridge - - - - - -
- -
- -
-
- - -
-

WebSocket Events

-

Holo Bridge provides real-time event streaming via Socket.IO. Subscribe to guilds and receive - Discord events as they happen. 45+ event types are supported.

- -
-

Connection

-

Connect to the WebSocket server using Socket.IO:

-
import { io } from 'socket.io-client';
-
-const socket = io('http://localhost:3000', {
-    auth: {
-        apiKey: 'your_api_key'
-    }
-});
-
-socket.on('connect', () => {
-    console.log('Connected to Holo Bridge');
-});
-
-socket.on('connect_error', (error) => {
-    console.error('Connection error:', error.message);
-});
-
-socket.on('disconnect', (reason) => {
-    console.log('Disconnected:', reason);
-});
-
- -
-

Authentication

-

Authentication is required and must be provided in the auth option when connecting: -

-
const socket = io('http://localhost:3000', {
-    auth: {
-        apiKey: 'your_secure_api_key'
-    }
-});
-
- Note: If authentication fails, the connection will be rejected with an error. -
-
- -
-

Subscribing to Guilds

-

After connecting, subscribe to specific guilds to receive their events:

-
// Subscribe to guilds
-socket.emit('subscribe', {
-    guildIds: ['123456789012345678', '987654321098765432']
-});
-
-// Listen for subscription confirmation
-socket.on('subscribed', (data) => {
-    console.log('Subscribed to guilds:', data.guildIds);
-});
-
-// Unsubscribe from guilds
-socket.emit('unsubscribe', {
-    guildIds: ['123456789012345678']
-});
-
-socket.on('unsubscribed', (data) => {
-    console.log('Unsubscribed from guilds:', data.guildIds);
-});
- -

Listening for Events

-

All Discord events are emitted through the discord event:

-
socket.on('discord', (payload) => {
-    console.log('Event:', payload.event);
-    console.log('Guild:', payload.guildId);
-    console.log('Data:', payload.data);
-});
-
- -
-

Message Events

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
messageCreateNew message sentSerializedMessage
messageUpdateMessage edited{ old, new }
messageDeleteMessage deleted{ id, channelId, guildId }
messageDeleteBulkMultiple messages deleted{ ids, channelId, guildId }
-
- -
-

Reaction Events

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
messageReactionAddReaction added to message{ messageId, channelId, guildId, userId, reaction }
messageReactionRemoveReaction removed from message{ messageId, channelId, guildId, userId, emoji }
messageReactionRemoveAllAll reactions removed from message{ messageId, channelId, guildId }
messageReactionRemoveEmojiAll reactions of specific emoji removed{ messageId, channelId, guildId, emoji }
-
- -
-

Poll Events

- - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
messagePollVoteAddUser voted on a poll{ messageId, channelId, guildId, userId, answerId }
messagePollVoteRemoveUser removed poll vote{ messageId, channelId, guildId, userId, answerId }
-
- -
-

Member Events

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
guildMemberAddMember joinedSerializedMember
guildMemberRemoveMember left/kicked{ user, guildId }
guildMemberUpdateMember updated{ old, new }
presenceUpdatePresence changed{ old, new }
userUpdateUser globally updated{ old, new }
-
- -
-

Channel Events

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
channelCreateChannel createdSerializedChannel
channelUpdateChannel modified{ old, new }
channelDeleteChannel deletedSerializedChannel
channelPinsUpdatePins updated in channel{ channelId, guildId, lastPinAt }
webhookUpdateWebhooks changed{ channelId, guildId }
-
- -
-

Thread Events

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
threadCreateThread created{ thread, newlyCreated }
threadUpdateThread modified{ old, new }
threadDeleteThread deleted{ id, guildId, parentId, name, type }
threadMembersUpdateThread members changed{ threadId, guildId, addedMembers, removedMemberIds, memberCount }
-
- -
-

Role Events

- - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
roleCreateRole createdSerializedRole
roleUpdateRole modified{ old, new }
roleDeleteRole deleted{ id, guildId }
-
- -
-

Guild Events

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
guildCreateBot joined guildSerializedGuild
guildUpdateGuild modified{ old, new }
guildDeleteBot left/removed{ id, name, unavailable }
guildBanAddUser banned{ guildId, user }
guildBanRemoveUser unbanned{ guildId, user }
guildIntegrationsUpdateIntegrations updated{ guildId }
guildAuditLogEntryCreateAudit log entry createdSerializedAuditLogEntry
-
- -
-

Emoji Events

- - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
emojiCreateCustom emoji addedSerializedEmoji
emojiUpdateEmoji modified{ old, new }
emojiDeleteEmoji removedSerializedEmoji
-
- -
-

Sticker Events

- - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
stickerCreateCustom sticker addedSerializedSticker
stickerUpdateSticker modified{ old, new }
stickerDeleteSticker removedSerializedSticker
- -

Sticker Object

-
{
-    "id": "123456789",
-    "name": "cool_sticker",
-    "description": "A cool sticker",
-    "packId": null,
-    "type": 2,
-    "format": 1,
-    "formatName": "png",
-    "available": true,
-    "guildId": "111222333",
-    "user": { ... },
-    "sortValue": null,
-    "tags": "cool",
-    "url": "https://cdn.discordapp.com/stickers/...",
-    "createdAt": "2025-01-01T00:00:00.000Z"
-}
-
- -
-

Voice Events

- - - - - - - - - - - - - - - -
EventDescriptionData
voiceStateUpdateVoice state changed{ old, new }
-
- -
-

Stage Instance Events

- - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
stageInstanceCreateStage went liveSerializedStageInstance
stageInstanceUpdateStage settings changed{ old, new }
stageInstanceDeleteStage endedSerializedStageInstance
- -

Stage Instance Object

-
{
-    "id": "123456789",
-    "guildId": "111222333",
-    "channelId": "444555666",
-    "topic": "Community Q&A Session",
-    "privacyLevel": 2,
-    "privacyLevelName": "guild_only",
-    "guildScheduledEventId": null,
-    "createdAt": "2025-01-01T12:00:00.000Z"
-}
-
- -
-

Scheduled Event Events

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
guildScheduledEventCreateScheduled event createdSerializedScheduledEvent
guildScheduledEventUpdateScheduled event updated{ old, new }
guildScheduledEventDeleteScheduled event deletedSerializedScheduledEvent
guildScheduledEventUserAddUser RSVPed to event{ guildScheduledEventId, userId, guildId }
guildScheduledEventUserRemoveUser cancelled RSVP{ guildScheduledEventId, userId, guildId }
- -

Scheduled Event Object

-
{
-    "id": "123456789",
-    "guildId": "111222333",
-    "channelId": "444555666",
-    "creatorId": "777888999",
-    "creator": { ... },
-    "name": "Community Game Night",
-    "description": "Join us for fun games!",
-    "scheduledStartTime": "2025-01-15T20:00:00.000Z",
-    "scheduledEndTime": "2025-01-15T23:00:00.000Z",
-    "privacyLevel": 2,
-    "status": 1,
-    "statusName": "scheduled",
-    "entityType": 2,
-    "entityTypeName": "voice",
-    "entityId": null,
-    "entityMetadata": null,
-    "userCount": 25,
-    "image": "abc123",
-    "imageUrl": "https://cdn.discordapp.com/...",
-    "createdAt": "2025-01-01T00:00:00.000Z"
-}
-
- -
-

AutoMod Events

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
autoModerationRuleCreateAutoMod rule createdSerializedAutoModRule
autoModerationRuleUpdateAutoMod rule updated{ old, new }
autoModerationRuleDeleteAutoMod rule deletedSerializedAutoModRule
autoModerationActionExecutionAutoMod action executedSerializedAutoModAction
- -

AutoMod Rule Object

-
{
-    "id": "123456789",
-    "guildId": "111222333",
-    "name": "Block Bad Words",
-    "creatorId": "444555666",
-    "eventType": 1,
-    "triggerType": 1,
-    "triggerTypeName": "keyword",
-    "triggerMetadata": {
-        "keywordFilter": ["badword1", "badword2"],
-        "regexPatterns": [],
-        "presets": [],
-        "allowList": [],
-        "mentionTotalLimit": null,
-        "mentionRaidProtectionEnabled": false
-    },
-    "actions": [
-        {
-            "type": 1,
-            "typeName": "block_message",
-            "metadata": {
-                "customMessage": "This message was blocked by AutoMod."
-            }
-        }
-    ],
-    "enabled": true,
-    "exemptRoles": ["777888999"],
-    "exemptChannels": []
-}
- -

AutoMod Action Execution Object

-
{
-    "ruleId": "123456789",
-    "ruleTriggerType": 1,
-    "guildId": "111222333",
-    "userId": "444555666",
-    "channelId": "777888999",
-    "messageId": "101010101",
-    "alertSystemMessageId": null,
-    "content": "The message content that triggered AutoMod",
-    "matchedKeyword": "badword1",
-    "matchedContent": "badword1",
-    "action": {
-        "type": 1,
-        "typeName": "block_message"
-    }
-}
-
- -
-

Invite Events

- - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
inviteCreateInvite createdSerializedInvite
inviteDeleteInvite deleted{ code, channelId, guildId }
- -

Invite Object

-
{
-    "code": "abc123",
-    "guildId": "111222333",
-    "channelId": "444555666",
-    "inviter": { ... },
-    "targetUser": null,
-    "targetType": null,
-    "uses": 5,
-    "maxUses": 100,
-    "maxAge": 86400,
-    "temporary": false,
-    "createdAt": "2025-01-01T00:00:00.000Z",
-    "expiresAt": "2025-01-02T00:00:00.000Z",
-    "url": "https://discord.gg/abc123"
-}
-
- -
-

Interaction Events

- - - - - - - - - - - - - - - -
EventDescriptionData
interactionCreateAny interaction (slash command, button, modal, etc.)SerializedInteraction
- -

Interaction Object

-
{
-    "id": "123456789",
-    "type": 2,
-    "typeName": "application_command",
-    "guildId": "111222333",
-    "channelId": "444555666",
-    "user": { ... },
-    "member": { ... },
-    "token": "interaction_token",
-    "applicationId": "777888999",
-    "commandName": "ping",
-    "commandId": "101010101",
-    "commandType": 1,
-    "customId": null,
-    "componentType": null,
-    "values": null,
-    "targetId": null,
-    "locale": "en-US",
-    "guildLocale": "en-US",
-    "createdAt": "2025-01-01T12:00:00.000Z"
-}
-
- -
-

Entitlement Events

-

For Discord app monetization (subscriptions, purchases).

- - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionData
entitlementCreateUser purchased subscriptionSerializedEntitlement
entitlementUpdateSubscription renewedSerializedEntitlement
entitlementDeleteSubscription refunded/removedSerializedEntitlement
-
- -
-

Other Events

- - - - - - - - - - - - - - - -
EventDescriptionData
typingStartUser started typing{ channelId, userId, timestamp, member }
-
- -

Error Handling

-
socket.on('error', (error) => {
-    console.error('WebSocket error:', error.message);
-    if (error.code) {
-        console.error('Error code:', error.code);
-    }
-});
- -

Complete Example

-
import { io } from 'socket.io-client';
-
-const socket = io('http://localhost:3000', {
-    auth: { apiKey: 'your_api_key' }
-});
-
-socket.on('connect', () => {
-    console.log('Connected!');
-    socket.emit('subscribe', { guildIds: ['123456789'] });
-});
-
-socket.on('subscribed', ({ guildIds }) => {
-    console.log('Subscribed to:', guildIds);
-});
-
-socket.on('discord', (payload) => {
-    switch (payload.event) {
-        // Message Events
-        case 'messageCreate':
-            console.log(`[${payload.data.author.username}]: ${payload.data.content}`);
-            break;
-        case 'messageReactionAdd':
-            console.log(`Reaction added: ${payload.data.reaction.emoji.name}`);
-            break;
-        
-        // Member Events
-        case 'guildMemberAdd':
-            console.log(`${payload.data.user.username} joined!`);
-            break;
-        
-        // Scheduled Events
-        case 'guildScheduledEventCreate':
-            console.log(`New event: ${payload.data.name}`);
-            break;
-        
-        // AutoMod Events
-        case 'autoModerationActionExecution':
-            console.log(`AutoMod blocked: ${payload.data.matchedKeyword}`);
-            break;
-        
-        // Interaction Events
-        case 'interactionCreate':
-            console.log(`Interaction: ${payload.data.typeName}`);
-            break;
-        
-        default:
-            console.log(`Event: ${payload.event}`);
-    }
-});
-
-socket.on('error', console.error);
-socket.on('disconnect', (reason) => console.log('Disconnected:', reason));
-
-
-
- - - - - - - \ No newline at end of file From 0b96db39bb820db9ef2eb0e59fa9987d7029029c Mon Sep 17 00:00:00 2001 From: Coder-soft Date: Tue, 16 Dec 2025 15:35:42 +0500 Subject: [PATCH 2/2] Add API reference and docs - Add comprehensive documentation for the REST API and related features. - Introduce the following Markdown files: docs/api-reference.md, docs/getting-started.md, docs/index.md, docs/network.md, docs/plugins.md, docs/security.md, and docs/websocket.md. - The docs include navigation, usage examples, and sections covering authentication, endpoints, plugins, networking, and real-time events. --- docs/api-reference.md | 516 ++++++++++++++++++++++++++++++++++++ docs/getting-started.md | 160 ++++++++++++ docs/index.md | 80 ++++++ docs/network.md | 230 ++++++++++++++++ docs/plugins.md | 454 ++++++++++++++++++++++++++++++++ docs/security.md | 220 ++++++++++++++++ docs/websocket.md | 562 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 2222 insertions(+) create mode 100644 docs/api-reference.md create mode 100644 docs/getting-started.md create mode 100644 docs/index.md create mode 100644 docs/network.md create mode 100644 docs/plugins.md create mode 100644 docs/security.md create mode 100644 docs/websocket.md diff --git a/docs/api-reference.md b/docs/api-reference.md new file mode 100644 index 0000000..ed863cb --- /dev/null +++ b/docs/api-reference.md @@ -0,0 +1,516 @@ +# API Reference + +Complete REST API documentation for HoloBridge. All endpoints require authentication via the `X-API-Key` header. + +**Navigation:** [Home](index.md) | [Getting Started](getting-started.md) | API Reference | [WebSocket](websocket.md) | [Plugins](plugins.md) | [Security](security.md) | [Network](network.md) + +--- + +## Table of Contents + +- [Authentication](#authentication) +- [Guilds](#guilds) +- [Channels](#channels) +- [Messages](#messages) +- [Members](#members) +- [Roles](#roles) +- [Stickers](#stickers) +- [Scheduled Events](#scheduled-events) +- [AutoMod](#automod) +- [Other Resources](#other-resources) +- [Application Commands](#application-commands) + +--- + +## Authentication + +All API requests require the `X-API-Key` header with your configured API key. + +```bash +curl -H "X-API-Key: your_api_key" http://localhost:3000/api/guilds +``` + +### Response Format + +All responses follow a consistent format: + +```json +// Success +{ + "success": true, + "data": { ... } +} + +// Error +{ + "success": false, + "error": "Error message", + "code": "ERROR_CODE" +} +``` + +### Error Codes + +| Code | Description | +|------|-------------| +| `UNAUTHORIZED` | Missing or invalid API key | +| `FORBIDDEN` | API key lacks required scope | +| `NOT_FOUND` | Resource not found | +| `VALIDATION_ERROR` | Invalid request body | +| `RATE_LIMITED` | Too many requests | +| `DISCORD_ERROR` | Discord API error | + +--- + +## Guilds + +### GET `/api/guilds` + +List all guilds the bot is in. + +```bash +curl -H "X-API-Key: your_key" http://localhost:3000/api/guilds +``` + +### GET `/api/guilds/:guildId` + +Get details of a specific guild. + +```bash +curl -H "X-API-Key: your_key" http://localhost:3000/api/guilds/123456789 +``` + +### GET `/api/guilds/:guildId/channels` + +Get all channels in a guild. + +### GET `/api/guilds/:guildId/roles` + +Get all roles in a guild. + +### GET `/api/guilds/:guildId/emojis` + +Get all emojis in a guild. + +### GET `/api/guilds/:guildId/bans` + +Get all bans in a guild. + +### GET `/api/guilds/:guildId/invites` + +Get all invites in a guild. + +--- + +## Channels + +### GET `/api/channels/:channelId` + +Get a channel by ID. + +### POST `/api/guilds/:guildId/channels` + +Create a new channel in a guild. + +#### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `name` | string | Yes | Channel name (1-100 chars) | +| `type` | string | Yes | text, voice, category, announcement, stage, forum | +| `topic` | string | No | Channel topic (max 1024 chars) | +| `parentId` | string | No | Category ID | +| `position` | number | No | Channel position | +| `nsfw` | boolean | No | NSFW flag | +| `rateLimitPerUser` | number | No | Slowmode (0-21600 seconds) | +| `bitrate` | number | No | Voice channel bitrate | +| `userLimit` | number | No | Voice channel user limit (0-99) | + +### PATCH `/api/channels/:channelId` + +Edit a channel. All fields are optional. + +### DELETE `/api/channels/:channelId` + +Delete a channel. + +### POST `/api/channels/:channelId/clone` + +Clone a channel. Optionally provide a `name` in the request body. + +### GET `/api/channels/:channelId/webhooks` + +Get all webhooks for a channel. + +### Threads + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/api/channels/:channelId/threads` | Create a thread | +| GET | `/api/channels/:channelId/threads` | Get all threads | +| POST | `/api/channels/:channelId/archive` | Archive a thread | +| DELETE | `/api/channels/:channelId/archive` | Unarchive a thread | +| POST | `/api/channels/:channelId/lock` | Lock a thread | +| DELETE | `/api/channels/:channelId/lock` | Unlock a thread | + +--- + +## Messages + +### GET `/api/channels/:channelId/messages` + +Get messages from a channel. + +#### Query Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `limit` | number | Number of messages (1-100, default: 50) | +| `before` | string | Get messages before this ID | +| `after` | string | Get messages after this ID | +| `around` | string | Get messages around this ID | + +### GET `/api/channels/:channelId/messages/pinned` + +Get pinned messages in a channel. + +### GET `/api/channels/:channelId/messages/:messageId` + +Get a specific message by ID. + +### POST `/api/channels/:channelId/messages` + +Send a message to a channel. + +#### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `content` | string | No* | Message content (max 2000 chars) | +| `embeds` | array | No* | Array of embed objects | +| `replyTo` | string | No | Message ID to reply to | +| `tts` | boolean | No | Text-to-speech | + +> *Either content or at least one embed is required. + +### PATCH `/api/channels/:channelId/messages/:messageId` + +Edit a message. + +### DELETE `/api/channels/:channelId/messages/:messageId` + +Delete a message. + +### POST `/api/channels/:channelId/messages/bulk-delete` + +Bulk delete messages. Send `messageIds` array in body. + +### POST `/api/channels/:channelId/messages/:messageId/crosspost` + +Crosspost a message (for announcement channels). + +### Reactions + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/api/channels/:channelId/messages/:messageId/reactions/:emoji` | Add reaction | +| DELETE | `/api/channels/:channelId/messages/:messageId/reactions/:emoji` | Remove reaction | +| DELETE | `/api/channels/:channelId/messages/:messageId/reactions` | Remove all reactions | +| GET | `/api/channels/:channelId/messages/:messageId/reactions/:emoji/users` | Get reaction users | + +### Pins + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/api/channels/:channelId/messages/:messageId/pin` | Pin a message | +| DELETE | `/api/channels/:channelId/messages/:messageId/pin` | Unpin a message | + +--- + +## Members + +### GET `/api/guilds/:guildId/members` + +List all members in a guild. + +#### Query Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `limit` | number | Max members to return (default: 1000) | + +### GET `/api/guilds/:guildId/members/search` + +Search members by username/nickname. + +#### Query Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `q` | string | Yes | Search query | +| `limit` | number | No | Max results (default: 20) | + +### GET `/api/guilds/:guildId/members/:userId` + +Get a specific member. + +### Moderation + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/api/guilds/:guildId/members/:userId/kick` | Kick member | +| POST | `/api/guilds/:guildId/members/:userId/ban` | Ban member | +| DELETE | `/api/guilds/:guildId/bans/:userId` | Unban user | +| POST | `/api/guilds/:guildId/members/:userId/timeout` | Timeout member | +| DELETE | `/api/guilds/:guildId/members/:userId/timeout` | Remove timeout | + +#### Ban Request Body + +| Field | Type | Description | +|-------|------|-------------| +| `reason` | string | Ban reason (max 512 chars) | +| `deleteMessageSeconds` | number | Seconds of messages to delete (0-604800) | + +#### Timeout Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `duration` | number | Yes | Duration in milliseconds | +| `reason` | string | No | Timeout reason | + +### Member Roles + +| Method | Endpoint | Description | +|--------|----------|-------------| +| PATCH | `/api/guilds/:guildId/members/:userId/nickname` | Set nickname | +| PATCH | `/api/guilds/:guildId/members/:userId/roles` | Modify roles | + +#### Modify Roles Request Body + +| Field | Type | Description | +|-------|------|-------------| +| `add` | string[] | Role IDs to add | +| `remove` | string[] | Role IDs to remove | + +--- + +## Roles + +### GET `/api/guilds/:guildId/roles` + +Get all roles in a guild. + +### GET `/api/guilds/:guildId/roles/search?name=RoleName` + +Search for a role by name. + +### GET `/api/guilds/:guildId/roles/:roleId` + +Get a specific role. + +### POST `/api/guilds/:guildId/roles` + +Create a new role. + +#### Request Body + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Role name (max 100 chars) | +| `color` | number | Color integer (0-16777215) | +| `hoist` | boolean | Display separately | +| `mentionable` | boolean | Allow mentions | +| `permissions` | string | Permission bitfield | + +### PATCH `/api/guilds/:guildId/roles/:roleId` + +Edit a role. Same fields as create, plus `position`. + +### DELETE `/api/guilds/:guildId/roles/:roleId` + +Delete a role. + +### GET `/api/guilds/:guildId/roles/:roleId/members` + +Get all member IDs with a specific role. + +### PATCH `/api/guilds/:guildId/roles/:roleId/permissions` + +Set role permissions. Send `permissions` (bitfield string) in body. + +--- + +## Stickers + +### GET `/api/guilds/:guildId/stickers` + +List all stickers in a guild. + +### GET `/api/guilds/:guildId/stickers/:stickerId` + +Get a specific sticker. + +### POST `/api/guilds/:guildId/stickers` + +Create a new sticker. + +### PATCH `/api/guilds/:guildId/stickers/:stickerId` + +Edit a sticker. + +### DELETE `/api/guilds/:guildId/stickers/:stickerId` + +Delete a sticker. + +--- + +## Scheduled Events + +### GET `/api/guilds/:guildId/scheduled-events` + +List all scheduled events. + +### GET `/api/guilds/:guildId/scheduled-events/:eventId` + +Get a specific event. + +### POST `/api/guilds/:guildId/scheduled-events` + +Create a new event. + +### PATCH `/api/guilds/:guildId/scheduled-events/:eventId` + +Edit an event. + +### DELETE `/api/guilds/:guildId/scheduled-events/:eventId` + +Delete an event. + +--- + +## AutoMod + +### GET `/api/guilds/:guildId/auto-moderation/rules` + +List all auto-moderation rules. + +### GET `/api/guilds/:guildId/auto-moderation/rules/:ruleId` + +Get a specific rule. + +### POST `/api/guilds/:guildId/auto-moderation/rules` + +Create a new rule. + +### PATCH `/api/guilds/:guildId/auto-moderation/rules/:ruleId` + +Edit a rule. + +### DELETE `/api/guilds/:guildId/auto-moderation/rules/:ruleId` + +Delete a rule. + +--- + +## Other Resources + +### Stage Instances + +Endpoints: `/api/stage-instances` (GET, POST, PATCH, DELETE) + +### Invites + +Endpoints: `/api/invites/:code` (GET, DELETE) + +### Webhooks + +Endpoints: `/api/webhooks/:webhookId` (GET, PATCH, DELETE) + +### Emojis + +Endpoints: `/api/guilds/:guildId/emojis` (GET, POST, PATCH, DELETE) + +--- + +## Application Commands + +Manage Discord Application Commands (Slash Commands) for your bot. Commands can be global (available in all guilds) or guild-specific. + +### Global Commands + +#### GET `/api/commands` + +List all global application commands. + +```bash +curl -H "X-API-Key: your_key" http://localhost:3000/api/commands +``` + +#### GET `/api/commands/:commandId` + +Get a specific global command by ID. + +#### POST `/api/commands` + +Create a new global application command. + +##### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `name` | string | Yes | Command name (1-32 chars, lowercase) | +| `description` | string | Yes | Command description (1-100 chars) | +| `type` | number | No | 1=CHAT_INPUT (default), 2=USER, 3=MESSAGE | +| `options` | array | No | Command options (max 25) | +| `default_member_permissions` | string | No | Permission bitfield | +| `dm_permission` | boolean | No | Allow in DMs | +| `nsfw` | boolean | No | Age-restricted command | + +##### Example + +```bash +curl -X POST -H "X-API-Key: your_key" -H "Content-Type: application/json" \ + -d '{"name": "hello", "description": "Says hello"}' \ + http://localhost:3000/api/commands +``` + +#### PATCH `/api/commands/:commandId` + +Edit a global application command. All fields optional. + +#### DELETE `/api/commands/:commandId` + +Delete a global application command. + +### Guild-Specific Commands + +Guild commands are only available in the specified guild. They update instantly (unlike global commands which can take up to an hour). + +#### GET `/api/guilds/:guildId/commands` + +List all commands for a specific guild. + +#### GET `/api/guilds/:guildId/commands/:commandId` + +Get a specific guild command. + +#### POST `/api/guilds/:guildId/commands` + +Create a guild-specific command. Same request body as global commands. + +#### PATCH `/api/guilds/:guildId/commands/:commandId` + +Edit a guild-specific command. + +#### DELETE `/api/guilds/:guildId/commands/:commandId` + +Delete a guild-specific command. + +--- + +## Next Steps + +- [WebSocket Events](websocket.md) - Real-time Discord event streaming +- [Plugins](plugins.md) - Extend HoloBridge with custom endpoints +- [Security](security.md) - API scopes and rate limiting +- [Network Configuration](network.md) - Access the API from other devices \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..70c90e0 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,160 @@ +# Getting Started + +This guide will walk you through installing, configuring, and running HoloBridge. + +**Navigation:** [Home](index.md) | Getting Started | [API Reference](api-reference.md) | [WebSocket](websocket.md) | [Plugins](plugins.md) | [Security](security.md) | [Network](network.md) + +--- + +## Prerequisites + +- Node.js 18 or higher +- npm or yarn package manager +- A Discord bot token + +## Installation + +### 1. Clone the Repository + +```bash +git clone https://github.com/coder-soft/holobridge.git +cd holobridge +``` + +### 2. Install Dependencies + +```bash +npm install +``` + +## Configuration + +### Environment Variables + +Copy `.env.example` to `.env` and fill in your values: + +```env +# Discord Bot Token (required) +# Get this from https://discord.com/developers/applications +DISCORD_TOKEN=your_discord_bot_token_here + +# API Configuration +PORT=3000 +HOST=0.0.0.0 +API_KEY=your_secure_api_key_here + +# Optional: Enable debug logging +DEBUG=false + +# Optional: Plugin configuration +PLUGINS_ENABLED=true +PLUGINS_DIR=plugins + +# Optional: Rate limiting +RATE_LIMIT_ENABLED=true +RATE_LIMIT_WINDOW_MS=60000 +RATE_LIMIT_MAX=100 +``` + +### Configuration Reference + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `DISCORD_TOKEN` | Yes | - | Your Discord bot token | +| `API_KEY` | Yes | - | API key for authenticating requests | +| `PORT` | No | `3000` | Server port | +| `HOST` | No | `0.0.0.0` | Host to bind to (see [Network Configuration](network.md)) | +| `DEBUG` | No | `false` | Enable debug logging | +| `PLUGINS_ENABLED` | No | `true` | Enable/disable the plugin system | +| `PLUGINS_DIR` | No | `plugins` | Directory for plugins | +| `RATE_LIMIT_ENABLED` | No | `true` | Enable rate limiting | +| `RATE_LIMIT_WINDOW_MS` | No | `60000` | Rate limit window in milliseconds | +| `RATE_LIMIT_MAX` | No | `100` | Max requests per window | + +## Running the Server + +### Development Mode + +```bash +npm run dev +``` + +### Production Mode + +```bash +npm run build +npm start +``` + +### Using Docker + +```bash +docker-compose up -d +``` + +## Discord Bot Setup + +Follow these steps to create a Discord bot and invite it to your server: + +1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) +2. Click "New Application" and give it a name +3. Go to the "Bot" section in the left sidebar +4. Click "Add Bot" and confirm +5. Enable the following **Privileged Gateway Intents**: + - Presence Intent + - Server Members Intent + - Message Content Intent +6. Click "Reset Token" to get your bot token and copy it to your `.env` file + +### Inviting the Bot + +1. Go to the "OAuth2" → "URL Generator" section +2. Select the following scopes: + - `bot` + - `applications.commands` +3. Select the permissions your bot needs (Administrator for full access) +4. Copy the generated URL and open it in your browser +5. Select a server and authorize the bot + +> âš ī¸ **Important:** Keep your bot token and API key secret. Never commit them to version control. + +## Testing the API + +Once the server is running, you can test the API with curl: + +```bash +# Health check (no auth required) +curl http://localhost:3000/health + +# List all guilds +curl -H "X-API-Key: your_api_key" http://localhost:3000/api/guilds +``` + +## Server Endpoints + +When the server starts, it will display all available endpoints: + +``` +🌐 API server listening on 0.0.0.0:3000 + Local: http://localhost:3000/api + Network: http://192.168.1.100:3000/api + API Docs: http://192.168.1.100:3000/api/docs + Plugin API: http://192.168.1.100:3000/api/plugins + WebSocket: ws://192.168.1.100:3000 + Health: http://192.168.1.100:3000/health +``` + +- **Local** - Access from the same machine +- **Network** - Access from other devices on your network (see [Network Configuration](network.md)) +- **API Docs** - Interactive Swagger UI documentation +- **Plugin API** - Endpoints registered by plugins +- **WebSocket** - Socket.IO endpoint for real-time events +- **Health** - Health check endpoint for monitoring + +## Next Steps + +- [API Reference](api-reference.md) - Explore all available REST API endpoints +- [WebSocket Events](websocket.md) - Learn how to receive real-time Discord events +- [Network Configuration](network.md) - Expose the API on your local network +- [Plugins](plugins.md) - Extend HoloBridge with custom functionality +- [Security](security.md) - Configure API scopes and rate limiting \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..bd97e5b --- /dev/null +++ b/docs/index.md @@ -0,0 +1,80 @@ +# HoloBridge Documentation + +A type-safe TypeScript bridge between websites and Discord bots. Provides a REST API and WebSocket interface for full Discord bot capabilities. + +## Quick Start + +- [Getting Started](getting-started.md) - Installation, configuration, and running the server +- [API Reference](api-reference.md) - Complete REST API endpoint documentation +- [WebSocket Events](websocket.md) - Real-time event streaming documentation +- [Plugins](plugins.md) - Create plugins with event handling and REST endpoints +- [Security](security.md) - API scopes, rate limiting, and security features +- [Network Configuration](network.md) - Expose your API on the local network + +## Features + +### 🌐 REST API + +Full REST API for all Discord operations including guilds, channels, messages, members, and roles. + +### ⚡ WebSocket Events + +Real-time event streaming via Socket.IO. Subscribe to guilds and receive Discord events instantly. + +### 🔒 Type-Safe + +Built with TypeScript and Zod validation. Every request and response is fully typed. + +### đŸ“Ļ Full Coverage + +Guilds, Channels, Roles, Members, Bans, Timeouts, Messages, Reactions, Pins, and more. + +### 🔌 Plugin System + +Extend functionality with plugins. Add routes, listen to events, and communicate between plugins. + +### 🌍 Network Exposed API + +Bind to `0.0.0.0` to expose the API on your local network for access from other devices. + +## Discord Operations + +- **Guilds** - List guilds, get details, channels, roles, emojis, bans, invites +- **Channels** - Create, edit, delete channels and threads +- **Messages** - Send, edit, delete, bulk delete, reactions, pins +- **Members** - List, search, kick, ban, timeout, role management +- **Roles** - Create, edit, delete, permissions management + +## Real-Time Events + +- **Message Events** - messageCreate, messageUpdate, messageDelete +- **Member Events** - guildMemberAdd, guildMemberRemove, guildMemberUpdate +- **Channel Events** - channelCreate, channelUpdate, channelDelete +- **Role Events** - roleCreate, roleUpdate, roleDelete +- **Voice Events** - voiceStateUpdate +- **Guild Events** - guildBanAdd, guildBanRemove +- **And 40+ more** - See the [WebSocket documentation](websocket.md) for the full list + +## API Endpoints Overview + +| Resource | Endpoints | +|----------|-----------| +| Guilds | `/api/guilds` | +| Channels | `/api/channels`, `/api/guilds/:guildId/channels` | +| Messages | `/api/channels/:channelId/messages` | +| Members | `/api/guilds/:guildId/members` | +| Roles | `/api/guilds/:guildId/roles` | +| Emojis | `/api/guilds/:guildId/emojis` | +| Stickers | `/api/guilds/:guildId/stickers` | +| Scheduled Events | `/api/guilds/:guildId/scheduled-events` | +| AutoMod | `/api/guilds/:guildId/auto-moderation` | +| Commands | `/api/commands`, `/api/guilds/:guildId/commands` | +| Webhooks | `/api/webhooks` | +| Invites | `/api/invites` | +| Stage Instances | `/api/stage-instances` | +| Voice | `/api/guilds/:guildId/voice` | +| Plugins | `/api/plugins` | + +## License + +HoloBridge Š 2025 - MIT License \ No newline at end of file diff --git a/docs/network.md b/docs/network.md new file mode 100644 index 0000000..5f734b0 --- /dev/null +++ b/docs/network.md @@ -0,0 +1,230 @@ +# Network Configuration + +HoloBridge can be configured to expose its API on your local network, allowing access from other devices such as phones, tablets, or other computers on the same network. + +**Navigation:** [Home](index.md) | [Getting Started](getting-started.md) | [API Reference](api-reference.md) | [WebSocket](websocket.md) | [Plugins](plugins.md) | [Security](security.md) | Network + +--- + +## Overview + +By default, HoloBridge binds to `0.0.0.0`, which means it listens on all available network interfaces. This allows the API to be accessed from: + +- **localhost** - The machine running HoloBridge +- **Local Network IP** - Other devices on the same network (e.g., `192.168.1.x`) + +## Configuration + +### HOST Environment Variable + +The `HOST` environment variable controls which network interface(s) the server binds to: + +```env +# Bind to all interfaces (accessible from network) - DEFAULT +HOST=0.0.0.0 + +# Bind to localhost only (not accessible from network) +HOST=127.0.0.1 + +# Bind to a specific IP address +HOST=192.168.1.100 +``` + +| Value | Description | Network Access | +|-------|-------------|----------------| +| `0.0.0.0` | All interfaces (default) | ✅ Yes | +| `127.0.0.1` | Localhost only | ❌ No | +| `localhost` | Localhost only | ❌ No | +| Specific IP | Single interface | ✅ Yes (from that network) | + +### PORT Environment Variable + +```env +# Default port +PORT=3000 + +# Custom port +PORT=8080 +``` + +## Server Output + +When HoloBridge starts, it displays all available access URLs: + +``` +🌐 API server listening on 0.0.0.0:3000 + Local: http://localhost:3000/api + Network: http://192.168.1.100:3000/api + API Docs: http://192.168.1.100:3000/api/docs + Plugin API: http://192.168.1.100:3000/api/plugins + WebSocket: ws://192.168.1.100:3000 + Health: http://192.168.1.100:3000/health +``` + +The **Network** URL shows your local network IP address, which other devices on your network can use to access the API. + +## Accessing from Other Devices + +### REST API + +From another device on your network: + +```bash +# Replace with your HoloBridge server's IP +curl -H "X-API-Key: your_api_key" http://192.168.1.100:3000/api/guilds +``` + +### WebSocket + +Connect to the WebSocket from any device: + +```javascript +import { io } from 'socket.io-client'; + +// Use the network IP address +const socket = io('http://192.168.1.100:3000', { + auth: { + apiKey: 'your_api_key' + } +}); + +socket.on('connect', () => { + console.log('Connected from remote device!'); +}); +``` + +### API Documentation (Swagger UI) + +Access the interactive API documentation from any browser on your network: + +``` +http://192.168.1.100:3000/api/docs +``` + +## Finding Your Network IP + +HoloBridge automatically detects and displays your local network IP. If you need to find it manually: + +### Windows + +```cmd +ipconfig +``` + +Look for "IPv4 Address" under your active network adapter. + +### macOS / Linux + +```bash +# macOS +ipconfig getifaddr en0 + +# Linux +hostname -I | awk '{print $1}' +``` + +## Security Considerations + +> âš ī¸ **Warning:** Exposing the API on your network means any device on that network can potentially access it. + +### Best Practices + +1. **Always use API key authentication** - Never disable the `API_KEY` requirement +2. **Use scoped API keys** - Create keys with minimal required permissions (see [Security](security.md)) +3. **Enable rate limiting** - Protect against abuse with rate limits +4. **Use a firewall** - Restrict access to trusted IP ranges if possible +5. **Don't expose to the internet** - The default configuration is for local network access only + +### Restricting Access + +To restrict access to localhost only: + +```env +HOST=127.0.0.1 +``` + +To use scoped API keys with limited permissions: + +```env +API_KEYS=[ + {"id":"mobile","name":"Mobile App","key":"mobile_xxx","scopes":["read:guilds","read:messages"]}, + {"id":"admin","name":"Admin Only","key":"admin_xxx","scopes":["admin"]} +] +``` + +See the [Security documentation](security.md) for more details on API scopes. + +## Docker Configuration + +When running in Docker, ensure port mapping is configured: + +```yaml +# docker-compose.yml +services: + holobridge: + ports: + - "3000:3000" # Maps host:container + environment: + - HOST=0.0.0.0 + - PORT=3000 +``` + +To restrict to localhost when using Docker: + +```yaml +services: + holobridge: + ports: + - "127.0.0.1:3000:3000" # Only accessible from host machine +``` + +## Troubleshooting + +### Cannot access from other devices + +1. **Check firewall settings** - Ensure port 3000 (or your configured port) is allowed +2. **Verify HOST setting** - Must be `0.0.0.0` or a specific network IP +3. **Check network connectivity** - Ensure devices are on the same network +4. **Verify the IP address** - Use the IP shown in the server output + +### Connection refused + +- The server may not be running +- The port may be blocked by a firewall +- HOST may be set to `127.0.0.1` + +### Authentication errors + +- Ensure you're including the `X-API-Key` header +- Check that the API key matches your `.env` configuration +- Verify the API key has the required scopes + +## Use Cases + +### Mobile App Development + +Access HoloBridge from your phone while developing a mobile Discord client: + +```javascript +// In your React Native / Flutter app +const API_URL = 'http://192.168.1.100:3000/api'; + +fetch(`${API_URL}/guilds`, { + headers: { 'X-API-Key': 'your_api_key' } +}); +``` + +### Multi-Device Dashboard + +Run a Discord dashboard on multiple screens in your home or office, all connecting to a single HoloBridge instance. + +### Development & Testing + +Test your Discord integration from different devices without deploying to a server. + +## Next Steps + +- [Security](security.md) - Configure API scopes and rate limiting +- [Getting Started](getting-started.md) - Initial setup and configuration +- [API Reference](api-reference.md) - Explore all available endpoints +- [WebSocket Events](websocket.md) - Real-time event streaming \ No newline at end of file diff --git a/docs/plugins.md b/docs/plugins.md new file mode 100644 index 0000000..02bfd4b --- /dev/null +++ b/docs/plugins.md @@ -0,0 +1,454 @@ +# Plugin System + +Extend HoloBridge with custom functionality using the powerful plugin system. Plugins can add REST API endpoints, listen to Discord events, and communicate with other plugins via a typed event bus. + +**Navigation:** [Home](index.md) | [Getting Started](getting-started.md) | [API Reference](api-reference.md) | [WebSocket](websocket.md) | Plugins | [Security](security.md) | [Network](network.md) + +--- + +## Table of Contents + +- [Quick Start](#quick-start) +- [Plugin Structure](#plugin-structure) +- [Plugin Context](#plugin-context) +- [REST API Routes](#rest-api-routes) +- [Event Bus](#event-bus) +- [Inter-Plugin Communication](#inter-plugin-communication) +- [Complete Example](#complete-example) +- [Best Practices](#best-practices) + +--- + +> **â„šī¸ Note:** Plugins are JavaScript/ESM modules placed in the `plugins/` directory. They are automatically loaded on startup. + +--- + +## Quick Start + +Create a file in the `plugins/` directory with a `.js` or `.mjs` extension: + +### plugins/my-plugin.js + +```javascript +export default { + metadata: { + name: 'my-plugin', + version: '1.0.0', + author: 'Your Name', + description: 'A sample HoloBridge plugin' + }, + + // Optional: Register REST endpoints + routes: (router, ctx) => { + router.get('/status', (req, res) => { + res.json({ status: 'ok', plugin: 'my-plugin' }); + }); + }, + + // Optional: Subscribe to events + events: (on, ctx) => [ + on.onDiscord('messageCreate', (msg) => { + ctx.logger.info('New message:', msg.content); + }), + ], + + // Optional: Called when plugin loads + onLoad: (ctx) => { + ctx.logger.info('Plugin loaded!'); + }, + + // Optional: Called when plugin unloads + onUnload: () => { + console.log('Plugin unloaded!'); + } +}; +``` + +--- + +## Plugin Structure + +### Plugin Metadata + +Every plugin must export an object with a `metadata` property: + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `name` | string | Yes | Unique plugin identifier (used in routes and logs) | +| `version` | string | Yes | Semantic version (e.g., "1.0.0") | +| `author` | string | No | Plugin author name | +| `description` | string | No | Short description of the plugin | + +### Lifecycle Hooks + +| Hook | When Called | Use Case | +|------|-------------|----------| +| `onLoad(ctx)` | After plugin is loaded | Initialize state, setup connections | +| `onUnload()` | Before plugin is unloaded | Cleanup resources, close connections | + +--- + +## Plugin Context + +The `ctx` object provides access to core HoloBridge services: + +```typescript +interface PluginContext { + client: Client; // Discord.js client + io: SocketIOServer; // Socket.IO server + config: Config; // Application configuration + app: Application; // Express application + eventBus: PluginEventBus; // Event bus for inter-plugin communication + logger: PluginLogger; // Logging utility + log: (message: string) => void; // Legacy logger + getPlugin: (name: string) => PluginMetadata | undefined; + listPlugins: () => string[]; +} +``` + +### Logger + +Use the built-in logger for consistent output: + +```javascript +ctx.logger.info('Information message'); +ctx.logger.warn('Warning message'); +ctx.logger.error('Error message'); +ctx.logger.debug('Debug message (only in debug mode)'); +``` + +--- + +## REST API Routes + +Plugins can register REST API endpoints that are automatically mounted at `/api/plugins/{plugin-name}/`: + +```javascript +routes: (router, ctx) => { + // GET /api/plugins/my-plugin/status + router.get('/status', (req, res) => { + res.json({ status: 'ok' }); + }); + + // POST /api/plugins/my-plugin/action + router.post('/action', (req, res) => { + const { userId, action } = req.body; + // Handle the action + res.json({ success: true }); + }); + + // Routes support all HTTP methods + router.put('/update', handler); + router.patch('/modify', handler); + router.delete('/remove', handler); + + // Add middleware + router.use(myMiddleware); +} +``` + +> **â„šī¸ Automatic Error Handling:** Plugin routes are automatically wrapped with error handling. Errors are caught and returned as JSON responses. + +--- + +## Event Bus + +HoloBridge provides a typed event bus for inter-plugin communication with three event categories: + +| Category | Prefix | Description | +|----------|--------|-------------| +| 🎮 Discord Events | `discord:` | Events forwarded from the Discord gateway | +| 🔌 Plugin Events | `plugin:` | Lifecycle events like plugin load/unload | +| ✨ Custom Events | `custom:` | Events emitted by plugins for inter-plugin communication | + +### Subscribing to Events + +Use the `events` hook to subscribe to events. Return an array of subscriptions for automatic cleanup: + +```javascript +events: (on, ctx) => [ + // Subscribe to Discord events + on.onDiscord('messageCreate', (message) => { + ctx.logger.info('New message:', message.content); + }), + + on.onDiscord('guildMemberAdd', (member) => { + ctx.logger.info('New member joined:', member.user.username); + }), + + // Subscribe to custom events from other plugins + on.onCustom('moderation:user-warned', (data) => { + ctx.logger.info(`User ${data.userId} was warned`); + }), + + // Subscribe to plugin lifecycle events + on.onPluginLoaded((data) => { + ctx.logger.info(`Plugin loaded: ${data.name} v${data.version}`); + }), + + on.onPluginUnloaded((data) => { + ctx.logger.info(`Plugin unloaded: ${data.name}`); + }), +] +``` + +### Emitting Custom Events + +Plugins can emit custom events for other plugins to consume: + +```javascript +events: (on, ctx) => { + // Emit a custom event + on.emit('my-plugin:action-performed', { + userId: '123456789', + action: 'ban', + reason: 'Spam' + }); + + return []; +} +``` + +### Available Discord Events + +All standard Discord.js events are available. Common events include: + +| Event | Data | Description | +|-------|------|-------------| +| `messageCreate` | Serialized Message | New message sent | +| `messageUpdate` | Serialized Message | Message edited | +| `messageDelete` | Message info | Message deleted | +| `guildMemberAdd` | Serialized Member | Member joined | +| `guildMemberRemove` | Serialized Member | Member left/kicked | +| `guildMemberUpdate` | Serialized Member | Member updated | +| `channelCreate` | Serialized Channel | Channel created | +| `channelUpdate` | Serialized Channel | Channel updated | +| `channelDelete` | Channel info | Channel deleted | +| `roleCreate` | Serialized Role | Role created | +| `roleUpdate` | Serialized Role | Role updated | +| `roleDelete` | Role info | Role deleted | +| `voiceStateUpdate` | Voice state data | Voice state changed | +| `guildBanAdd` | Ban info | Member banned | +| `guildBanRemove` | Ban info | Member unbanned | +| `threadCreate` | Serialized Thread | Thread created | +| `threadUpdate` | Serialized Thread | Thread updated | +| `threadDelete` | Thread info | Thread deleted | + +See [WebSocket Events](websocket.md) for the complete list. + +### Plugin Lifecycle Events + +| Event | Data | Description | +|-------|------|-------------| +| `plugin:loaded` | `{ name, version }` | A plugin was loaded | +| `plugin:unloaded` | `{ name }` | A plugin was unloaded | +| `plugin:error` | `{ name, error }` | A plugin encountered an error | + +--- + +## Direct Event Bus Access + +For advanced use cases, access the event bus directly via `ctx.eventBus`: + +```javascript +onLoad: (ctx) => { + const { eventBus } = ctx; + + // Subscribe to any event + const subscription = eventBus.subscribe('custom:my-event', (data) => { + console.log('Received:', data); + }); + + // Subscribe once + eventBus.subscribeOnce('discord:ready', () => { + console.log('Bot is ready!'); + }); + + // Emit Discord events (typically done by core) + eventBus.emitDiscord('messageCreate', messageData); + + // Emit custom events + eventBus.emitCustom('my-plugin:action', { key: 'value' }); + + // Emit plugin lifecycle events + eventBus.emitPlugin('plugin:error', { name: 'my-plugin', error: new Error('Oops') }); + + // Get listener counts (for debugging) + console.log(eventBus.getListenerCounts()); +} +``` + +### Event Bus Methods + +| Method | Description | +|--------|-------------| +| `onDiscord(event, handler)` | Subscribe to a Discord event | +| `onCustom(event, handler)` | Subscribe to a custom event | +| `onPlugin(event, handler)` | Subscribe to a plugin lifecycle event | +| `emitDiscord(event, data)` | Emit a Discord event | +| `emitCustom(event, data)` | Emit a custom event | +| `emitPlugin(event, data)` | Emit a plugin lifecycle event | +| `subscribe(event, handler)` | Subscribe to any event (returns subscription object) | +| `subscribeOnce(event, handler)` | Subscribe once and automatically unsubscribe | +| `unsubscribeAll(subscriptions)` | Unsubscribe from multiple events at once | + +--- + +## Inter-Plugin Communication + +Plugins can discover and interact with other loaded plugins: + +```javascript +onLoad: (ctx) => { + // List all loaded plugins + const plugins = ctx.listPlugins(); + ctx.logger.info('Loaded plugins:', plugins); + + // Get another plugin's metadata + const modPlugin = ctx.getPlugin('moderation'); + if (modPlugin) { + ctx.logger.info(`Moderation plugin v${modPlugin.version} is loaded`); + } +} +``` + +### Example: Plugin Communication Pattern + +```javascript +// Plugin A: moderation.js +export default { + metadata: { name: 'moderation', version: '1.0.0' }, + + routes: (router, ctx) => { + router.post('/warn', (req, res) => { + const { userId, reason } = req.body; + + // Emit event for other plugins + ctx.eventBus.emitCustom('moderation:user-warned', { + userId, + reason, + timestamp: Date.now() + }); + + res.json({ success: true }); + }); + } +}; + +// Plugin B: logging.js +export default { + metadata: { name: 'logging', version: '1.0.0' }, + + events: (on, ctx) => [ + // Listen for moderation events + on.onCustom('moderation:user-warned', (data) => { + ctx.logger.info(`[AUDIT] User ${data.userId} warned: ${data.reason}`); + // Log to database, send to webhook, etc. + }), + ] +}; +``` + +--- + +## Complete Example + +Here's a complete example of a plugin that demonstrates all features: + +```javascript +// plugins/welcome.js +export default { + metadata: { + name: 'welcome', + version: '1.0.0', + author: 'HoloBridge', + description: 'Welcome new members with customizable messages' + }, + + // Configuration stored in memory (use a database in production) + _config: { + enabled: true, + channelId: null, + message: 'Welcome to the server, {user}!' + }, + + routes: (router, ctx) => { + // GET /api/plugins/welcome/config + router.get('/config', (req, res) => { + res.json({ + success: true, + data: this._config + }); + }); + + // PATCH /api/plugins/welcome/config + router.patch('/config', (req, res) => { + const { enabled, channelId, message } = req.body; + + if (enabled !== undefined) this._config.enabled = enabled; + if (channelId !== undefined) this._config.channelId = channelId; + if (message !== undefined) this._config.message = message; + + res.json({ success: true, data: this._config }); + }); + }, + + events: (on, ctx) => [ + on.onDiscord('guildMemberAdd', async (member) => { + if (!this._config.enabled || !this._config.channelId) return; + + try { + const channel = await ctx.client.channels.fetch(this._config.channelId); + if (channel?.isTextBased()) { + const message = this._config.message + .replace('{user}', `<@${member.user.id}>`) + .replace('{username}', member.user.username) + .replace('{server}', member.guild.name); + + await channel.send(message); + ctx.logger.info(`Welcomed ${member.user.username}`); + } + } catch (error) { + ctx.logger.error('Failed to send welcome message:', error); + } + }), + ], + + onLoad: (ctx) => { + ctx.logger.info('Welcome plugin loaded!'); + ctx.logger.info(`Routes available at /api/plugins/welcome/`); + }, + + onUnload: () => { + console.log('[welcome] Plugin unloaded'); + } +}; +``` + +--- + +## Best Practices + +### ✅ Do: + +- Return event subscriptions from the `events` hook for automatic cleanup +- Use `ctx.logger` for consistent, prefixed logging +- Handle errors gracefully in event handlers +- Use semantic versioning for your plugin version +- Namespace custom events with your plugin name (e.g., `my-plugin:event-name`) + +### âš ī¸ Avoid: + +- Blocking the event loop with synchronous operations +- Storing sensitive data in plugin state without encryption +- Using the deprecated `onEvent` hook (use `events` instead) +- Creating memory leaks by not cleaning up resources in `onUnload` + +--- + +## Next Steps + +- [API Reference](api-reference.md) - Explore the REST API that plugins can extend +- [WebSocket Events](websocket.md) - See all available Discord events you can subscribe to +- [Security](security.md) - Configure API scopes for plugin endpoints +- [Network Configuration](network.md) - Access plugin APIs from other devices \ No newline at end of file diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 0000000..b95ec2c --- /dev/null +++ b/docs/security.md @@ -0,0 +1,220 @@ +# Security & API Scopes + +HoloBridge provides granular access control through API scopes, allowing you to create API keys with limited permissions. + +**Navigation:** [Home](index.md) | [Getting Started](getting-started.md) | [API Reference](api-reference.md) | [WebSocket](websocket.md) | [Plugins](plugins.md) | Security | [Network](network.md) + +--- + +## Table of Contents + +- [API Key Configuration](#api-key-configuration) +- [Available Scopes](#available-scopes) +- [Rate Limiting](#rate-limiting) +- [Best Practices](#best-practices) + +--- + +## API Key Configuration + +### Single Key (Simple) + +For basic setups, use a single API key in `.env`: + +```env +API_KEY=your_secure_api_key +``` + +This key has `admin` scope (full access). + +### Multiple Keys with Scopes + +For production, define multiple keys with specific permissions using the `API_KEYS` environment variable: + +```env +API_KEYS=[ + {"id":"dashboard","name":"Web Dashboard","key":"dash_xxx","scopes":["read:guilds","read:members"]}, + {"id":"bot","name":"Chat Bot","key":"bot_xxx","scopes":["read:messages","write:messages"]}, + {"id":"admin","name":"Admin Panel","key":"admin_xxx","scopes":["admin"]} +] +``` + +### API Key Schema + +Each API key object has the following properties: + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `id` | string | Yes | Unique identifier for the key | +| `name` | string | Yes | Human-readable name | +| `key` | string | Yes | The actual API key value | +| `scopes` | string[] | Yes | Array of permission scopes | +| `createdAt` | date | No | When the key was created | + +--- + +## Available Scopes + +| Scope | Permissions | +|-------|-------------| +| `read:guilds` | List guilds, get guild details | +| `read:channels` | List channels, get channel info | +| `read:members` | List members, get member details | +| `read:messages` | Read message history | +| `write:messages` | Send, edit, delete messages | +| `write:members` | Kick, ban, timeout members | +| `write:channels` | Create, edit, delete channels | +| `write:roles` | Create, edit, delete roles | +| `events` | Subscribe to WebSocket events | +| `admin` | Full access (bypasses all checks) | + +### Scope Examples + +**Read-only dashboard:** +```json +{"id":"dashboard","name":"Dashboard","key":"dash_xxx","scopes":["read:guilds","read:channels","read:members"]} +``` + +**Message bot:** +```json +{"id":"msgbot","name":"Message Bot","key":"msg_xxx","scopes":["read:messages","write:messages"]} +``` + +**Moderation bot:** +```json +{"id":"modbot","name":"Mod Bot","key":"mod_xxx","scopes":["read:members","write:members"]} +``` + +**WebSocket listener:** +```json +{"id":"listener","name":"Event Listener","key":"ws_xxx","scopes":["events","read:guilds"]} +``` + +--- + +## Rate Limiting + +HoloBridge includes built-in rate limiting to protect against abuse. + +### Configuration + +```env +RATE_LIMIT_ENABLED=true +RATE_LIMIT_WINDOW_MS=60000 # 1 minute window +RATE_LIMIT_MAX=100 # 100 requests per window +``` + +| Variable | Default | Description | +|----------|---------|-------------| +| `RATE_LIMIT_ENABLED` | `true` | Enable/disable rate limiting | +| `RATE_LIMIT_WINDOW_MS` | `60000` | Time window in milliseconds | +| `RATE_LIMIT_MAX` | `100` | Maximum requests per window | + +### Response Headers + +All API responses include rate limit headers: + +| Header | Description | +|--------|-------------| +| `X-RateLimit-Limit` | Maximum requests per window | +| `X-RateLimit-Remaining` | Requests remaining in current window | +| `X-RateLimit-Reset` | Unix timestamp when limit resets | + +### Rate Limited Response + +When the limit is exceeded, you'll receive a `429 Too Many Requests` response: + +```json +{ + "success": false, + "error": "Too many requests", + "code": "RATE_LIMITED", + "retryAfter": 45 +} +``` + +The `retryAfter` field indicates how many seconds to wait before retrying. + +--- + +## Best Practices + +### API Key Security + +1. **Use scoped keys** — Give each integration only the permissions it needs +2. **Rotate keys regularly** — Update API keys periodically +3. **Keep admin keys secure** — Only use admin scope for trusted applications +4. **Never commit keys** — Add `.env` to `.gitignore` +5. **Use environment variables** — Don't hardcode keys in your application + +### Network Security + +When exposing the API on your network (see [Network Configuration](network.md)): + +1. **Always require authentication** — Never disable the `API_KEY` requirement +2. **Use scoped keys for remote access** — Create specific keys for each client device +3. **Monitor usage** — Watch rate limit headers to identify issues +4. **Use HTTPS in production** — Consider using a reverse proxy with TLS + +### Example: Secure Multi-Client Setup + +```env +# Main admin key (local use only) +API_KEY=admin_super_secret_key + +# Scoped keys for different clients +API_KEYS=[ + {"id":"web-dashboard","name":"Web Dashboard","key":"web_abc123","scopes":["read:guilds","read:channels","read:members","events"]}, + {"id":"mobile-app","name":"Mobile App","key":"mob_def456","scopes":["read:guilds","read:messages"]}, + {"id":"bot-service","name":"Bot Service","key":"bot_ghi789","scopes":["read:messages","write:messages","events"]}, + {"id":"admin-cli","name":"Admin CLI","key":"cli_jkl012","scopes":["admin"]} +] +``` + +### Monitoring and Auditing + +- Log API key usage (which key accessed what endpoint) +- Set up alerts for rate limit violations +- Review and revoke unused keys periodically +- Use unique keys per integration for better tracking + +--- + +## Authentication Errors + +### 401 Unauthorized + +Missing or invalid API key: + +```json +{ + "success": false, + "error": "API key required", + "code": "UNAUTHORIZED" +} +``` + +**Solution:** Include the `X-API-Key` header with a valid key. + +### 403 Forbidden + +API key lacks required scope: + +```json +{ + "success": false, + "error": "Insufficient permissions", + "code": "FORBIDDEN" +} +``` + +**Solution:** Use an API key with the required scope, or add the scope to the existing key. + +--- + +## Next Steps + +- [Getting Started](getting-started.md) - Initial setup and configuration +- [API Reference](api-reference.md) - Explore all available REST API endpoints +- [Network Configuration](network.md) - Expose the API on your local network +- [Plugins](plugins.md) - Create plugins with custom endpoints \ No newline at end of file diff --git a/docs/websocket.md b/docs/websocket.md new file mode 100644 index 0000000..a4af3c9 --- /dev/null +++ b/docs/websocket.md @@ -0,0 +1,562 @@ +# WebSocket Events + +Real-time Discord event streaming via Socket.IO. Subscribe to guilds and receive Discord events as they happen. **45+ event types are supported.** + +**Navigation:** [Home](index.md) | [Getting Started](getting-started.md) | [API Reference](api-reference.md) | WebSocket | [Plugins](plugins.md) | [Security](security.md) | [Network](network.md) + +--- + +## Table of Contents + +- [Connection](#connection) +- [Authentication](#authentication) +- [Subscribing to Guilds](#subscribing-to-guilds) +- [Event Types](#event-types) + - [Message Events](#message-events) + - [Reaction Events](#reaction-events) + - [Poll Events](#poll-events) + - [Member Events](#member-events) + - [Channel Events](#channel-events) + - [Thread Events](#thread-events) + - [Role Events](#role-events) + - [Guild Events](#guild-events) + - [Emoji Events](#emoji-events) + - [Sticker Events](#sticker-events) + - [Voice Events](#voice-events) + - [Stage Instance Events](#stage-instance-events) + - [Scheduled Event Events](#scheduled-event-events) + - [AutoMod Events](#automod-events) + - [Invite Events](#invite-events) + - [Interaction Events](#interaction-events) + - [Entitlement Events](#entitlement-events) + - [Other Events](#other-events) +- [Error Handling](#error-handling) +- [Complete Example](#complete-example) + +--- + +## Connection + +Connect to the WebSocket server using Socket.IO: + +```javascript +import { io } from 'socket.io-client'; + +const socket = io('http://localhost:3000', { + auth: { + apiKey: 'your_api_key' + } +}); + +socket.on('connect', () => { + console.log('Connected to HoloBridge'); +}); + +socket.on('connect_error', (error) => { + console.error('Connection error:', error.message); +}); + +socket.on('disconnect', (reason) => { + console.log('Disconnected:', reason); +}); +``` + +For network access from other devices, use the network IP: + +```javascript +const socket = io('http://192.168.1.100:3000', { + auth: { apiKey: 'your_api_key' } +}); +``` + +See [Network Configuration](network.md) for more details. + +--- + +## Authentication + +Authentication is required and must be provided in the `auth` option when connecting: + +```javascript +const socket = io('http://localhost:3000', { + auth: { + apiKey: 'your_secure_api_key' + } +}); +``` + +> **Note:** If authentication fails, the connection will be rejected with an error. + +--- + +## Subscribing to Guilds + +After connecting, subscribe to specific guilds to receive their events: + +```javascript +// Subscribe to guilds +socket.emit('subscribe', { + guildIds: ['123456789012345678', '987654321098765432'] +}); + +// Listen for subscription confirmation +socket.on('subscribed', (data) => { + console.log('Subscribed to guilds:', data.guildIds); +}); + +// Unsubscribe from guilds +socket.emit('unsubscribe', { + guildIds: ['123456789012345678'] +}); + +socket.on('unsubscribed', (data) => { + console.log('Unsubscribed from guilds:', data.guildIds); +}); +``` + +### Listening for Events + +All Discord events are emitted through the `discord` event: + +```javascript +socket.on('discord', (payload) => { + console.log('Event:', payload.event); + console.log('Guild:', payload.guildId); + console.log('Data:', payload.data); +}); +``` + +--- + +## Event Types + +### Message Events + +| Event | Description | Data | +|-------|-------------|------| +| `messageCreate` | New message sent | SerializedMessage | +| `messageUpdate` | Message edited | `{ old, new }` | +| `messageDelete` | Message deleted | `{ id, channelId, guildId }` | +| `messageDeleteBulk` | Multiple messages deleted | `{ ids, channelId, guildId }` | + +--- + +### Reaction Events + +| Event | Description | Data | +|-------|-------------|------| +| `messageReactionAdd` | Reaction added to message | `{ messageId, channelId, guildId, userId, reaction }` | +| `messageReactionRemove` | Reaction removed from message | `{ messageId, channelId, guildId, userId, emoji }` | +| `messageReactionRemoveAll` | All reactions removed from message | `{ messageId, channelId, guildId }` | +| `messageReactionRemoveEmoji` | All reactions of specific emoji removed | `{ messageId, channelId, guildId, emoji }` | + +--- + +### Poll Events + +| Event | Description | Data | +|-------|-------------|------| +| `messagePollVoteAdd` | User voted on a poll | `{ messageId, channelId, guildId, userId, answerId }` | +| `messagePollVoteRemove` | User removed poll vote | `{ messageId, channelId, guildId, userId, answerId }` | + +--- + +### Member Events + +| Event | Description | Data | +|-------|-------------|------| +| `guildMemberAdd` | Member joined | SerializedMember | +| `guildMemberRemove` | Member left/kicked | `{ user, guildId }` | +| `guildMemberUpdate` | Member updated | `{ old, new }` | +| `presenceUpdate` | Presence changed | `{ old, new }` | +| `userUpdate` | User globally updated | `{ old, new }` | + +--- + +### Channel Events + +| Event | Description | Data | +|-------|-------------|------| +| `channelCreate` | Channel created | SerializedChannel | +| `channelUpdate` | Channel modified | `{ old, new }` | +| `channelDelete` | Channel deleted | SerializedChannel | +| `channelPinsUpdate` | Pins updated in channel | `{ channelId, guildId, lastPinAt }` | +| `webhookUpdate` | Webhooks changed | `{ channelId, guildId }` | + +--- + +### Thread Events + +| Event | Description | Data | +|-------|-------------|------| +| `threadCreate` | Thread created | `{ thread, newlyCreated }` | +| `threadUpdate` | Thread modified | `{ old, new }` | +| `threadDelete` | Thread deleted | `{ id, guildId, parentId, name, type }` | +| `threadMembersUpdate` | Thread members changed | `{ threadId, guildId, addedMembers, removedMemberIds, memberCount }` | + +--- + +### Role Events + +| Event | Description | Data | +|-------|-------------|------| +| `roleCreate` | Role created | SerializedRole | +| `roleUpdate` | Role modified | `{ old, new }` | +| `roleDelete` | Role deleted | `{ id, guildId }` | + +--- + +### Guild Events + +| Event | Description | Data | +|-------|-------------|------| +| `guildCreate` | Bot joined guild | SerializedGuild | +| `guildUpdate` | Guild modified | `{ old, new }` | +| `guildDelete` | Bot left/removed | `{ id, name, unavailable }` | +| `guildBanAdd` | User banned | `{ guildId, user }` | +| `guildBanRemove` | User unbanned | `{ guildId, user }` | +| `guildIntegrationsUpdate` | Integrations updated | `{ guildId }` | +| `guildAuditLogEntryCreate` | Audit log entry created | SerializedAuditLogEntry | + +--- + +### Emoji Events + +| Event | Description | Data | +|-------|-------------|------| +| `emojiCreate` | Custom emoji added | SerializedEmoji | +| `emojiUpdate` | Emoji modified | `{ old, new }` | +| `emojiDelete` | Emoji removed | SerializedEmoji | + +--- + +### Sticker Events + +| Event | Description | Data | +|-------|-------------|------| +| `stickerCreate` | Custom sticker added | SerializedSticker | +| `stickerUpdate` | Sticker modified | `{ old, new }` | +| `stickerDelete` | Sticker removed | SerializedSticker | + +#### Sticker Object + +```json +{ + "id": "123456789", + "name": "cool_sticker", + "description": "A cool sticker", + "packId": null, + "type": 2, + "format": 1, + "formatName": "png", + "available": true, + "guildId": "111222333", + "user": { ... }, + "sortValue": null, + "tags": "cool", + "url": "https://cdn.discordapp.com/stickers/...", + "createdAt": "2025-01-01T00:00:00.000Z" +} +``` + +--- + +### Voice Events + +| Event | Description | Data | +|-------|-------------|------| +| `voiceStateUpdate` | Voice state changed | `{ old, new }` | + +--- + +### Stage Instance Events + +| Event | Description | Data | +|-------|-------------|------| +| `stageInstanceCreate` | Stage went live | SerializedStageInstance | +| `stageInstanceUpdate` | Stage settings changed | `{ old, new }` | +| `stageInstanceDelete` | Stage ended | SerializedStageInstance | + +#### Stage Instance Object + +```json +{ + "id": "123456789", + "guildId": "111222333", + "channelId": "444555666", + "topic": "Community Q&A Session", + "privacyLevel": 2, + "privacyLevelName": "guild_only", + "guildScheduledEventId": null, + "createdAt": "2025-01-01T12:00:00.000Z" +} +``` + +--- + +### Scheduled Event Events + +| Event | Description | Data | +|-------|-------------|------| +| `guildScheduledEventCreate` | Scheduled event created | SerializedScheduledEvent | +| `guildScheduledEventUpdate` | Scheduled event updated | `{ old, new }` | +| `guildScheduledEventDelete` | Scheduled event deleted | SerializedScheduledEvent | +| `guildScheduledEventUserAdd` | User RSVPed to event | `{ guildScheduledEventId, userId, guildId }` | +| `guildScheduledEventUserRemove` | User cancelled RSVP | `{ guildScheduledEventId, userId, guildId }` | + +#### Scheduled Event Object + +```json +{ + "id": "123456789", + "guildId": "111222333", + "channelId": "444555666", + "creatorId": "777888999", + "creator": { ... }, + "name": "Community Game Night", + "description": "Join us for fun games!", + "scheduledStartTime": "2025-01-15T20:00:00.000Z", + "scheduledEndTime": "2025-01-15T23:00:00.000Z", + "privacyLevel": 2, + "status": 1, + "statusName": "scheduled", + "entityType": 2, + "entityTypeName": "voice", + "entityId": null, + "entityMetadata": null, + "userCount": 25, + "image": "abc123", + "imageUrl": "https://cdn.discordapp.com/...", + "createdAt": "2025-01-01T00:00:00.000Z" +} +``` + +--- + +### AutoMod Events + +| Event | Description | Data | +|-------|-------------|------| +| `autoModerationRuleCreate` | AutoMod rule created | SerializedAutoModRule | +| `autoModerationRuleUpdate` | AutoMod rule updated | `{ old, new }` | +| `autoModerationRuleDelete` | AutoMod rule deleted | SerializedAutoModRule | +| `autoModerationActionExecution` | AutoMod action executed | SerializedAutoModAction | + +#### AutoMod Rule Object + +```json +{ + "id": "123456789", + "guildId": "111222333", + "name": "Block Bad Words", + "creatorId": "444555666", + "eventType": 1, + "triggerType": 1, + "triggerTypeName": "keyword", + "triggerMetadata": { + "keywordFilter": ["badword1", "badword2"], + "regexPatterns": [], + "presets": [], + "allowList": [], + "mentionTotalLimit": null, + "mentionRaidProtectionEnabled": false + }, + "actions": [ + { + "type": 1, + "typeName": "block_message", + "metadata": { + "customMessage": "This message was blocked by AutoMod." + } + } + ], + "enabled": true, + "exemptRoles": ["777888999"], + "exemptChannels": [] +} +``` + +#### AutoMod Action Execution Object + +```json +{ + "ruleId": "123456789", + "ruleTriggerType": 1, + "guildId": "111222333", + "userId": "444555666", + "channelId": "777888999", + "messageId": "101010101", + "alertSystemMessageId": null, + "content": "The message content that triggered AutoMod", + "matchedKeyword": "badword1", + "matchedContent": "badword1", + "action": { + "type": 1, + "typeName": "block_message" + } +} +``` + +--- + +### Invite Events + +| Event | Description | Data | +|-------|-------------|------| +| `inviteCreate` | Invite created | SerializedInvite | +| `inviteDelete` | Invite deleted | `{ code, channelId, guildId }` | + +#### Invite Object + +```json +{ + "code": "abc123", + "guildId": "111222333", + "channelId": "444555666", + "inviter": { ... }, + "targetUser": null, + "targetType": null, + "uses": 5, + "maxUses": 100, + "maxAge": 86400, + "temporary": false, + "createdAt": "2025-01-01T00:00:00.000Z", + "expiresAt": "2025-01-02T00:00:00.000Z", + "url": "https://discord.gg/abc123" +} +``` + +--- + +### Interaction Events + +| Event | Description | Data | +|-------|-------------|------| +| `interactionCreate` | Any interaction (slash command, button, modal, etc.) | SerializedInteraction | + +#### Interaction Object + +```json +{ + "id": "123456789", + "type": 2, + "typeName": "application_command", + "guildId": "111222333", + "channelId": "444555666", + "user": { ... }, + "member": { ... }, + "token": "interaction_token", + "applicationId": "777888999", + "commandName": "ping", + "commandId": "101010101", + "commandType": 1, + "customId": null, + "componentType": null, + "values": null, + "targetId": null, + "locale": "en-US", + "guildLocale": "en-US", + "createdAt": "2025-01-01T12:00:00.000Z" +} +``` + +--- + +### Entitlement Events + +For Discord app monetization (subscriptions, purchases). + +| Event | Description | Data | +|-------|-------------|------| +| `entitlementCreate` | User purchased subscription | SerializedEntitlement | +| `entitlementUpdate` | Subscription renewed | SerializedEntitlement | +| `entitlementDelete` | Subscription refunded/removed | SerializedEntitlement | + +--- + +### Other Events + +| Event | Description | Data | +|-------|-------------|------| +| `typingStart` | User started typing | `{ channelId, userId, timestamp, member }` | + +--- + +## Error Handling + +```javascript +socket.on('error', (error) => { + console.error('WebSocket error:', error.message); + if (error.code) { + console.error('Error code:', error.code); + } +}); +``` + +--- + +## Complete Example + +```javascript +import { io } from 'socket.io-client'; + +const socket = io('http://localhost:3000', { + auth: { apiKey: 'your_api_key' } +}); + +socket.on('connect', () => { + console.log('Connected!'); + socket.emit('subscribe', { guildIds: ['123456789'] }); +}); + +socket.on('subscribed', ({ guildIds }) => { + console.log('Subscribed to:', guildIds); +}); + +socket.on('discord', (payload) => { + switch (payload.event) { + // Message Events + case 'messageCreate': + console.log(`[${payload.data.author.username}]: ${payload.data.content}`); + break; + case 'messageReactionAdd': + console.log(`Reaction added: ${payload.data.reaction.emoji.name}`); + break; + + // Member Events + case 'guildMemberAdd': + console.log(`${payload.data.user.username} joined!`); + break; + + // Scheduled Events + case 'guildScheduledEventCreate': + console.log(`New event: ${payload.data.name}`); + break; + + // AutoMod Events + case 'autoModerationActionExecution': + console.log(`AutoMod blocked: ${payload.data.matchedKeyword}`); + break; + + // Interaction Events + case 'interactionCreate': + console.log(`Interaction: ${payload.data.typeName}`); + break; + + default: + console.log(`Event: ${payload.event}`); + } +}); + +socket.on('error', console.error); +socket.on('disconnect', (reason) => console.log('Disconnected:', reason)); +``` + +--- + +## Next Steps + +- [API Reference](api-reference.md) - REST API endpoints +- [Plugins](plugins.md) - Create plugins that listen to events +- [Network Configuration](network.md) - Connect from other devices +- [Security](security.md) - API scopes and authentication \ No newline at end of file