Skip to content

feat(discord): support per-category mention and ignore rules - #77380

Open
andrexibiza wants to merge 1 commit into
NousResearch:mainfrom
andrexibiza:feat/discord-category-rules
Open

andrexibiza wants to merge 1 commit into
NousResearch:mainfrom
andrexibiza:feat/discord-category-rules

Conversation

@andrexibiza

@andrexibiza andrexibiza commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Related #38539 #38540 #54338 #61593

What & Why

Adds per-category Discord mention/ignore rules so a bot's gating can target a Discord category and everything inside it (channels + threads) instead of enumerating every child channel ID.

Three new config keys mirror the existing channel-ID helpers (_discord_free_response_channels() et al.):

discord:
  free_response_categories:   ["<category_id>"]   # additive: free in this category + its channels/threads
  ignored_categories:         ["<category_id>"]   # subtractive: silent here, even when @mentioned
  require_mention_categories: ["<category_id>"]   # flip free→gated for this category

The headline use case from #38539: running two bots with inverse regional coverage. Bot 1 wants "free everywhere except category C" — previously inexpressible without enumerating every non-C channel ID (brittle, breaks when a channel is added). Now:

# Bot 1: free globally, gated inside category C
discord:
  require_mention: false
  require_mention_categories: ["<category_id>"]

# Bot 2: gated globally, free inside category C
discord:
  require_mention: true
  free_response_categories: ["<category_id>"]

Implementation:

  • _discord_free_response_categories(), _discord_ignored_categories(), _discord_require_mention_categories() — same parsing contract as the channel keys: list in YAML, CSV in env (DISCORD_FREE_RESPONSE_CATEGORIES, DISCORD_IGNORED_CATEGORIES, DISCORD_REQUIRE_MENTION_CATEGORIES), "*" wildcard sentinel preserved.
  • _discord_category_keys(channel) — resolves the category ID on text channels and threads (discord.py exposes Thread.category through the parent; a missing/uncached parent is treated as "no category" so the message falls through to the channel-ID rules).
  • Gating block (_handle_message + ingress admission + missed-message backfill dispatch): ignored_categories silences even when @mentioned; category-derived free/require-mention scopes augment (union) the channel-derived sets; require_mention_categories overrides a free-response exemption for that category.
  • Precedence: allowed_channelsignored_channelsignored_categoriesfree_response_channels / free_response_categoriesrequire_mention_categoriesrequire_mention global default.
  • Startup warning when a category is listed in both free_response_categories and ignored_categories (mutual exclusion, per the issue spec).
  • YAML→env bridge in _apply_yaml_config writes the three env vars following the existing channel-ID pattern.

Thread inheritance: a thread whose parent channel sits in a listed category picks up the category rule through Thread.category — no new inheritance code.

How to test

scripts/run_tests.sh tests/gateway/test_discord_category_rules.py

26 new tests cover: free-response category waives mention (channels + threads + wildcard), ignored category silences even when @mentioned (channels + threads + wildcard + beats free-response), require-mention category gates when globally free (and mentions still work, and other channels stay free, and it overrides a channel-level free-response exemption), CSV/YAML/numeric-scalar parsing parity for all three accessors, the mutual-exclusion warning, the ingress admission gate, and the config.yaml → env bridge.

Existing gating suites re-run clean (see Platforms).

What platforms tested on

  • Windows 10 native (git-bash), Python 3.12, discord.py 2.7.1.
  • tests/gateway/test_discord_category_rules.py: 26 passed.
  • tests/gateway/test_discord_channel_controls.py + test_discord_free_response.py + test_discord_allowed_channels.py + test_discord_allowed_mentions.py + test_discord_approval_mentions.py + test_discord_bot_filter.py + test_discord_roles_dm_scope.py: 46 passed.
  • Full tests/gateway/ -k discord (minus test_discord_voice_mixer.py, which fails collection on this machine from a pre-existing numpy binary mismatch): 294 passed; the only 4 failures (video/attachment send tests) reproduce identically on clean main and are unrelated to this diff.
  • python scripts/check-windows-footguns.py on both files: clean. git diff --check: clean.

Why this matters to users

Before: a Discord bot operator who wanted "respond freely everywhere except inside one category" (e.g. a second bot covering the rest of the server) had to maintain a hand-curated list of every channel ID outside that category — a list that silently drifts every time anyone adds a channel. Silencing or whitelisting a whole category likewise required enumerating its children.

After: the rule is one line — require_mention_categories: ["<category_id>"] or free_response_categories / ignored_categories. New channels inside the category inherit the rule automatically, and the config survives category renames (IDs only, per the issue's conventions). Multi-bot servers can express complementary per-region gating without drift or enumeration.

References

Add free_response_categories, ignored_categories, and
require_mention_categories config keys (env + YAML) so Discord mention
and ignore rules can target a category and everything inside it, instead
of enumerating every child channel. Mirrors the existing *_channels
helpers: CSV-in-env/list-in-YAML parsing, "*" wildcard sentinel, thread
inheritance through the parent channel's category, and a startup warning
when a category is listed in both free and ignore lists.

Precedence: allowed_channels -> ignored_channels -> ignored_categories
-> free_response_channels/free_response_categories ->
require_mention_categories -> require_mention global default.
@andrexibiza

Copy link
Copy Markdown
Contributor Author

Discord category gating is complete at head 985831e4e88981f9afb2a4a25317e601596fe197. Commit 985831e4e889 adds free_response_categories, ignored_categories, and require_mention_categories through YAML and environment parsing, resolves category IDs for channels and threads, and applies the documented precedence so an ignored category silences mentions and a require-mention category overrides a free-response exemption. This removes brittle child-channel enumeration while preserving channel fallback when a thread parent is unavailable; the same category rule follows newly added channels automatically. GitHub check rollup on this exact head: SUCCESS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/discord Discord bot adapter type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(discord): support per-category mention and ignore rules feat(discord): support per-category mention and ignore rules

2 participants