Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

 
 

Latest commit

 

History

158 Commits

Folders and files

Repository files navigation

MeshCore Discord Bridge

A bidirectional bridge between MeshCore mesh networks and Discord, with webhook-based message display, image uploads, emoji reaction mirroring, channel subscriptions, emergency alerts, and operator controls.

This fork significantly extends the original project with multi-channel routing, multi-server support, flood protection, message chunking, reaction mirroring (compatible with MeshCoreOne), webhook-based posting with per-user avatars, image uploads, and much more.


Prerequisites

  • Node.js: v18.0.0 or higher (required for top-level await and native fetch)
  • Hardware: A MeshCore node flashed with the latest client firmware, connected via USB
  • Process Manager: PM2 is recommended for 24/7 background operation

Quick Start

git clone https://github.com/tek126/MeshCoreDiscordBridge.git
cd MeshCoreDiscordBridge
npm install
node setup.js

The setup wizard will walk you through configuration step by step, including where to find each value in Discord.


Installation & Setup

1. Hardware

  1. Flash your MeshCore node and connect it via USB.
  2. Identify your serial port:
    • Linux: /dev/ttyUSB0 or /dev/ttyACM0
    • macOS: /dev/tty.usbserial-XXXX
    • Windows: COM3, COM4, etc.

2. Discord Developer Portal

  1. Open the Discord Developer Portal and create a new application.
  2. In the Bot tab, enable:
    • Presence Intent
    • Server Members Intent
    • Message Content Intent
  3. Copy your Bot Token.
  4. Go to OAuth2 > URL Generator, select bot and applications.commands, then use the generated link to invite the bot to your server.
  5. Grant the bot these server-level permissions:
    • Send Messages
    • Manage Webhooks (for webhook-based message display)
    • Add Reactions (for reaction mirroring from mesh)
    • Manage Roles (for channel subscriptions)
    • Manage Channels (for channel subscription visibility)

3. ImgBB API Key (optional, for image uploads)

  1. Create a free account at imgbb.com
  2. Go to api.imgbb.com and click "Get API Key"
  3. Copy the key into your config

4. Configuration

Run the setup wizard for guided configuration:

node setup.js

Or create config.json manually in the root directory (this file is gitignored). See Config Reference below for all options.


Running the Bridge

With PM2 (recommended)

sudo npm install pm2@latest -g
pm2 start main.js --name mesh-bridge
pm2 logs mesh-bridge

To persist across reboots:

pm2 startup
pm2 save

Direct

node main.js

Features

