Repository navigation
Add Pulumi-managed Discord channels (forum/media support) - #208
Merged
Merged
Conversation
Add a DiscordChannel dynamic resource to src/discord.ts, driven by a new
src/config/channels.ts, so maintainers can declare Discord channels in
this repo. Each entry either adopts an existing channel by its ID and
enforces the declared settings on it in place, or creates a new channel
of type text, forum or media. Forum and media are Discord's only
thread-only channel types.
The resource uses GET/PATCH /channels/{id} and
POST /guilds/{guild}/channels through the existing discordFetch helper.
read() is side-effect free and stores the live settings as `state`;
diff() compares that state with the declared config, so drift made by
hand in Discord is corrected by the next deploy (make up runs
`pulumi up --refresh`). Only declared settings are managed, and only
those valid for the channel type: forum/media settings are never sent to
text channels and defaultForumLayout only to forums. Tag IDs read from
Discord are reused on updates so tags are not recreated, and the
REQUIRE_TAG flag is merged into the channel's other flag bits.
The resource never deletes a channel: delete() only drops the resource
from Pulumi state, because deleting a channel destroys its history.
Changing an entry's `id` or `type` replaces the resource the same way.
Discord cannot convert a text channel into a forum (the API converts
text <-> announcement only), so an existing channel can only have its
settings managed; making it thread-only means creating a new forum/media
channel (no `id`) and retiring the old channel by hand. Adopting a text
channel with type 'forum' fails with an error that says so.
Validation (npm run validate) checks snowflake formats, duplicate
entries, the 20-tag limit, type-specific settings and allowed
auto-archive durations; test-config.ts covers the validation and the
pure config <-> Discord API mapping. DISCORD_CHANNELS ships empty, so
this change is a no-op on deploy.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyDWqMwnKtkynCQNahMKxf
Pulumi PreviewClick to expand preview output |
localden
marked this pull request as ready for review
October 3, 2026 20:16
- name is optional when adopting by id (DiscordChannelConfig is now a union of adopted/new entries); it is sent in the PATCH and compared for drift only when declared, and labels fall back to the id - Name validation follows Discord's rule (1-100 code points); uppercase letters and spaces are a WARNING (printed, never failing the check) - requireTag needs tags: error on a new channel or with availableTags empty, warning on an adopted channel that declares none, and the provider refuses the PATCH when the live channel has no tags either - Topic length is validated: 1024 for text, 4096 for forum/media - position is a create-time hint only (sent on POST, excluded from drift and PATCH), documented in channels.ts and the README - Channel POST/PATCH calls send X-Audit-Log-Reason so the guild audit log shows where changes came from Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyDWqMwnKtkynCQNahMKxf
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Den Delimarsky · Slack thread
Before: Discord channels were not managed from this repo at all;
src/discord.tsonly synced roles and member role assignments. There was no way to declare a channel's settings here, and no way to get a thread-only channel (Discord's forum/media types) through the deploy.After: Maintainers declare channels in
src/config/channels.ts, keyed by Discord channel ID. An entry withidadopts the existing channel and enforces the declared settings on it in place (anything not declared is left as it is,nameincluded: it is optional when adopting, so a typo cannot rename a live channel); an entry withoutidcreates a newtext,forumormediachannel, which is the only way to get thread-only behaviour. Removing an entry never deletes the channel (it is only dropped from Pulumi state), and changing an entry'sidortypelikewise leaves the old channel alone. Existing text channels cannot be converted: Discord's API only converts text <-> announcement, sotype: 'forum'on theidof a text channel fails the deploy with an error that says to create a new forum channel instead.How: A
DiscordChanneldynamic resource insrc/discord.tsnext to the existing ones, using the shareddiscordFetchhelper and the samediscord:botToken/discord:guildIdgate:GET /channels/{id}(adopt + refresh),POST /guilds/{guild}/channels(create),PATCH /channels/{id}(settings).read()is side-effect free and stores the live settings asstate; a customdiff()compares that state with the declared config, so drift made by hand in Discord is corrected by the next deploy (make uprunspulumi up --refresh), whileid/type/guildIdchanges trigger replacement. Only type-valid fields are sent (forum/media fields never reach text channels,defaultForumLayoutonly forums), tag IDs read back from Discord are reused so updates do not recreate tags,REQUIRE_TAGis merged into the channel's other flag bits (and refused with a clear error when the channel would have no tags to pick from),positionis a create-time hint only (Discord renumbers positions, so it is sent onPOSTand excluded from drift andPATCH), and the channelPOST/PATCHcalls carryX-Audit-Log-Reason: modelcontextprotocol/access deployso the guild audit log shows where a change came from. The config <-> API mapping lives in pure functions inchannels.ts;scripts/validate-config.tschecks snowflake formats, duplicates, name length (Discord's 1-100 characters, unicode and emoji allowed) and topic length (1024 text / 4096 forum and media),requireTagwithout tags, the 20-tag limit, type-specific settings and allowed auto-archive durations, and prints aWARNING:without failing for names Discord would normalize (uppercase, spaces) and forrequireTagon an adopted channel that relies on the tags already on Discord;scripts/test-config.tsgains 35 tests for validation and mapping.npm run checkpasses locally: formatting clean, validation OK, 79 tests passed, 0 failed. The README gets a short "Discord channels" section.Notes for reviewers
POST/PATCHon channels. I could not verify its current permissions from the repo.DISCORD_CHANNELSships empty, so this PR is a no-op on deploy; it only adds the capability.nameon an adopted channel. Removing a setting from an entry stops managing it rather than resetting it.positionis applied on creation only; reorder channels in Discord.pulumiis not available in my environment, sonpm run buildcould not be run locally;npx tsc --noEmitreports only the pre-existing missing@pulumi/googleworkspacelocal SDK (needspulumi install). CI runs the real build and preview.🤖 Generated with Claude Code
https://claude.ai/code/session_01SyDWqMwnKtkynCQNahMKxf
Generated by Claude Code