Skip to content

This document was written by AI and has been manually reviewed.

Rules Reference

Rules file formats

NextBridge supports JSON, YAML, and TOML rules files. Place the file in the data directory (default: data/). The first file found in this order is used:

  1. rules.json
  2. rules.yaml / rules.yml
  3. rules.toml

Converting between formats

Use the built-in convert command to translate between formats:

sh
uv run main.py convert data/rules.json data/rules.yaml
uv run main.py convert data/rules.yaml data/rules.toml

Structure

The rules file has the following structure regardless of format:

jsonc
{
  "rules": [
    {
      "id": "optional-stable-rule-id",
      // ... rule object ...
    }
  ]
}

Rules are evaluated in order for every incoming message. A message can match multiple rules.

Rule id

Each rule supports an optional id field.

  • If id is configured, NextBridge uses it directly.
  • If id is not configured, NextBridge generates a stable hash from the rule object after removing all msg blocks.

This id is used as part of the message mapping key in storage, so mappings remain stable across restarts.

Notes:

  • Changing only msg formatting does not change the auto-generated rule id.
  • Changing routing fields (for example from / to / channels) can change the auto-generated rule id.
  • If duplicate ids are detected, NextBridge auto-adjusts them with suffixes like #2, #3.

Rule types

connect

Links all listed channels bidirectionally. Any message from one channel is forwarded to all others.

jsonc
{
  "type": "connect",
  "channels": {
    "<instance_id>": {
      // ... channel address ...
    },
    "<instance_id>": {
      // ... channel address ...
    }
  },
  "msg": {
    // ... global msg config ...
  }
}

Per-channel msg override

Each channel entry may contain a "msg" key that overrides the global "msg" for messages sent to that channel. Keys from the channel-level msg win over the global msg.

json
{
  "type": "connect",
  "channels": {
    "my_dc": {
      "server_id": "111",
      "channel_id": "222",
      "msg": {
        "msg_format": "{msg}",
        "webhook_title": "{user} ({user_id}) @ {from}",
        "webhook_avatar": "{user_avatar}"
      }
    },
    "my_qq": {
      "group_id": "123456789",
      "msg": {
        "msg_format": "{user} ({user_id}): {msg}"
      }
    },
    "my_tg": {
      "chat_id": "-100987654321",
      "msg": {
        "msg_format": "{user} ({user_id}): {msg}"
      }
    }
  },
  "msg": {
    "msg_format": "{user} ({user_id}): {msg}"
  }
}

forward (default)

Routes messages from one set of channels to another (unidirectional). Omit "type" or set it to "forward".

jsonc
{
  "from": {
    "<instance_id>": {
      // ... channel address ...
    }
  },
  "to": {
    "<instance_id>": {
      // ... channel address ...
    }
  },
  "msg": {
    // ... msg config ...
  }
}

msg config

Controls how the message is formatted when sent to a target.

KeyTypeDefaultDescription
msg_formatstring"{msg}"Template string for the message text
webhook_titlestringDiscord webhook display name (Discord only)
webhook_avatarstringDiscord webhook avatar URL (Discord only)

msg_format template variables

VariableDescription
{platform}Platform name of the sender, e.g. napcat, discord
{instance_id}Instance ID of the sender as defined in config.json
{from}Alias for {instance_id}
{user}Display name of the sender
{user_id}Platform-native user ID
{user_avatar}Avatar URL of the sender (may be empty)
{msg}The message text content

Rich header tag

The <richheader> self-closing tag can be embedded anywhere in msg_format. The bridge strips it from the final text and passes its attributes to each driver for platform-native rendering.

<richheader title="..." content="..."/>
AttributeDescription
titlePrimary line — typically the sender name
contentSecondary line — typically the user ID or role

Both attributes support the same {variable} substitutions as msg_format.

Rendering by platform:

PlatformRendered as
Telegram (with rich_header_host)Small OG link-preview card above the message (avatar + title + content)
Telegram (fallback)**title** · *content* HTML bold/italic header prepended to text
Discord**title** · *content* Markdown bold/italic header prepended to text
QQ (NapCat)[title · content] plain text prepended
Feishu / DingTalk[title · content] plain text prepended

Example — rich header for Telegram with QQ-style plain text for QQ:

json
{
  "type": "connect",
  "channels": {
    "my_qq": {
      "group_id": "123456789",
      "msg": { "msg_format": "{user} ({user_id}): {msg}" }
    },
    "my_tg": {
      "chat_id": "-100987654321",
      "msg": {
        "msg_format": "<richheader title=\"{user}\" content=\"{user_id} @ {platform}\"/> {msg}"
      }
    }
  }
}

On Telegram (when rich_header_host is configured) this produces a compact card with the sender's avatar and name displayed above the message body.

Note: The rich header card is only shown for text-only messages. Messages that include media attachments fall back to the bold/italic HTML header in the caption.


Examples

json
{ "msg_format": "{user} ({user_id}): {msg}" }
Alice (123456789): hello everyone
json
{ "msg_format": "[{platform}] {user}: {msg}" }
[discord] Alice: hello everyone

Channel address keys

The channel address dict inside from, to, or channels depends on the driver:

PlatformKeys
NapCat (QQ)group_id
Discordserver_id, channel_id
Telegramchat_id
Feishuchat_id
DingTalkopen_conversation_id

See each driver's page for details.


Attachments

Media attachments (images, videos, voice, files) are automatically carried through the bridge. The bridge server downloads the file from the source and re-uploads it to the target platform — the target platform never fetches from the source URL directly. Each driver respects its configured max_file_size. If the file is too large or the download fails, a text fallback with the URL is appended to the message instead.


Security: sensitive value detection

NextBridge automatically scans outgoing message text for strings that match credentials in config.json (bot tokens, secrets, webhook URLs, passwords). If a match is found, the message is blocked and a warning is logged:

[WRN] Message to 'my_discord' blocked: text contains a sensitive value from config
      (token/secret/webhook). Possible credential leak.

This prevents accidental leakage of credentials through the bridge (e.g. if a user sends a message containing a token they copied from somewhere).