Files
meshcore-bot/config.ini.example
Adam Gessaman 8d4e960052 Web_Viewer: refactor database path handling
- Introduced a new method to retrieve the database path, allowing for a fallback to the [Bot] section if the [Web_Viewer] db_path is unset.
- Updated various parts of the codebase to utilize this new method, ensuring consistent database path resolution across the application.
- Enhanced the configuration example to clarify the optional nature of the db_path setting for the web viewer.
2026-02-11 09:47:29 -08:00

1076 lines
42 KiB
Plaintext

[Connection]
# Connection type: serial, ble, or tcp
# serial: Connect via USB serial port
# ble: Connect via Bluetooth Low Energy
# tcp: Connect via TCP/IP
connection_type = serial
# Serial port (for serial connection)
# Common ports: /dev/ttyUSB0, /dev/tty.usbserial-*, COM3 (Windows)
serial_port = /dev/ttyUSB0
# BLE device name (for BLE connection)
# Leave commented out for auto-detection, or specify exact device name
#ble_device_name = MeshCore
# TCP hostname or IP address (for TCP connection)
#hostname = 192.168.1.60
# TCP port (for TCP connection)
#tcp_port = 5000
# Connection timeout in seconds
timeout = 30
[Bot]
# Bot name for identification and logging
bot_name = MeshCoreBot
# RF Data Correlation Settings
# Time window for correlating RF data with messages (seconds)
rf_data_timeout = 15.0
# Time to wait for RF data correlation (seconds)
message_correlation_timeout = 10.0
# Enable enhanced correlation strategies
enable_enhanced_correlation = true
# Bot node ID (leave empty for auto-assignment)
node_id =
# Enable/disable bot responses
# true: Bot will respond to keywords and commands
# false: Bot will only listen and log messages
enabled = true
# Passive mode (only listen, don't respond)
# true: Bot will not send any messages
# false: Bot will respond normally
passive_mode = false
# Rate limiting in seconds between messages
# Prevents spam by limiting how often the bot can send messages
rate_limit_seconds = 10
# Bot transmission rate limit in seconds between bot messages
# Prevents bot from overwhelming the mesh network
bot_tx_rate_limit_seconds = 1.0
# Transmission delay in milliseconds before sending messages
# Helps prevent message collisions on the mesh network
# Recommended: 100-500ms for busy networks, 0 for quiet networks
tx_delay_ms = 250
# DM retry settings for improved reliability (meshcore-2.1.6+)
# Maximum number of retry attempts for failed DM sends
dm_max_retries = 3
# Maximum flood attempts (when path reset is needed)
dm_max_flood_attempts = 2
# Number of attempts before switching to flood mode
dm_flood_after = 2
# Timezone for bot operations
# Use standard timezone names (e.g., America/New_York, Europe/London, UTC)
# Leave empty to use system timezone
timezone =
# Bot location for geographic proximity calculations and astronomical data
# Default latitude for bot location (decimal degrees)
# Example: 40.7128 for New York City, 48.50 for Victoria BC
bot_latitude = 40.7128
# Default longitude for bot location (decimal degrees)
# Example: -74.0060 for New York City, -123.00 for Victoria BC
bot_longitude = -74.0060
# Maximum number of channels to fetch from MeshCore node
# MeshCore supports up to 40 channels (default: 40)
# Set to a lower value if you want to limit channel fetching for performance
max_channels = 12
# Interval-based advertising settings
# Send periodic flood adverts at specified intervals
# 0: Disabled (default)
# >0: Send flood advert every N hours
advert_interval_hours = 0
# Send startup advert when bot finishes initializing
# false: No startup advert (default)
# zero-hop: Send local broadcast advert
# flood: Send network-wide flood advert
startup_advert = false
# Auto-manage contact list when new contacts are discovered
# device: Device handles auto-addition using standard auto-discovery mode, bot manages contact list capacity (purge old contacts when near limits)
# bot: Bot automatically adds new companion contacts to device, bot manages contact list capacity (purge old contacts when near limits)
# false: Manual mode - no automatic actions, use !repeater commands to manage contacts (default)
auto_manage_contacts = false
# Database path for main bot database
# Default: meshcore_bot.db
db_path = meshcore_bot.db
[Localization]
# Language code for bot responses (en, es, es-MX, es-ES, fr, de, ja, etc.)
# Default: en (English)
# The bot will use translations from translations/{language}.json
# Supports locale codes:
# - Simple codes: en, es, fr, de, ja
# - Locale codes: es-MX (Mexican Spanish), es-ES (Spain Spanish), fr-CA (Canadian French)
# If locale-specific file not found, falls back to base language (e.g., es.json)
# If translation file is missing or key not found, falls back to English
language = en
# Path to translation files directory (relative to bot root)
# Default: translations/
translation_path = translations/
[Admin_ACL]
# Admin Access Control List (ACL) for restricted commands
# Only users with public keys listed here can execute admin commands
#
# SECURITY IMPORTANT:
# - Public keys MUST be exactly 64 hexadecimal characters (ed25519 format)
# - Invalid formats will be rejected with error logs
# - Empty or whitespace-only values disable admin access
# - Keys are case-insensitive (normalized to lowercase)
#
# Format: comma-separated list of 64-character hex public keys (without spaces)
# Example: f5d2b56d19b24412756933e917d4632e088cdd5daeadc9002feca73bf5d2b56d,1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
#
# IMPORTANT: Leave blank to disable all admin commands. Set your actual admin pubkey(s) here.
admin_pubkeys =
# Commands that require admin access (comma-separated)
# These commands will only work for users in the admin_pubkeys list
admin_commands = repeater,webviewer
[Plugin_Overrides]
# Plugin Overrides - Use alternative plugin implementations
# Format: command_name = alternative_file_name
# The alternative_file_name should be the name of a Python file (without .py extension)
# in the modules/commands/alternatives/ directory
#
# Example: To use an alternative weather plugin for international users:
# wx = wx_international
#
# This will replace the default wx command with the plugin from
# modules/commands/alternatives/wx_international.py
#
# Note: The alternative plugin must have the same 'name' metadata as the command
# it's replacing, or the override will use the alternative plugin's name instead.
[Companion_Purge]
# Enable companion contact purging
# true: Purge inactive companions when contact list is full
# false: Never purge companions (default: false for safety)
companion_purge_enabled = false
# Days since last DM to consider companion inactive
# Companions who haven't DM'd the bot in this many days may be purged
companion_dm_threshold_days = 30
# Days since last advert to consider companion inactive
# Companions who haven't adverted in this many days may be purged
companion_advert_threshold_days = 30
# Minimum days since last activity (DM or advert) before purge
# Companions must be inactive for at least this many days
companion_min_inactive_days = 30
[Jokes]
# Enable or disable the joke command
# true: Joke command is available
# false: Joke command is disabled
joke_enabled = true
# Enable seasonal joke defaults
# When enabled, October defaults to spooky jokes, December defaults to Christmas jokes
# true: Seasonal defaults are applied
# false: No seasonal defaults (always random)
seasonal_jokes = true
# Enable or disable the dad joke command
# true: Dad joke command is available
# false: Dad joke command is disabled
dadjoke_enabled = true
# Handle long jokes (over 130 characters)
# false: Fetch new jokes until we get a short one
# true: Split long jokes into multiple messages
long_jokes = false
[Joke_Command]
# Channels where joke command is allowed
# IMPORTANT: Leave commented out (or omit entirely) to use global monitor_channels (default behavior)
# If uncommented with empty value (channels = ), command will be DM-only
# Comma-separated list to restrict to specific channels (only joke command works there)
# Example: channels = #jokes,#fun (only joke command works in these channels)
# channels = general,#bot,#jokes
[DadJoke_Command]
# Channels where dadjoke command is allowed
# IMPORTANT: Leave commented out (or omit entirely) to use global monitor_channels (default behavior)
# If uncommented with empty value (channels = ), command will be DM-only
# Comma-separated list to restrict to specific channels (only dadjoke command works there)
# Example: channels = #jokes,#fun (only dadjoke command works in these channels)
# channels = general,#bot,#jokes
[Keywords]
# Available placeholders (message-based):
# {sender} - Name/ID of message sender
# {connection_info} - Path info, SNR, and RSSI combined (e.g., "01,5f (2 hops) | SNR: 15 dB | RSSI: -120 dBm")
# {snr} - Signal-to-noise ratio in dB
# {rssi} - Received signal strength indicator in dBm
# {timestamp} - Message timestamp in HH:MM:SS format
# {path} - Message routing path (e.g., "01,5f (2 hops)")
# {path_distance} - Total distance between all hops in path with locations (e.g., "123.4km (3 segs, 1 no-loc)")
# {firstlast_distance} - Distance between first and last repeater in path (e.g., "45.6km" or empty if locations missing)
# {elapsed} - Message elapsed time
#
# Available placeholders (mesh network info - same as Scheduled_Messages):
# Total counts (ever heard):
# {total_contacts} - Total number of contacts ever heard
# {total_repeaters} - Total number of repeater devices ever heard
# {total_companions} - Total number of companion devices ever heard
# {total_roomservers} - Total number of roomserver devices ever heard
# {total_sensors} - Total number of sensor devices ever heard
#
# Recent activity:
# {recent_activity_24h} - Number of unique users active in last 24 hours
#
# Active in last 30 days (last_heard):
# {total_contacts_30d} - Total contacts active (last_heard) in last 30 days
# {total_repeaters_30d} - Total repeaters active (last_heard) in last 30 days
# {total_companions_30d} - Total companions active (last_heard) in last 30 days
# {total_roomservers_30d} - Total roomservers active (last_heard) in last 30 days
# {total_sensors_30d} - Total sensors active (last_heard) in last 30 days
#
# New devices (first heard in last 7 days):
# {new_companions_7d} - New companion devices first heard in last 7 days
# {new_repeaters_7d} - New repeater devices first heard in last 7 days
# {new_roomservers_7d} - New roomserver devices first heard in last 7 days
# {new_sensors_7d} - New sensor devices first heard in last 7 days
#
# Legacy placeholders (for backward compatibility):
# {repeaters} - Same as {total_repeaters}
# {companions} - Same as {total_companions}
test = "ack @[{sender}]{phrase_part} | {connection_info} | Received at: {timestamp}"
ping = "Pong!"
pong = "Ping!"
help = "Bot Help: test (or t), ping, help, hello, cmd, advert, @string, wx, aqi, sun, moon, solar, hfcond, satpass, prefix, path, sports, dice, roll, stats | More: 'help <command>'"
# Override 'cmd' command output
# cmd = "Available commands: test (or t), ping, help, hello, cmd, advert, @string, wx, aqi, sun, moon, solar, hfcond, satpass, prefix, path, sports, dice, roll, stats"
[Channels]
# Channels to monitor (comma-separated)
# Bot will only respond to messages on these channels
# Use exact channel names as configured on your MeshCore node
monitor_channels = general,test,emergency
# Enable DM responses
# true: Bot will respond to direct messages
# false: Bot will ignore direct messages
respond_to_dms = true
[Banned_Users]
# List of banned user IDs (comma-separated)
# Bot will ignore messages from these users
banned_users =
[Scheduled_Messages]
# Scheduled message format: HHMM = channel:message
# Time format: HHMM (24-hour, no colon)
# Bot will send these messages at the specified times daily
# Example: 0800 = general:Good morning! Weather update coming soon.
#
# Available placeholders for mesh network information:
#
# Total counts (ever heard):
# {total_contacts} - Total number of contacts ever heard
# {total_repeaters} - Total number of repeater devices ever heard
# {total_companions} - Total number of companion devices ever heard
# {total_roomservers} - Total number of roomserver devices ever heard
# {total_sensors} - Total number of sensor devices ever heard
#
# Recent activity:
# {recent_activity_24h} - Number of unique users active in last 24 hours
#
# Active in last 30 days (last_heard):
# {total_contacts_30d} - Total contacts active (last_heard) in last 30 days
# {total_repeaters_30d} - Total repeaters active (last_heard) in last 30 days
# {total_companions_30d} - Total companions active (last_heard) in last 30 days
# {total_roomservers_30d} - Total roomservers active (last_heard) in last 30 days
# {total_sensors_30d} - Total sensors active (last_heard) in last 30 days
#
# New devices (first heard in last 7 days):
# {new_companions_7d} - New companion devices first heard in last 7 days
# {new_repeaters_7d} - New repeater devices first heard in last 7 days
# {new_roomservers_7d} - New roomserver devices first heard in last 7 days
# {new_sensors_7d} - New sensor devices first heard in last 7 days
#
# Legacy placeholders (for backward compatibility):
# {repeaters} - Same as {total_repeaters}
# {companions} - Same as {total_companions}
#
# Example with placeholders:
# 0800 = Public:Good morning! Network: {total_contacts} total ({total_repeaters} repeaters, {total_companions} companions). {new_repeaters_7d} new repeaters, {new_companions_7d} new companions in last 7d. {recent_activity_24h} active in 24h.
# Example with 30-day active devices and new devices in 7d:
# 0900 = Public:{total_contacts_30d} active in last 30d: ({total_repeaters_30d} repeaters, {total_companions_30d} companions). {new_repeaters_7d} new repeaters, {new_companions_7d} new companions in last 7d. {recent_activity_24h} users active in last 24h.
#0800 = Public:Good morning! Bot is online and ready.
#1200 = Public:Midday status check - all systems operational.
#1800 = Public:Evening update - bot status: Good
[Logging]
# Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL
# DEBUG: Most verbose, shows all details
# INFO: Standard logging level
# WARNING: Only warnings and errors
# ERROR: Only errors
# CRITICAL: Only critical errors
log_level = INFO
# Log file path (leave empty for console only)
# Bot will write logs to this file in addition to console
log_file = meshcore_bot.log
# Enable colored console output
# true: Use colors in console output
# false: Plain text output
colored_output = true
# MeshCore library log level (separate from bot log level)
# Controls debug output from the meshcore library itself
# Options: DEBUG, INFO, WARNING, ERROR, CRITICAL
meshcore_log_level = INFO
[Custom_Syntax]
# Custom syntax patterns for special message formats
# Format: pattern = "response_format"
# Available fields: {sender}, {phrase}, {connection_info}, {snr}, {timestamp}, {path}, {path_distance}, {firstlast_distance}
# {phrase}: The text after the trigger (for custom syntax patterns)
# {path_distance}: Total distance between all hops in path with locations (e.g., "123.4km (3 segs, 1 no-loc)")
# {firstlast_distance}: Distance between first and last repeater in path (e.g., "45.6km" or empty if locations missing)
#
# Note: The "t" command is now handled by the test command as an alias
# "t phrase" works the same as "test phrase" - both use the test response format
# Example: "t hello world" -> "ack {sender}: hello world | {connection_info}"
[External_Data]
# Weather API key (future feature)
weather_api_key =
# Weather update interval in seconds (future feature)
weather_update_interval = 3600
# Tide API key (future feature)
tide_api_key =
# Tide update interval in seconds (future feature)
tide_update_interval = 1800
# N2YO API key for satellite pass information
# Get free key at: https://www.n2yo.com/login/
n2yo_api_key =
# AirNow API key for AQI data
# Get free key at: https://docs.airnowapi.org/
airnow_api_key =
# Forecast.Solar API key for solar forecast data
# Get key at: https://forecast.solar/ (free tier works without key, paid tier for 3+ day forecasts)
# Free tier: 2-day forecast, 1-hour resolution
# Paid tier (14 EUR/year): 3-6 day forecast, 15-30 minute resolution
forecast_solar_api_key =
# Repeater prefix API URL for prefix command
# Leave empty to disable prefix command functionality
# Configure your own regional API endpoint
repeater_prefix_api_url =
# Repeater prefix cache duration in hours
# How long to cache prefix data before refreshing from API
# Recommended: 1-6 hours (data doesn't change frequently)
repeater_prefix_cache_hours = 1
[Prefix_Command]
# Enable or disable repeater geolocation in prefix command
# true: Show city names with repeaters when location data is available
# false: Show only repeater names without location information
show_repeater_locations = true
# Use reverse geocoding for coordinates without city names
# true: Automatically look up city names from GPS coordinates
# false: Only show coordinates if no city name is available
use_reverse_geocoding = true
# Hide prefix source information
# true: Hide "Source: domain.com" line from prefix command output
# false: Show source information (default)
hide_source = false
# Prefix heard time window (days)
# Number of days to look back when showing prefix results (default command behavior)
# Only repeaters heard within this window will be shown by default
# Use "prefix XX all" to show all repeaters regardless of time
prefix_heard_days = 7
# Prefix free time window (days)
# Number of days to look back when determining which prefixes are "free"
# Only repeaters heard within this window will be considered as using a prefix
# Repeaters not heard in this window will be excluded from used prefixes list
prefix_free_days = 7
# Maximum range for prefix filtering (kilometers)
# Repeaters beyond this distance from bot location will be excluded from prefix lookups
# This prevents prefix collisions from far-away repeaters from affecting local prefix availability
# Set to 0 to disable range limiting
max_prefix_range = 200
[Weather]
# NOTE: Unit settings are used by both the wx and gwx commands and Weather_Service plugin
# Weather provider selection
# Options: noaa, openmeteo
# noaa: Use NOAA API (US-focused, better for US locations, includes weather alerts)
# openmeteo: Use Open-Meteo API (global coverage, works worldwide)
# Default: noaa
weather_provider = noaa
# Default state for city name disambiguation
# When users type "wx seattle", it will search for "seattle, WA, USA"
# Use 2-letter state abbreviation (e.g., WA, CA, NY, TX)
default_state = WA
# Default country for city name disambiguation (for international weather plugin)
# Use 2-letter country code (e.g., US, CA, GB, AU)
default_country = US
# Temperature unit for weather display
# Options: fahrenheit, celsius
# Default: fahrenheit
temperature_unit = fahrenheit
# Wind speed unit for weather display
# Options: mph, kmh, ms (meters per second)
# Default: mph
wind_speed_unit = mph
# Precipitation unit for weather display
# Options: inch, mm
# Default: inch
precipitation_unit = inch
[Solar_Config]
# URL timeout for external API calls (seconds)
url_timeout = 10
# Use Zulu/UTC time for astronomical data
# true: Use 24-hour UTC format
# false: Use 12-hour local format
use_zulu_time = false
[Channels_List]
# Common hashtag channels for the region
# Format: channel_name = description
# These channels will be listed when users use the 'channels' command
# The bot will automatically add the '#' prefix when displaying channels
# General channels (no category prefix)
general = General discussion and chat
weather = Weather updates and conditions
emergency = Emergency communications and alerts
# Sub-command channels (format: subcommand.channel_name = description)
# Example: channels sports -> sports.sounders = Seattle Sounders FC
# Sports-focused channels
sports.sounders = Seattle Sounders FC
sports.kraken = Seattle Kraken
sports.mariners = Seattle Mariners
sports.seahawks = Seattle Seahawks
sports.reign = OL Reign
sports.storm = Seattle Storm
sports.huskies = Washington Huskies
# Local area channels
local.capitolhill = Capitol Hill neighborhood
local.ballard = Ballard neighborhood
local.fremont = Fremont neighborhood
local.queenanne = Queen Anne neighborhood
# Technology channels
tech.mesh = Mesh networking and technical discussions
tech.hamradio = Amateur radio and ham radio topics
tech.programming = Programming and development
tech.iot = Internet of Things projects
# Emergency-focused channels
emergency.emergency = Emergency communications and alerts
emergency.weather = Weather updates and conditions
emergency.traffic = Traffic updates and road conditions
emergency.hamradio = Amateur radio emergency net
[Sports_Command]
# Enable or disable the sports command
# true: Sports command is available
# false: Sports command is disabled
sports_enabled = true
# Default teams to show when 'sports' command is used without arguments
# Comma-separated list of team names (use lowercase)
teams = seahawks,mariners,sounders,kraken
# Channels where sports command is allowed
# IMPORTANT: Leave commented out (or omit entirely) to use global monitor_channels (default behavior)
# If uncommented with empty value (channels = ), command will be DM-only
# Comma-separated list to restrict to specific channels (only sports command works there)
# Example: channels = #sounders,#seahawks (only sports command works in these channels)
# channels = general,#bot,#sounders,#seahawks
# Channel overrides for sports command
# Format: channel_name = default_team
# Allows sports command to work in specific channels with default team shortcuts
# Example: #sounders = sounders (sports in #sounders becomes "sports sounders")
channel_override = #sounders=sounders,#seahawks=seahawks,#kraken=kraken,#mariners=mariners
# ESPN API timeout in seconds
api_timeout = 10
[Stats_Command]
# Enable or disable the stats command
# true: Stats command is available
# false: Stats command is disabled
stats_enabled = true
# Data retention settings
# Number of days to keep stats data (older data will be automatically cleaned up)
# Recommended: 7-30 days to balance storage usage with historical data
data_retention_days = 7
# Enable automatic cleanup of old stats data
# true: Automatically clean up old data based on data_retention_days
# false: Manual cleanup only
auto_cleanup = true
# Stats collection settings
# Track all incoming messages (not just commands)
# true: Record all messages for comprehensive stats
# false: Only record command executions
track_all_messages = true
# Track command execution details
# true: Record detailed command execution info
# false: Basic command tracking only
track_command_details = true
# Privacy settings
# Anonymize user data in stats
# true: Replace user IDs with anonymous identifiers
# false: Keep actual user IDs in stats
anonymize_users = false
[Path_Command]
# Geographic proximity calculation method
# simple: Use proximity to bot location (default)
# path: Use proximity to previous/next nodes in the path for more realistic routing
proximity_method = simple
# Enable path proximity fallback
# When path proximity can't be calculated (missing location data), fall back to simple proximity
# true: Fall back to bot location proximity when path data unavailable
# false: Show collision warning when path proximity unavailable
path_proximity_fallback = true
# Maximum range for geographic proximity guessing (kilometers)
# Repeaters beyond this distance will have reduced confidence or be rejected
# Set to 0 to disable range limiting
max_proximity_range = 200
# Maximum age for repeater data in path matching (days)
# Only include repeaters that have been heard within this many days
# Helps filter out stale or inactive repeaters from path decoding
# Set to 0 to disable age filtering
max_repeater_age_days = 14
# Star bias multiplier for path command
# When a contact is starred in the web viewer, multiply its selection score by this value
# Higher values = stronger preference for starred repeaters
# Default: 2.5 (starred repeaters get 2.5x their normal score)
# Set to 1.0 to disable star bias
star_bias_multiplier = 2.5
# Recency vs Proximity weighting (0.0 to 1.0)
# Controls how much recency (when last heard) vs proximity (distance) matters
# 0.0 = 100% proximity (only distance matters)
# 1.0 = 100% recency (only when last heard matters)
# 0.4 = 40% recency, 60% proximity (default - balanced for path routing)
# Lower values favor closer repeaters, higher values favor recently heard repeaters
recency_weight = 0.4
# Confidence indicator symbols for path command
# High confidence (>= 0.9): Shows when path decoding is very reliable
high_confidence_symbol = 🎯
# Medium confidence (>= 0.8): Shows when path decoding is reasonably reliable
medium_confidence_symbol = 📍
# Low confidence (< 0.8): Shows when path decoding has uncertainty
low_confidence_symbol = ❓
# Enable "p" shortcut for path command (similar to "t" for test command)
# true: Respond to just "p" or "p <path_data>" as a shortcut for "path"
# false: Only respond to "path", "decode", or "route" keywords (default)
enable_p_shortcut = false
[Hacker_Command]
# Enable or disable the hacker command
# true: Hacker command is available (responds to Linux commands with supervillain mainframe errors)
# false: Hacker command is disabled
hacker_enabled = false
[Multitest_Command]
# Response format for multitest command results
# Available fields: {sender}, {path_count}, {paths}, {listening_duration}
# {sender}: Name/ID of message sender
# {path_count}: Number of unique paths found
# {paths}: Newline-separated list of paths
# {listening_duration}: Listening window duration in seconds
# Leave empty to use default format
# Example: "Found {path_count} unique path(s) for @[{sender}]:\n{paths}"
response_format =
[Greeter_Command]
# Enable or disable the greeter command
# true: Bot will greet users on their first public channel message
# false: Greeter is disabled
enabled = false
# Channels where greetings should occur (comma-separated)
# IMPORTANT: Leave commented out (or omit entirely) to use global monitor_channels (default behavior)
# If uncommented with empty value (channels = ), command will be DM-only
# Comma-separated list to restrict to specific channels (only greeter command works there)
# Example: channels = general,welcome,newbies
# If not specified, uses the channels from [Channels] monitor_channels setting
# channels =
# Greeting message template (default for all channels)
# Available fields: {sender} - the user's name/ID
# For multi-part greetings, separate messages with pipe (|)
# Example (single): "Welcome to the mesh, @[{sender}]!"
# Example (multi-part): "Welcome to the mesh, @[{sender}]!|This is a great place to chat.|Use !help for commands."
greeting_message = Welcome to the mesh, @[{sender}]!
# Channel-specific greeting messages (optional)
# Format: channel_name:greeting_message,channel_name2:greeting_message2
# If a channel has a specific greeting, it will be used instead of the default greeting_message
# Example: Public:Welcome to Public channel, @[{sender}]!|general:Welcome to general, @[{sender}]!
# Multi-part greetings are supported per channel using pipe (|) separator
# Leave empty to use greeting_message for all channels
channel_greetings =
# Per-channel greetings (tracking behavior)
# false: Greet each user only once globally (default - user gets one greeting total)
# true: Greet each user once per channel (user can be greeted on each channel separately)
# Note: This controls tracking, not the greeting message itself. Use channel_greetings for different messages.
per_channel_greetings = false
# Include mesh network information in greeting
# true: Add mesh statistics to greeting (total contacts, repeaters, etc.)
# false: Only send the greeting message
include_mesh_info = true
# Mesh info format template
# Available fields: {total_contacts}, {repeaters}, {companions}, {recent_activity_24h}
# Example: "\n\nMesh Info: {total_contacts} contacts, {repeaters} repeaters"
# Note: Mesh info is appended to the last greeting message part
mesh_info_format = \n\nMesh Info: {total_contacts} contacts, {repeaters} repeaters, {recent_activity_24h} active in last 24h
# Rollout period in days
# When greeter is first enabled on an active mesh, this sets how many days
# to listen and mark all active users as already greeted before beginning
# to greet new users. This prevents greeting everyone on an established mesh.
# Set to 0 to disable rollout (will greet all new users immediately)
# Note: Use auto_backfill to mark historical users and shorten/eliminate rollout period
rollout_days = 7
# Auto-backfill from historical message_stats data
# true: Automatically mark all users who have posted on public channels in the past
# false: Only mark users during rollout period (default)
# This allows shortening or eliminating the rollout period by using existing data
auto_backfill = false
# Backfill lookback period in days
# Number of days to look back when auto-backfilling (0 = all time)
# Only used if auto_backfill = true
# Example: 30 = only mark users who posted in last 30 days
# Example: 0 = mark all users who have ever posted (all time)
backfill_lookback_days = 30
[Alert_Command]
# Enable or disable the alert command
# true: Alert command is available
# false: Alert command is disabled
alert_enabled = false
# PulsePoint agency IDs by county/region
# Format: agency.<county_name> = comma-separated agency IDs
# Get agency IDs from: https://web.pulsepoint.org/ or PulsePoint searchagencies API
# You can use any naming convention that makes sense for your region
# Examples: agency.county1, agency.city1, agency.region1, etc.
# Example: County agencies
# agency.county1 =
# Example: City agencies (can have multiple entries for the same region)
# agency.city1 =
# agency.city1_alias =
# Example: Combined region (all counties/agencies)
# agency.region_all =
[Announcements_Command]
# Enable or disable the announcements command
# true: Announcements command is available
# false: Announcements command is disabled (default)
enabled = false
# Announcements Access Control List (ACL)
# Only users with public keys listed here can send announcements
# This ACL automatically inherits all members from the Admin_ACL
# Format: comma-separated list of 64-character hex public keys (without spaces)
# Example: f5d2b56d19b24412756933e917d4632e088cdd5daeadc9002feca73bf5d2b56d
# Leave empty to only use Admin_ACL members
announcements_acl =
# Default channel for announcements when no channel is specified
# Announcements will be sent to this channel if no channel is provided
default_announcement_channel = Public
# Announcement cooldown in minutes
# Prevents the same announcement from being sent too frequently
# Default: 60 minutes
announcement_cooldown = 60
# Announcement triggers
# Format: announce.<trigger_name> = <announcement_text>
# Users can send: announce <trigger_name> [channel]
# If channel is not specified, uses default_announcement_channel
# Example triggers:
announce.default = This is the default announcement.
announce.bots = This is an announcement with the list of bots.
announce.other = This is a different announcement on another topic.
[Web_Viewer]
# Enable or disable the web data viewer
# SECURITY NOTE: Web viewer has NO AUTHENTICATION built-in
# Only enable if you understand the security implications
# true: web viewer is available for viewing bot data
# false: web viewer is disabled (RECOMMENDED for production)
enabled = false
# Web viewer host address
# SECURITY WARNING:
# - 127.0.0.1: Only accessible from localhost (SECURE - recommended)
# - 0.0.0.0: Accessible from any network interface (INSECURE - exposes all bot data to network)
#
# Using 0.0.0.0 will expose:
# - All messages and their contents
# - Contact list and repeater information
# - Real-time packet stream
# - Bot configuration details
#
# WITHOUT ANY AUTHENTICATION OR ENCRYPTION
host = 127.0.0.1
# Web viewer port
# Must be between 1024-65535 (non-privileged ports)
# Default: 8080
port = 8080
# Enable debug mode for the web viewer
# true: Enable Flask debug mode (auto-reload on changes)
# false: Production mode (recommended)
debug = false
# Auto-start web viewer with bot
# true: Start web viewer automatically when bot starts
# false: Start web viewer manually (recommended)
auto_start = false
# Optional: database path for web viewer. If unset, the viewer uses [Bot] db_path (recommended).
# Only set this if you use a separate database for the viewer; you will see a startup warning.
# See docs/WEB_VIEWER.md for migrating from a separate database.
# db_path = meshcore_bot.db
# Additional hashtag channels to decode in the packet stream
# The web viewer can decrypt GroupText messages from hashtag channels
# without adding them to the radio. Enter channel names (with or without #)
# as a comma-separated list. The Public channel is always included.
#
# Example: #CQ,#ARES,#EmComm
# This will allow decryption of messages on #CQ, #ARES, and #EmComm channels
# in addition to any channels configured on your radio.
#
# Note: Only hashtag channels work here - custom channels with private keys
# must be added to the radio itself.
decode_hashtag_channels =
####################################################################################################
# #
# Service Plugins Config #
# #
# Settings for non-interactive bot services and plugins (weather, alerts, etc.) #
# #
####################################################################################################
[PacketCapture]
# Enable packet capture service
# true: Capture packets and publish to MQTT/file
# false: Packet capture service disabled
enabled = false
# Output file for packet data (optional)
# Leave empty to disable file output
# Packets will be written as JSON lines
output_file =
# Verbose output (show JSON packet data in logs)
# true: Show packet data in logs
# false: Minimal logging
verbose = false
# Debug output (show detailed debugging info)
# true: Show all debugging information
# false: Standard logging
debug = false
# Owner information (for packet analyzer registration)
# Owner public key (64-character hex string)
owner_public_key =
# Owner email address
owner_email =
# Private key file path for auth token generation (fallback if device signing unavailable)
# Optional - on-device signing is preferred
# Supports 64-byte orlp format (128 hex chars) or 32-byte seed (64 hex chars)
# Required only if device doesn't support on-device signing or auth_token_method = python
# Note: If not provided and auth_token_method = python, the service will attempt to fetch
# the private key from the device automatically
private_key_path =
# Auth token signing method
# device: Try on-device signing first, fallback to Python signing (default, recommended)
# python: Use Python signing only (requires private_key_path or device key export capability)
auth_token_method = device
# Location Code (IATA code for your location)
# Used in topic templates and auth tokens
# Example: SEA, LAX, JFK, etc.
# Default: LOC (if not set)
iata = LOC
# MQTT configuration
# Enable MQTT publishing
mqtt_enabled = true
# MQTT Broker Configuration
# You can configure multiple MQTT brokers by using mqtt1_*, mqtt2_*, mqtt3_*, etc.
# Each broker can have independent settings for transport, TLS, authentication, and topics.
#
# Broker-specific options (replace N with broker number: 1, 2, 3, etc.):
# mqttN_enabled = true/false # Enable/disable this broker
# mqttN_server = hostname # MQTT broker hostname or IP
# mqttN_port = 1883 # MQTT broker port (1883 for TCP, 443 for WSS)
# mqttN_transport = tcp/websockets # Transport type (tcp or websockets)
# mqttN_use_tls = true/false # Enable TLS/SSL (required for WSS)
# mqttN_websocket_path = /mqtt # WebSocket path (for websockets transport)
# mqttN_username = # MQTT username (optional, auto-generated for auth tokens)
# mqttN_password = # MQTT password (optional, auto-generated for auth tokens)
# mqttN_use_auth_token = true/false # Use JWT auth token instead of username/password
# mqttN_token_audience = # JWT audience (usually the broker hostname)
# mqttN_topic_status = # Status topic template (uses placeholders below)
# mqttN_topic_packets = # Packets topic template (uses placeholders below)
# mqttN_topic_prefix = # Legacy topic prefix (fallback if topic_status/topic_packets not set)
# mqttN_client_id = # MQTT client ID (optional, auto-generated from bot name)
#
# Topic template placeholders:
# {IATA} - Uppercase IATA code (e.g., SEA)
# {iata} - Lowercase IATA code (e.g., sea)
# {PUBLIC_KEY} - Uppercase device public key (64 hex chars)
# {public_key} - Lowercase device public key (64 hex chars)
# MQTT Broker 1 - Let's Mesh Analyzer (US)
mqtt1_enabled = true
mqtt1_server = mqtt-us-v1.letsmesh.net
mqtt1_port = 443
mqtt1_transport = websockets
mqtt1_use_tls = true
mqtt1_use_auth_token = true
mqtt1_token_audience = mqtt-us-v1.letsmesh.net
mqtt1_topic_status = meshcore/{IATA}/{PUBLIC_KEY}/status
mqtt1_topic_packets = meshcore/{IATA}/{PUBLIC_KEY}/packets
mqtt1_websocket_path = /mqtt
mqtt1_client_id =
# MQTT Broker 2 - Let's Mesh Analyzer (EU)
mqtt2_enabled = true
mqtt2_server = mqtt-eu-v1.letsmesh.net
mqtt2_port = 443
mqtt2_transport = websockets
mqtt2_use_tls = true
mqtt2_use_auth_token = true
mqtt2_token_audience = mqtt-eu-v1.letsmesh.net
mqtt2_topic_status = meshcore/{IATA}/{PUBLIC_KEY}/status
mqtt2_topic_packets = meshcore/{IATA}/{PUBLIC_KEY}/packets
mqtt2_websocket_path = /mqtt
mqtt2_client_id =
# Stats and status publishing
# Enable stats in status messages
stats_in_status_enabled = true
# Stats refresh interval (seconds)
stats_refresh_interval = 300
# JWT renewal interval (seconds, 0 = disabled)
jwt_renewal_interval = 86400
# Health check interval (seconds, 0 = disabled)
health_check_interval = 30
# Health check grace period (consecutive failures before warning)
health_check_grace_period = 2
[MapUploader]
# Enable map uploader service
# Uploads node adverts (repeaters, room servers, sensors) to map.meshcore.dev
# CHAT adverts are skipped (only non-CHAT adverts are uploaded)
# Adverts without GPS coordinates or with any coordinate exactly 0.0 are skipped
# Uses your device's radio parameters (freq, cr, sf, bw) for all uploads
# true: Enable map uploader service
# false: Map uploader disabled (default)
enabled = false
# API endpoint URL
# The map.meshcore.dev API endpoint for uploading node adverts
# Default: https://map.meshcore.dev/api/v1/uploader/node
api_url = https://map.meshcore.dev/api/v1/uploader/node
# Private key file path (optional)
# Path to file containing device private key for signing uploads
# If not provided, the service will attempt to fetch the private key from the device
# Supports 64-byte orlp format (128 hex chars) or 32-byte seed (64 hex chars)
# Required only if device doesn't support private key export
private_key_path =
# Minimum time between re-uploads of same node (seconds)
# Prevents uploading the same node too frequently to avoid API spam
# Only nodes with timestamps newer than last upload + this interval will be uploaded
# Default: 3600 (1 hour)
min_reupload_interval = 3600
# Verbose logging
# Enable detailed debug logging including upload data and signature details
# true: Show detailed debug information (useful for troubleshooting)
# false: Standard logging (default)
verbose = false
[Weather_Service]
# Enable weather service
# true: Enable scheduled weather forecasts and alert monitoring
# false: Weather service disabled
enabled = false
# Daily weather forecast time
# Format: HH:MM (24-hour format, e.g., "6:00" for 6 AM)
# Or use "sunrise" or "sunset" for dynamic times based on your location
# Bot will send daily weather forecast at this time
weather_alarm = 6:00
# NOTE: Temperature, wind speed, and precipitation units are inherited from [Weather] section
# See [Weather] section below for temperature_unit, wind_speed_unit, and precipitation_unit settings
# Bot position for weather forecasts and alerts
# Latitude in decimal degrees
my_position_lat =
# Longitude in decimal degrees
my_position_lon =
# Channel for daily weather forecasts
# Weather forecasts will be sent to this channel
weather_channel = #weather
# Channel for weather alerts
# Weather alerts will be sent to this channel
alerts_channel = #weather
# Weather alert polling interval (milliseconds)
# How often to check for new weather alerts
# Default: 600000 (10 minutes)
poll_weather_alerts_interval = 600000
# Thunder/storm data collection interval (milliseconds)
# How often to aggregate thunder data for evaluation
# Default: 600000 (10 minutes)
# Note: Storm detection area must be configured for this to work
blitz_collection_interval = 600000
# Storm detection area (optional)
# Only report storms detected within this area
# Leave empty to disable area filtering
# Format: decimal degrees
# blitz_area_min_lat = 47.51
# blitz_area_min_lon = 15.54
# blitz_area_max_lat = 48.76
# blitz_area_max_lon = 18.62
[DiscordBridge]
# Enable Discord bridge service
# Posts MeshCore channel messages to Discord via webhooks (one-way, read-only)
# true: Enable Discord bridge
# false: Discord bridge disabled (default)
enabled = false
# IMPORTANT: Direct messages (DMs) are NEVER bridged to Discord
# This is hardcoded for privacy and cannot be changed
# Discord Rate Limits:
# - Webhooks are limited to 30 messages per minute per webhook URL
# - The service will log warnings if approaching rate limit (within 20% of exhaustion)
# - If rate limited, Discord returns HTTP 429 with retry-after header
# Avatar generation style
# Controls how user avatars are generated in Discord
# Options:
# color - Generate colored Discord default avatars based on username hash (default, no external API)
# fun-emoji - Fun emoji-style avatars from DiceBear API
# avataaars - Cartoon avatar faces from DiceBear API
# bottts - Robot avatars from DiceBear API
# identicon - Geometric patterns (GitHub-style) from DiceBear API
# pixel-art - Retro pixel-style avatars from DiceBear API
# adventurer - Adventure-themed avatars from DiceBear API
# initials - User initials on colored background from DiceBear API
# Default: color
avatar_style = color
# Channel mappings: bridge.<meshcore_channel> = <discord_webhook_url>
# Only channels explicitly listed here will be bridged to Discord
# Get webhook URLs from Discord: Channel Settings → Integrations → Webhooks → Create Webhook
#
# SECURITY WARNING: Webhook URLs contain authentication tokens
# - Keep this config file secure and never commit real webhook URLs to version control
# - Anyone with the webhook URL can post to your Discord channel
# - Rotate webhook URLs immediately if exposed
#
# Example mappings:
# bridge.general = https://discord.com/api/webhooks/123456789012345678/abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRST
# bridge.emergency = https://discord.com/api/webhooks/987654321098765432/ZYXWVUTSRQPONMLKJIHGFEDCBA0987654321zyxwvutsrqponmlkjihgf
# bridge.wx = https://discord.com/api/webhooks/111222333444555666/WeatherChannelWebhookTokenGoesHere1234567890abcdefghijklmno