Mesh to Discord

  • Webhook-based display -- Mesh users appear with their own username and unique auto-generated avatar in Discord
  • Packet path display -- Each message shows the repeater path it took in small text below the message (e.g. 2 hops: [ACE1] Westmere X2 -> [C4] ESP | W). Supports 1, 2, and 3-byte hash modes with automatic repeater name resolution from the contacts list and persistent backup
  • Reaction mirroring -- Emoji reactions from MeshCoreOne are applied as native Discord reactions on the correct message (hash-compatible with MeshCoreOne's Crockford Base32 algorithm). Supports both old (emoji@[Name]hash) and new (@[Name]emoji\nhash) formats
  • Bridge prefix stripping -- Configurable list of other bridge node names to strip (e.g. txtMesh)
  • Message deduplication -- Duplicate messages from multiple bridges are detected and dropped (30-second window)
  • Node join announcements -- New mesh nodes are announced in Discord when first discovered
  • Unmapped channel labels -- Messages from mesh channels without a Discord route are labeled with the channel name
  • Language filter -- Configurable word filter with customizable response (see LANGUAGE_FILTER config)

Discord to Mesh

  • Always-forward channels -- Designated Discord channels automatically relay all messages to mesh
  • Image uploads -- Images posted in Discord are uploaded to your file host (FILE_HOST_URL), or to ImgBB if none is set, and the link is sent to mesh
  • File attachments -- Non-image files are sent with file type, size, and a link (e.g. [PDF, 1.4MB] https://unym.pics/x7Kp2a.pdf). Without a file host, the link is a TinyURL to the Discord CDN URL, which expires after about a day
  • File host contract -- POST {FILE_HOST_URL}/upload with Authorization: Bearer <token>, the raw file as the body, and an X-Filename header; respond with JSON {"url": "..."}
  • Reaction mirroring -- Discord emoji reactions on bridged messages are sent to mesh in MeshCoreOne-compatible format with correct hash
  • Mention translation -- Discord mentions (@user, @role, #channel) are resolved to readable names before forwarding to mesh
  • [D] tag -- Discord-origin messages are tagged with [D] so mesh users can identify them
  • Message chunking -- Long messages are split with n/N counters and paced to avoid mesh overload
  • Flood protection -- Per-channel rate limiting prevents accidental mesh spam

Emergency Channel

  • @everyone alerts -- First message on the emergency mesh channel pings @everyone in Discord
  • Auto-reply -- Sends a confirmation message back to mesh ("Your message has been forwarded...")
  • Reminder pings -- If no Discord user responds within a configurable time (default 5 min), pings @everyone again
  • Cooldown -- Configurable cooldown (default 30 min) before a new alert cycle can trigger

Channel Subscriptions

  • Reaction-based roles -- Users react to a message to subscribe/unsubscribe from mesh channels
  • Auto-setup -- /subscribe-setup creates roles, sets channel permissions, and posts the subscription message
  • Channel visibility -- Subscribed channels are visible only to users with the corresponding role
  • Public channels -- Public and emergency channels remain visible to everyone. On startup, routed channels that are not subscribable are made visible to @everyone, except those listed in VISIBILITY_SYNC_SKIP_CHANNEL_IDS

User Moderation

  • Admin block -- /block <username> to permanently block a mesh user from Discord forwarding
  • Community vote-block -- /voteblock <username> <reason> starts a 15-minute community vote. Requires 10% of online members (minimum 3). Admins can veto with a reaction
  • Escalating bans -- Vote-blocks escalate: 4 days, 8 days, then permanent
  • Appeals -- Blocked users can reply "appeal" on mesh once per day to send a message to Discord
  • Auto-expiry -- Vote-blocks expire automatically with notifications to both mesh and Discord

Direct Messages

  • DM forwarding -- Mesh DMs to the bridge node are forwarded as Discord DMs to a configurable user
  • DM replies -- Reply to the forwarded Discord DM to send a response back to the mesh user
  • Welcome messages -- Two-step welcome for new mesh users: a channel greeting on their first Public message, then a DM with details when their advert arrives. Both messages are configurable and can be enabled/disabled via WELCOME_ENABLED
  • DM chunking -- Long DMs are automatically split into multiple messages with part numbers

Scheduled Messages

  • Flexible scheduling -- Send recurring messages to mesh channels, Discord channels, or both
  • Multiple formats -- daily HH:MM, weekly mon HH:MM, every Nh, every Nm
  • Manage via commands -- /schedule add, /schedule list, /schedule remove

Bridge Controls

  • Auto-reconnect -- Automatically reconnects if the USB serial connection drops
  • Live config reload -- Reload config.json without restarting via /bridge reload
  • Pause/resume -- Temporarily halt all forwarding in both directions
  • Graceful shutdown -- Clean shutdown on SIGTERM/SIGINT saves message history and stops timers
  • Persistent message history -- Reaction hash tracking survives restarts (saved to disk, 24-hour expiry)
  • Contacts backup -- Repeater/node names are backed up to disk for path resolution even after device resets
  • Contact pruning -- Automatic removal of stale contacts from the radio to free slots (configurable threshold and age)

Web Configuration UI

  • Browser-based admin dashboard -- Edit config, view status/metrics, and control the bridge from a web browser
  • Discord OAuth -- Only users with admin roles or permissions can access (uses BRIDGE_ADMIN_ROLE_IDS)
  • Live status -- Auto-refreshing dashboard showing uptime, message counts, mesh connection, error counts
  • Full config editor -- Dynamic form with appropriate inputs for each value type (text, number, boolean, arrays, objects)
  • Bridge controls -- Pause/resume forwarding and reload config from the browser

Web UI Setup

  1. In the Discord Developer Portal, go to your app's OAuth2 page
  2. Add a redirect URL: http://your-host:3000/auth/callback (adjust host/port)
  3. Copy the Client Secret
  4. Add to your config.json:
    {
      "WEB_PORT": 3000,
      "WEB_CLIENT_SECRET": "your_client_secret_here",
      "WEB_CALLBACK_URL": "http://your-host:3000/auth/callback"
    }
  5. Restart the bridge -- the web UI will be available at http://your-host:3000

Commands

Use /meshhelp in Discord to see all available commands.

Command Description Permission
/send <message> Send a message to mesh Everyone
/advert Send a flood advert Everyone
/nodes List all known mesh nodes Everyone
/repeater <name> Show repeater info and stats Everyone
/meshhelp Show command help Everyone
/voteblock <user> <reason> Start a community vote to block a mesh user Everyone
/bridge status Show bridge status Everyone
/bridge pause Pause forwarding Admin
/bridge resume Resume forwarding Admin
/bridge reload Reload config.json Admin
/subscribe-setup Post channel subscription message Admin
/subscribe-refresh Update subscription message with new channels Admin
/block <username> Block a mesh user Admin
/unblock <username> Unblock a mesh user Admin
/blocklist Show blocked users with expiry info Everyone
/prune Remove stale contacts from the radio Admin
/schedule add <target> <cron> <msg> Schedule a recurring message Admin
/schedule list List scheduled messages Admin
/schedule remove <id> Remove a scheduled message Admin

Legacy prefix commands (!send, !advert) are also supported using the configured identifier.


Config Reference

Run node setup.js for guided configuration. Below is a reference of all config keys.

Core Settings

Key Description
identifier Prefix for legacy text commands (e.g. ! means !send)
CLIENT_ID Discord Application ID (Developer Portal > General Information)
DISCORD_TOKEN Discord bot token (Developer Portal > Bot)
SERIAL_PORT USB serial port for MeshCore device (e.g. /dev/ttyUSB0)
GUILD_IDS Array of Discord server IDs for slash command registration
MESH_NODE_NAME Your bridge's MeshCore device name (used for outgoing reaction targeting)

Channel Routing

Key Description
DISCORD_CHANNEL_ID Default/fallback Discord channel for messages
DISCORD_ROUTES Maps mesh channel index to Discord channel ID (mesh to Discord)
DISCORD_TO_MESH_ROUTES Maps Discord channel ID to mesh channel index (Discord to mesh)
DISCORD_ALWAYS_FORWARD_CHANNEL_IDS Discord channels that auto-forward all messages to mesh
DISCORD_CHANNEL_ID_MESHMONDAY Optional channel for messages containing #meshmonday

Image & File Uploads

Key Description
FILE_HOST_URL Base URL of a self-hosted upload service (e.g. https://unym.pics). When set, images and files are re-uploaded there
FILE_HOST_TOKEN Bearer token for the file host's POST /upload endpoint
IMGBB_API_KEY ImgBB API key for image uploads (get one at api.imgbb.com). Used when no file host is set, or as a fallback if the upload fails

Emergency Channel

Key Description
EMERGENCY_MESH_CHANNEL_IDX Mesh channel index for emergency messages
EMERGENCY_DISCORD_CHANNEL_ID Discord channel ID for emergency alerts
EMERGENCY_COOLDOWN_MINUTES Minutes before a new alert cycle can trigger (default 30)
EMERGENCY_REMINDER_MINUTES Minutes before re-pinging if no Discord reply (default 5)

Channel Subscriptions

Key Description
SUBSCRIBE_CHANNEL_ID Discord channel where the subscription message is posted
SUBSCRIBE_MESSAGE_ID Auto-populated after running /subscribe-setup
SUBSCRIBABLE_CHANNELS Array of { name, emoji, discordChannelId } objects
_SUBSCRIBE_ROLE_MAP Auto-populated role mapping (do not edit manually)
VISIBILITY_SYNC_SKIP_CHANNEL_IDS Routed channels to leave alone on startup. Otherwise any routed, non-subscribable channel hidden from @everyone is made visible

Direct Messages & Welcome

Key Description
DM_FORWARD_DISCORD_USER_ID Discord user ID to receive forwarded mesh DMs (reply to respond)
WELCOME_ENABLED Enable/disable welcome messages (true/false, default true)
WELCOME_MAX_UNKNOWN_REPEATERS Max unknown repeaters in path before skipping welcome DM (default 1)
WELCOME_CHANNEL_MESSAGE Channel greeting for new users on Public. Use {name} for username
WELCOME_DM_MESSAGE DM sent to new users when their advert is received

Scheduled Messages

Key Description
SCHEDULED_MESSAGES Auto-managed array of scheduled messages (use /schedule commands)

Bridge Configuration

Key Description
BRIDGE_PREFIXES Array of other bridge node names to strip from messages
BRIDGE_ADMIN_ROLE_IDS Discord role IDs allowed to use admin commands
NODE_ANNOUNCE_CHANNEL_ID Channel for new node announcements (falls back to default)
DEBUG Enable verbose debug logging (true/false)

Message Handling

Key Description
MESH_MAXLEN Max message length for mesh in characters (default 160)
MESH_CHUNK_DELAY_MS Delay in ms between chunked message parts (default 2500)
MESH_MIN_SEND_GAP_MS Minimum gap in ms between any two mesh transmissions, so repeaters aren't handed packets back to back (default 5000, 0 disables)
MESH_CHANNEL_SCOPES Per-channel flood scope (region) for messages the bridge sends, e.g. {"2": "troy", "1475203223852814470": "alb"}. Keys are mesh channel indexes or routed Discord channel IDs; values are public region names, written without a # (a leading # is ignored), or "*" for unscoped. Channels not listed, and DMs, use the radio's default scope. Only repeaters with that region configured will forward scoped messages. Needs companion firmware v8+ ("*" needs v12+); private $ regions aren't supported
MESH_KNOWN_SCOPES Extra region names to recognise on received messages, e.g. ["troy", "sar"]. The hop line on Discord shows the scope a message arrived with (· alb) when it's one of these or a MESH_CHANNEL_SCOPES region, · unknown scope for other scoped messages, and nothing for unscoped ones. Scope never affects what the bridge receives

Flood Protection (FLOOD_PROTECT)

Key Description
WINDOW_SECONDS Sliding window duration for rate limiting (default 15)
MAX_MESSAGES_PER_WINDOW Max messages allowed per window before cooldown (default 6)
COOLDOWN_SECONDS How long to block forwarding after limit is hit (default 60)
WARN_IN_CHANNEL Post a warning in Discord when flood protection triggers (true/false)

Language Filter (LANGUAGE_FILTER)

Key Description
ENABLED Enable/disable the language filter (default true if section present)
WORDS Array of words to filter (case-insensitive)
RESPONSE Message sent back to mesh and Discord when triggered

Contact Pruning

Key Description
CONTACT_PRUNE_INTERVAL_MINUTES How often to run auto-prune (default 60)
CONTACT_PRUNE_STALE_HOURS Remove contacts not seen for this many hours (default 48)
CONTACT_PRUNE_THRESHOLD Only prune when contact count exceeds this (default 200)

User Moderation

Key Description
BLOCKED_MESH_USERS Array of blocked users (managed via /block and /voteblock commands)

Web UI

Key Description
WEB_PORT Port for the web configuration UI (web server only starts if set)
WEB_CLIENT_SECRET Discord OAuth2 client secret (Developer Portal > OAuth2)
WEB_CALLBACK_URL OAuth redirect URI (e.g. http://your-host:3000/auth/callback)

Credits

Originally created by Hude06. This fork by tek126 with significant feature additions.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages