CMIBot is a Discord support bot built around a channel-aware /lookup workflow.
Today it fully supports the CMI context, includes a working Jobs context, and now also supports lightweight plugin contexts for SVIS, Residence, MFM, TryMe, and TradeMe, with the active plugin decided by the Discord channel ID.
/lookupis now the primary slash command- Channel ID decides which plugin context is active
#testcan be switched live between plugin contexts through the debug command/lookup reloadis global and rebuilds caches for every plugin context/lookup statsand/lookup langstatsare context-aware and only show data for the active plugin/lookup debugshows channel routing, tracked plugins, file counts, uptime, runtime versions, cache health, memory usage, disk footprint, and active test overrides
The CMI context is fully wired and currently supports:
configlanguage|langplaceholdermaterialcommand|cmdpermission|permfaqtabcompletelangstatsstatsdebugreloadhelp
The Jobs context is now wired for Jobs locale files, Jobs translatable-word files, shared CMILib YAML search, and Jobs log-based support data.
Right now Jobs supports:
configlanguage|langplaceholdercommand|cmdpermission|permfaqhelpstatslangstatsdebugreload
These are currently not part of the Jobs scope:
materialtabcomplete
The SVIS context currently supports:
configlanguage|langcommand|cmdpermission|permlangstatsstatsdebugreloadhelp
The Residence context currently supports:
configlanguage|langplaceholdercommand|cmdpermission|permlangstatsstatsdebugreloadhelp
The MFM context currently supports:
configlanguage|langlangstatsstatsdebugreloadhelp
The TryMe context currently supports:
configlanguage|langlangstatsstatsdebugreloadhelp
The TradeMe context currently supports:
configlanguage|langlangstatsstatsdebugreloadhelp
/lookup help
/lookup config dynmap
/lookup config chat file:Chat.yml
/lookup config "mini message" mode:whole
/lookup config bluemap related:true
/lookup language home
/lookup lang "was fireballed by"
/lookup placeholder balance
/lookup material shulker
/lookup cmd balance
/lookup perm cmi.command.balance
/lookup faq refund
/lookup tabcomplete [playername] mode:whole
/lookup stats
/lookup langstats
/lookup debug
/lookup reload
/lookup help
/lookup language points
/lookup placeholder jobsr_user_points
/lookup cmd join
/lookup perm jobs.use
/lookup faq vault
/lookup stats
/lookup langstats
/lookup help
/lookup config particle
/lookup language selection
/lookup cmd gui
/lookup perm sv.worldedit.use
/lookup langstats
/lookup stats
/lookup help
/lookup config build
/lookup language invalid
/lookup placeholder owner
/lookup cmd set
/lookup perm residence.select
/lookup langstats
/lookup stats
/lookup help
/lookup config farm
/lookup language mob
/lookup langstats
/lookup stats
/lookup help
/lookup config tryme
/lookup language message
/lookup langstats
/lookup stats
/lookup help
/lookup config trade
/lookup language seller
/lookup langstats
/lookup stats
When used in a configured test channel by an admin:
/lookup debug context:cmi
/lookup debug context:jobs
/lookup debug context:auto
auto clears the manual override and returns the test channel to its default configured context.
The bot uses explicit channel IDs from .env:
DISCORD_ALLOWED_CHANNEL_IDS: every channel where the bot is allowed to runDISCORD_CMI_CHANNEL_IDS: channels that should route to the CMI contextDISCORD_JOBS_CHANNEL_IDS: channels that should route to the Jobs contextDISCORD_SVIS_CHANNEL_IDS: channels that should route to the SVIS contextDISCORD_MFM_CHANNEL_IDS: channels that should route to the MFM contextDISCORD_TRYME_CHANNEL_IDS: channels that should route to the TryMe contextDISCORD_TRADEME_CHANNEL_IDS: channels that should route to the TradeMe contextDISCORD_RESIDENCE_CHANNEL_IDS: channels that should route to the Residence contextDISCORD_TEST_CHANNEL_IDS: channels that are allowed to override context liveDISCORD_TEST_DEFAULT_CONTEXT: default context for test channels, currentlycmi
Current defaults:
#cmi:526402563847880725#jobs-reborn:526402919826849804#selectionvisualizer:714110524731686962#mobfarmmanager:713838315572559892#tryme:714111148059787285#trademe:713838991744434277#residence:526403195476639744#test:1493976695152054353
If a channel is not in DISCORD_ALLOWED_CHANNEL_IDS, the bot refuses to run there.
On npm start, the bot now warms a global cache and prints totals grouped by plugin context, for example:
Loaded 19563 entries from 74 files into the search cache.
CMI:
- config: 4499 entries from 21 YAML configuration files
- language: 4145 entries from 2 YAML locale files
- placeholder: 224 entries from 1 placeholder data file
- material: 1697 entries from 1 material data file
- command: 306 entries from 1 command data file
- permission: 778 entries from 2 permission data files
- faq: 54 entries from 1 FAQ data file
- tabcomplete: 77 entries from 1 tab-complete data file
Jobs:
- config: 347 entries from 1 YAML configuration file
- language: 2655 entries from 2 YAML locale files
- placeholder: 72 entries from 1 placeholder data file
- command: 57 entries from 1 command data file
- permission: 65 entries from 1 permission data file
- faq: 35 entries from 32 FAQ data files
Shared CMILib data:
- config: 36 entries from 1 YAML configuration file
- language: 2240 entries from 2 YAML locale files
/lookup reload rebuilds this cache globally for every configured plugin context.
For startup and reload summaries, shared CMILib config and language data is shown in its own Shared CMILib data: section at the bottom so it does not visually look like plugin-owned data inside the CMI or Jobs blocks.
/lookup debug is a compact health dashboard for the current channel context. It now reports:
- detected plugin context
- channel type and channel ID
- tracked plugin list
- known channel-route counts
- current context file counts
- uptime
- Node and
discord.jsversions - project disk size
- RAM RSS and heap usage
- global and current-context cache totals
- last cache reload timestamp
- largest cache bucket
- active test-channel overrides
- tracked plugin disk footprint
- which commands are available or unsupported in the current context
mode: exact|whole|broadlimit: 1-15for most commandssummary: true|false
configsupportsfile: <name>config,language, andlangsupportrelated: true|falsematerialuseslimit: 1-25and defaults to25
AI support is behind two gates:
OPENAI_ENABLED=true- the user must have one of the configured
AI_ROLE_IDS
If AI is disabled, summary:true is rejected cleanly.
The bot already includes:
- per-user cooldowns
- query length checks
- blocklisted filler-word rejection
- allowlisted short-token exceptions
- disallowed
@and backtick input rejection - no-mention Discord replies
- audit logging to
logs/cmibot-usage.jsonl - safe
file:filtering against indexed files only
Because file filtering uses the active plugin context, a Jobs channel cannot search CMI config files and vice versa.
At the moment, Jobs config includes:
JobsPlugin/generalConfig.yml- shared
CMILibPlugin/CMILib/config.yml
Jobs language now includes:
JobsPlugin/locale/messages_en.ymlJobsPlugin/TranslatableWords/Words_en.yml- shared
CMILibPlugin/CMILib/Translations/**/*_EN.yml
Jobs faq can now load from both:
JobsPlugin/data/faq.logJobsPlugin/data/faq/*.md
That means new markdown FAQ files dropped into the Jobs FAQ folder are picked up on /lookup reload without needing a separate conversion step.
Copy .env.example to .env and fill in the values.
DISCORD_TOKENDISCORD_APPLICATION_IDDISCORD_GUILD_IDDISCORD_ALLOWED_CHANNEL_IDSDISCORD_CMI_CHANNEL_IDSDISCORD_JOBS_CHANNEL_IDSDISCORD_SVIS_CHANNEL_IDSDISCORD_MFM_CHANNEL_IDSDISCORD_TRYME_CHANNEL_IDSDISCORD_RESIDENCE_CHANNEL_IDSDISCORD_TEST_CHANNEL_IDSDISCORD_TEST_DEFAULT_CONTEXTALLOWED_ROLE_IDSADMIN_ROLE_IDSAI_ROLE_IDS
OPENAI_ENABLEDOPENAI_API_KEYOPENAI_MODEL
DISPLAY_PATH_PREFIXDEFAULT_RESULT_LIMITLOOKUP_COOLDOWN_SECONDSSUMMARY_COOLDOWN_SECONDSQUERY_MIN_LENGTHQUERY_MAX_LENGTHQUERY_BLOCKLISTQUERY_ALLOWLISTQUERY_DEBUG_ERRORSAUDIT_LOG_PATH
LOOKUP_INCLUDE_GLOBSLOOKUP_EXCLUDE_GLOBSLANGLOOKUP_INCLUDE_GLOBSLANGLOOKUP_EXCLUDE_GLOBSPLACEHOLDER_INCLUDE_GLOBSPLACEHOLDER_EXCLUDE_GLOBSMATERIAL_INCLUDE_GLOBSMATERIAL_EXCLUDE_GLOBSCOMMAND_INCLUDE_GLOBSCOMMAND_EXCLUDE_GLOBSPERMISSION_INCLUDE_GLOBSPERMISSION_EXCLUDE_GLOBSFAQ_INCLUDE_GLOBSFAQ_EXCLUDE_GLOBSTABCOMPLETE_INCLUDE_GLOBSTABCOMPLETE_EXCLUDE_GLOBS
These are the current Jobs search scopes:
JOBS_LOOKUP_INCLUDE_GLOBSJOBS_LOOKUP_EXCLUDE_GLOBSJOBS_LANGUAGE_INCLUDE_GLOBSJOBS_LANGUAGE_EXCLUDE_GLOBSJOBS_PLACEHOLDER_INCLUDE_GLOBSJOBS_PLACEHOLDER_EXCLUDE_GLOBSJOBS_COMMAND_INCLUDE_GLOBSJOBS_COMMAND_EXCLUDE_GLOBSJOBS_PERMISSION_INCLUDE_GLOBSJOBS_PERMISSION_EXCLUDE_GLOBSJOBS_FAQ_INCLUDE_GLOBSJOBS_FAQ_EXCLUDE_GLOBS
These are the current YAML-only plugin search scopes:
SVIS_LOOKUP_INCLUDE_GLOBSSVIS_LOOKUP_EXCLUDE_GLOBSSVIS_LANGUAGE_INCLUDE_GLOBSSVIS_LANGUAGE_EXCLUDE_GLOBSSVIS_COMMAND_INCLUDE_GLOBSSVIS_COMMAND_EXCLUDE_GLOBSSVIS_PERMISSION_INCLUDE_GLOBSSVIS_PERMISSION_EXCLUDE_GLOBSRESIDENCE_PLACEHOLDER_INCLUDE_GLOBSRESIDENCE_PLACEHOLDER_EXCLUDE_GLOBSRESIDENCE_LOOKUP_INCLUDE_GLOBSRESIDENCE_LOOKUP_EXCLUDE_GLOBSRESIDENCE_LANGUAGE_INCLUDE_GLOBSRESIDENCE_LANGUAGE_EXCLUDE_GLOBSRESIDENCE_COMMAND_INCLUDE_GLOBSRESIDENCE_COMMAND_EXCLUDE_GLOBSRESIDENCE_PERMISSION_INCLUDE_GLOBSRESIDENCE_PERMISSION_EXCLUDE_GLOBSMFM_LOOKUP_INCLUDE_GLOBSMFM_LOOKUP_EXCLUDE_GLOBSMFM_LANGUAGE_INCLUDE_GLOBSMFM_LANGUAGE_EXCLUDE_GLOBSTRYME_LOOKUP_INCLUDE_GLOBSTRYME_LOOKUP_EXCLUDE_GLOBSTRYME_LANGUAGE_INCLUDE_GLOBSTRYME_LANGUAGE_EXCLUDE_GLOBSTRADEME_LOOKUP_INCLUDE_GLOBSTRADEME_LOOKUP_EXCLUDE_GLOBSTRADEME_LANGUAGE_INCLUDE_GLOBSTRADEME_LANGUAGE_EXCLUDE_GLOBS
Right now the live CMI data remains where it already works:
CMIPlugin/CMI/
CMIPlugin/data/
CMILibPlugin/CMILib/
JobsPlugin/data/
SVISPlugin/
ResidencePlugin/
MFMPlugin/
TryMePlugin/
TradeMePlugin/
src/
This refactor now supports the plugin-folder layout you created, while still keeping the data-loading rules driven by env globs.
That means:
- CMI behavior stays stable
- Jobs can be added incrementally
- new plugin contexts can follow the same folder-and-glob pattern later
The local CLI now understands plugin context too:
node src/cli.js cmi stats
node src/cli.js jobs stats
node src/cli.js cmi config --file Chat.yml dynmapIf no plugin is provided, it defaults to cmi.
npm install
npm start.envis ignored.env.exampleis trackedlogs/is ignoredCMIPlugin/CMI/cmi.sqlite.dbis ignored as runtime data
- add real Jobs YAML config data when you are ready to index more than the shared CMILib files
- extend the multi-plugin structure to more support channels beyond the current CMI, Jobs, SVIS, Residence, MFM, TryMe, and TradeMe setup