Skip to content

Release v2.5.0 - #17

Merged
M1tsumi merged 12 commits into
mainfrom
next
Jul 1, 2026
Merged

M1tsumi merged 12 commits into
mainfrom
next

Conversation

@M1tsumi

@M1tsumi M1tsumi commented Jun 30, 2026 •

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added new Discord models and stronger typing across message, user, guild, audit log, moderation, and interaction data (with safe “unknown” handling).
    • Introduced Community guild configuration support (member welcome, MFA, welcome screen, incidents) and additional REST endpoints, plus a new voice channel status REST method.
    • Expanded gateway event coverage with new dedicated callbacks (including voice status/start-time and other newly dispatched updates), plus new select menu builders.
  • Bug Fixes

    • Improved gateway dispatch so more updates reach the correct dedicated callbacks (including voice channel updates).
  • Documentation

    • Updated release notes, README, and getting started versioning to 2.5.0.

M1tsumi added 6 commits June 29, 2026 20:16
- Add missing REST endpoints: Set Voice Channel Status, Get Guild Role
  Member Counts, Invite Target Users (get/update/job status)
- Add missing gateway events: VoiceChannelStatusUpdate,
  VoiceChannelStartTimeUpdate
- Update model fields across Message, Guild, GuildMember, User, Invite,
  Attachment, Reaction, MessageReference, PartialAttachment
- Add new structs: MessageSnapshot, MessageCall, SharedClientTheme,
  ReactionCountDetails, IncidentsData, WelcomeScreen, AvatarDecorationData,
  Collectibles, InviteTargetUsersJobStatus
- Add attachment editing support (description, is_spoiler) via
  PartialAttachment and editMessage attachments param
- Update PermissionBitset with setVoiceChannelStatus
- Update Gateway and AuditLog models with voice status fields
- Add GUILD_MEDIA channel type to documentation
- Add putMultipart and putFile helpers to HTTPClient
- Humanize README: restructure, replace em dashes, highlight SwiftDisc
  differentiators (actor safety, typed models, gateway lifecycle, DX)
… ApplicationCommandCreate

- Convert SlashCommandBuilder from class @unchecked Sendable to struct w/ value semantics
- Add type(.user)/type(.message) support for context menu commands
- Add nsfw, contexts, nameLocalizations, descriptionLocalizations to builder
- Add subCommand/subCommandGroup option builders, integer/number choices
- Update ApplicationCommandCreate with type, nsfw, contexts fields
- Add UserSelectMenuBuilder, RoleSelectMenuBuilder, MentionableSelectMenuBuilder
  to ComponentsBuilder (with minValues/maxValues support)
- Rewrite ShardingBot example to use ShardingGatewayManager actor API
…x Voice Channel Status events

- Add getGuildWelcomeScreen / modifyGuildWelcomeScreen REST endpoints
- Add 20+ missing gateway event callbacks (onGuildEmojisUpdate,
  onGuildStickersUpdate, onWebhooksUpdate, onIntegrationsUpdate,
  onInviteCreate/Delete, onAuditLogEntryCreate, onAutoModRuleCRUD,
  onUserUpdate, onChannelPinsUpdate, onThreadMemberUpdate,
  onThreadListSync, onGuildMembersChunk, onScheduledEventUserAdd/Remove,
  onVoiceChannelStatusUpdate, onVoiceChannelStartTimeUpdate,
  onChannelInfo)
- Fix voiceChannelStatusUpdate and voiceChannelStartTimeUpdate being
  silently dropped in EventDispatcher (no switch case existed)
- Remove all remaining break-only cases in EventDispatcher; every
  DiscordEvent now invokes its callback
- Update GAP_ANALYSIS.md to reflect current state
- .gitignore: exclude GAP_ANALYSIS.md from commits
…come, MFA, Incidents

Typed enums added:
- ChannelType (replaces raw Int on Channel.type)
- MessageType (replaces raw Int? on Message.type)
- MessageFlags OptionSet (replaces raw Int? on Message.flags)
- InteractionType (replaces raw Int on Interaction.type)
- GuildFeature (replaces [String] on Guild.features)
- GuildMemberFlags OptionSet (replaces Int? on GuildMember.flags)
- ChannelFlags OptionSet (replaces Int? on Channel.flags)
- AuditLogEventType (replaces raw Int on AuditLogEntry.action_type)
- EventPrivacyLevel (replaces raw Int on GuildScheduledEvent.privacy_level
  and StageInstance.privacy_level)
- AutoModerationRule.EventType, TriggerType, ActionType (replaces raw Int)
- Application model (new file with full fields)
- NewMemberWelcome model + GET/PATCH endpoints
- MFALevel model + GET/POST endpoints
- modifyGuildIncidentActions PUT endpoint

All call sites updated: EventDispatcher, DiscordClient, MessagePayload,
example bots, and tests.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@M1tsumi, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b998ecaa-e8e2-4fee-b4ca-09126f77dc31

📥 Commits

Reviewing files that changed from the base of the PR and between bf21ae7 and 043b6bf.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • README.md
  • Sources/SwiftDisc/SwiftDisc.docc/SwiftDisc.md
  • Tests/SwiftDiscTests/SlashCommandRouterTests.swift
  • Tests/SwiftDiscTests/TestFixtures.swift
📝 Walkthrough

Walkthrough

SwiftDisc 2.5.0 adds typed Discord models and payloads, expands gateway callbacks, introduces new REST endpoints and multipart upload helpers, refactors high-level builders, and updates examples, docs, tests, and version metadata to match the new API surface.

Changes

SwiftDisc 2.5.0 Feature Release

Layer / File(s) Summary
Typed models and new payloads
Sources/SwiftDisc/Models/*
Introduces typed enums/option sets for Discord model fields and adds new payload structs for applications, guild configuration, messages, users, invites, attachments, and snowflake markers.
Gateway event models and dispatch
Sources/SwiftDisc/Gateway/GatewayModels.swift, Sources/SwiftDisc/Internal/EventDispatcher.swift, Sources/SwiftDisc/DiscordClient.swift
Adds voice status gateway payloads and routes additional gateway events to dedicated on* callbacks.
REST surface and HTTP multipart uploads
Sources/SwiftDisc/DiscordClient.swift, Sources/SwiftDisc/REST/HTTPClient.swift
Adds guild configuration endpoints, typed request parameters, invite target-user refactoring, voice status support, and PUT multipart/file upload helpers.
High-level builders and sharding
Sources/SwiftDisc/HighLevel/SlashCommandBuilder.swift, Sources/SwiftDisc/HighLevel/ComponentsBuilder.swift, Sources/SwiftDisc/HighLevel/MessagePayload.swift, Sources/SwiftDisc/HighLevel/ShardingGatewayManager.swift
Refactors slash-command and component builders, migrates message flags to typed values, and initializes sharded event streaming earlier.
Examples, docs, tests, and version metadata
Examples/*, Tests/*, README.md, CHANGELOG.md, Sources/SwiftDisc/Internal/DiscordConfiguration.swift, Sources/SwiftDisc/URLSessionTransport.swift, Sources/SwiftDisc/SwiftDisc.docc/GettingStarted.md, .gitignore
Updates example code, documentation, tests, version strings, release notes, and ignore rules for the 2.5.0 release.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • M1tsumi/SwiftDisc#13: Related through the modifyGuildMember(..., flags:) API path that now uses GuildMemberFlags.
  • M1tsumi/SwiftDisc#14: Related through the voice gateway modeling and dispatch updates in GatewayModels.swift, EventDispatcher.swift, and DiscordClient.swift.
  • M1tsumi/SwiftDisc#15: Related through the channel stub/cache path that now uses typed channel values.

Poem

I whisker through 2.5 with a hop,
Typed flags and enums never stop.
New callbacks twinkle, neat and bright,
The sharding bunnies hum all night.
SwiftDisc blooms—sniff, snip, hooray! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the broad release-focused changeset for version 2.5.0.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch next

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/SwiftDisc/Models/Guild.swift (1)

311-346: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Update the initializer to use GuildFeature.

Line 320 still accepts [String]?, but Line 334 assigns it into self.features: [GuildFeature]?. That is a compile-time type mismatch, so this release won't build until the initializer signature is updated too.

Proposed fix
     public init(
         id: GuildID,
         name: String,
         owner_id: UserID? = nil,
         member_count: Int? = nil,
         roles: [Role]? = nil,
         emojis: [Emoji]? = nil,
         channels: [Channel]? = nil,
         members: [GuildMember]? = nil,
-        features: [String]? = nil,
+        features: [GuildFeature]? = nil,
         icon: String? = nil,
         description: String? = nil,
         banner: String? = nil,
         preferred_locale: String? = nil,
         premium_tier: Int? = nil,
         nsfw_level: Int? = nil
     ) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SwiftDisc/Models/Guild.swift` around lines 311 - 346, The Guild
initializer currently takes features as [String]? even though the Guild model
stores self.features as [GuildFeature]?, causing a type mismatch. Update the
public init signature in Guild to accept [GuildFeature]? for features, and keep
the assignment to self.features aligned with that type so the initializer
compiles cleanly.
🧹 Nitpick comments (1)
Tests/SwiftDiscTests/InteractionDecodingTests.swift (1)

33-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add one decode case for the .unknown fallback.

These assertions cover the known mappings, but not the new forward-compatibility branch in InteractionType.init(from:). A fixture with an unmapped type value would lock in the .unknown behavior this release depends on.

Suggested test
func testDecodesUnknownInteractionType() throws {
    let json = #"{"id":"1","application_id":"2","type":999,"token":"tok"}"#
    let interaction = try JSONDecoder().decode(Interaction.self, from: Data(json.utf8))
    XCTAssertEqual(interaction.type, .unknown)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Tests/SwiftDiscTests/InteractionDecodingTests.swift` around lines 33 - 54,
Add a decoding test that covers the new fallback in InteractionType.init(from:),
since the current InteractionDecodingTests only verify known type mappings. In
InteractionDecodingTests, add a fixture with an unmapped type value and decode
it through JSONDecoder into Interaction, then assert the resulting
interaction.type is .unknown so the forward-compatibility behavior is locked in.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Examples/ShardingBot.swift`:
- Around line 37-40: The `Task` consuming `manager.events` is starting before
`eventStream` is guaranteed to exist, which can race `connect()` and cause traps
or missed shard events. Move the event consumer setup in `ShardingBot` so it is
created only after `eventStream` has been initialized by `connect()`, and keep
the `for await` loop on `manager.events` or the equivalent stream access inside
that post-initialization path.

In `@README.md`:
- Line 22: The README claim in the “Everything is typed.” section overstates the
guarantee by saying invalid payloads fail at compile time. Update this wording
to reflect that the Swift structs and REST client provide typed models and
decoding/runtime validation, while malformed Discord responses still fail during
decoding at runtime. Keep the message accurate and aligned with the surrounding
description of native Swift structs and concrete REST return types.

In `@Sources/SwiftDisc/DiscordClient.swift`:
- Around line 4353-4357: The `setVoiceChannelStatus(channelId:status:)` call to
`http.put(path:body:)` leaves the generic response type unconstrained because
its result is discarded. Update this method to explicitly bind the response to
`EmptyResponse`, consistent with the other no-content HTTP calls in
`DiscordClient`, so the compiler has a concrete `T` for the request.

In `@Sources/SwiftDisc/HighLevel/ComponentsBuilder.swift`:
- Around line 231-234: The mentionable default serialization in
MentionableSelectMenuBuilder is hardcoding every default entry as type "user",
which breaks role defaults. Update defaultMentionables(_:) and the build() path
to preserve the caller’s intended type, either by adding typed overloads for
user and role defaults or by accepting explicit DefaultSelectValue entries and
mapping them directly when constructing MessageComponent.mentionableSelect.

In `@Sources/SwiftDisc/HighLevel/SlashCommandBuilder.swift`:
- Around line 203-214: subCommand and subCommandGroup currently create
OptionBuilder instances but cannot carry nested options, so any configured
children are lost when building the payload. Update OptionBuilder to store child
options and expose the same option helper methods used by SlashCommandBuilder
(for example the documented boolean-style helpers), then have
OptionBuilder.build() include those nested options for .subCommand and
.subCommandGroup. Make sure the subCommand/subCommandGroup methods in
SlashCommandBuilder continue to pass the configured builder through addOption
with the nested options preserved.
- Around line 73-103: The OptionBuilder fluent setters are returning modified
copies instead of mutating the builder passed through configure?, so changes
like choice and localization updates are lost unless the caller reassigns the
result. Update OptionBuilder’s required, nameLocalizations,
descriptionLocalizations, and all choice overloads to follow the same mutating
behavior used by the surrounding builder APIs, and ensure they update the
builder’s stored option/choices state directly so calls from SlashCommandBuilder
configuration closures persist.
- Around line 217-228: `SlashCommandBuilder.build()` is still including
slash-command-only fields for context-menu commands; update the `build()` logic
so `description`, `descriptionLocalizations`, and `options` are only populated
when `type` is `.chatInput`, and omitted entirely for `.user` and `.message`
command creates. Keep the existing `build()` entry point and its
`DiscordClient.ApplicationCommandCreate` construction, but gate those fields
alongside the existing `type` check so non-chat-input commands serialize without
them.

In `@Sources/SwiftDisc/Internal/EventDispatcher.swift`:
- Around line 273-275: Update the USER_UPDATE handling in EventDispatcher so
client.cache is refreshed before invoking onUserUpdate. In the .userUpdate case,
upsert or replace the affected user in the cache using the event payload first,
then keep the existing callback flow (`client.onUserUpdate` and the callback
invocation) unchanged so listeners see the latest user state.

In `@Sources/SwiftDisc/Models/Application.swift`:
- Around line 12-16: Update the Application model’s owner property to match the
Discord application payload schema: it should be a partial user object, not
PartialGuild. Fix the property type in Application so decoding succeeds when
owner is present, and use the existing partial user model/type that represents
users in this codebase.

In `@Sources/SwiftDisc/Models/Attachment.swift`:
- Around line 52-55: Update Attachment.isSpoiler to prefer the server-provided
is_spoiler property as the primary source of truth. In Attachment, read
is_spoiler first and only fall back to checking the spoiler bit in flags when
is_spoiler is nil. Keep the existing flag-based behavior as a fallback so the
computed property still works for older payloads.

In `@Sources/SwiftDisc/Models/AuditLog.swift`:
- Around line 3-76: `AuditLogEventType` is currently used both for decoding and
for building `DiscordClient.getGuildAuditLog(actionType:)` queries, so
`.unknown` must not be serializable as a valid filter. Update
`AuditLogEventType` to separate decode-only handling from request-facing values,
or add a helper used by `getGuildAuditLog(actionType:)` that refuses `.unknown`
before converting `rawValue` into the `action_type` query parameter. Make sure
the fix is centered around `AuditLogEventType` and the audit-log request path so
decoded unknown values cannot produce `action_type=-1`.

In `@Sources/SwiftDisc/Models/AutoModeration.swift`:
- Around line 6-46: The AutoModeration enums TriggerType, ActionType, and
EventType currently decode unknown raw values to .unknown, but that value is
then encoded back into createAutoModerationRule/modifyAutoModerationRule request
bodies as -1. Update the Codable behavior so .unknown is never serialized for
outbound Discord writes, either by splitting request/response types or by adding
encode(to:) implementations that fail or otherwise reject .unknown explicitly in
these enum definitions.

In `@Sources/SwiftDisc/Models/Channel.swift`:
- Around line 86-87: The `Channel` API change is source-breaking because the
public stored property and initializer now require `ChannelType`/`ChannelFlags?`
instead of `Int`/`Int?`. Keep backward-compatible shims in `Channel` by
preserving the old initializer and any public integer-backed properties as
deprecated overloads that bridge to the new types, and add migration notes; if
that compatibility cannot be maintained, treat the change as a major-version
bump. Apply the same compatibility approach to the related API surface
referenced in the same release, including the symbols around `Channel` and
`ChannelFlags`.

In `@Sources/SwiftDisc/Models/ScheduledEvent.swift`:
- Around line 3-13: EventPrivacyLevel currently decodes unknown raw values into
.unknown, but because it still uses synthesized encoding that case can be sent
back as -1 in REST payloads. Update EventPrivacyLevel to use a custom Encodable
implementation (or a matching Codable conformance) so only valid cases like
.guildOnly are encoded, and make .unknown fail to encode or otherwise be omitted
when used by the scheduled-event and stage-instance request models.

In `@Sources/SwiftDisc/REST/HTTPClient.swift`:
- Around line 391-408: The PUT multipart flow in HTTPClient is dropping
Discord’s error payload because executeWithRetry returns the response body as
resp.data but the caller ignores it and later passes empty Data() into
makeAPIError. Update the PUT path to keep the returned body alongside
HTTPURLResponse, then forward that actual data into makeAPIError for non-2xx
responses, matching the behavior used by the sibling request methods so
DiscordError preserves the API error details.
- Around line 386-409: putMultipart in HTTPClient.swift is missing the same
maxUploadBytes validation used by the other multipart helpers, so oversized
uploads can bypass the configured limit. Add the file-size check before building
the multipart body by following the pattern used in postMultipart,
patchMultipart, deleteMultipart, and postStickerMultipart, and make sure putFile
and updateInviteTargetUsers inherit the guard through putMultipart.

---

Outside diff comments:
In `@Sources/SwiftDisc/Models/Guild.swift`:
- Around line 311-346: The Guild initializer currently takes features as
[String]? even though the Guild model stores self.features as [GuildFeature]?,
causing a type mismatch. Update the public init signature in Guild to accept
[GuildFeature]? for features, and keep the assignment to self.features aligned
with that type so the initializer compiles cleanly.

---

Nitpick comments:
In `@Tests/SwiftDiscTests/InteractionDecodingTests.swift`:
- Around line 33-54: Add a decoding test that covers the new fallback in
InteractionType.init(from:), since the current InteractionDecodingTests only
verify known type mappings. In InteractionDecodingTests, add a fixture with an
unmapped type value and decode it through JSONDecoder into Interaction, then
assert the resulting interaction.type is .unknown so the forward-compatibility
behavior is locked in.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d103bd1e-401b-46dc-89bc-aa2aabd62a58

📥 Commits

Reviewing files that changed from the base of the PR and between d81c40f and f514642.

📒 Files selected for processing (34)
  • .gitignore
  • CHANGELOG.md
  • Examples/ComponentsV2Bot.swift
  • Examples/ShardingBot.swift
  • README.md
  • Sources/SwiftDisc/DiscordClient.swift
  • Sources/SwiftDisc/Gateway/GatewayModels.swift
  • Sources/SwiftDisc/HighLevel/ComponentsBuilder.swift
  • Sources/SwiftDisc/HighLevel/MessagePayload.swift
  • Sources/SwiftDisc/HighLevel/SlashCommandBuilder.swift
  • Sources/SwiftDisc/Internal/DiscordConfiguration.swift
  • Sources/SwiftDisc/Internal/EventDispatcher.swift
  • Sources/SwiftDisc/Models/Application.swift
  • Sources/SwiftDisc/Models/Attachment.swift
  • Sources/SwiftDisc/Models/AuditLog.swift
  • Sources/SwiftDisc/Models/AutoModeration.swift
  • Sources/SwiftDisc/Models/Channel.swift
  • Sources/SwiftDisc/Models/Files.swift
  • Sources/SwiftDisc/Models/Guild.swift
  • Sources/SwiftDisc/Models/GuildMember.swift
  • Sources/SwiftDisc/Models/Interaction.swift
  • Sources/SwiftDisc/Models/Invite.swift
  • Sources/SwiftDisc/Models/MFALevel.swift
  • Sources/SwiftDisc/Models/Message.swift
  • Sources/SwiftDisc/Models/NewMemberWelcome.swift
  • Sources/SwiftDisc/Models/PermissionBitset.swift
  • Sources/SwiftDisc/Models/ScheduledEvent.swift
  • Sources/SwiftDisc/Models/Snowflake.swift
  • Sources/SwiftDisc/Models/StageInstance.swift
  • Sources/SwiftDisc/Models/User.swift
  • Sources/SwiftDisc/REST/HTTPClient.swift
  • Sources/SwiftDisc/SwiftDisc.docc/GettingStarted.md
  • Sources/SwiftDisc/URLSessionTransport.swift
  • Tests/SwiftDiscTests/InteractionDecodingTests.swift

Comment thread Examples/ShardingBot.swift Resolved
Comment thread README.md Resolved
Comment thread Sources/SwiftDisc/DiscordClient.swift Resolved
Comment on lines +6 to +46
public enum TriggerType: Int, Codable, Sendable {
case keyword = 1
case spam = 3
case keywordPreset = 4
case mentionSpam = 5
/// Member profile trigger type. Checks if a member's profile contains disallowed keywords.
/// Introduced for member profile moderation.
case memberProfile = 6
case unknown = -1

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(Int.self)
self = TriggerType(rawValue: rawValue) ?? .unknown
}
}

/// Auto Moderation action types for rule responses.
public enum ActionType: Int, Sendable {
public enum ActionType: Int, Codable, Sendable {
case blockMessage = 1
case sendAlert = 2
case timeout = 3
case blockMemberInteraction = 4
case unknown = -1

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(Int.self)
self = ActionType(rawValue: rawValue) ?? .unknown
}
}

/// The event type for an auto moderation rule (1 = MESSAGE_SEND, 2 = MEMBER_UPDATE).
public enum EventType: Int, Codable, Sendable {
case messageSend = 1
case memberUpdate = 2
case unknown = -1

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(Int.self)
self = EventType(rawValue: rawValue) ?? .unknown
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Don’t serialize .unknown back into Discord writes.

These enums are used by createAutoModerationRule/modifyAutoModerationRule, so a decoded future value now round-trips as -1 in POST/PATCH bodies. That turns forward-compatible decoding into an invalid outbound API contract. Please either split request/response enums or make encode(to:) reject .unknown explicitly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SwiftDisc/Models/AutoModeration.swift` around lines 6 - 46, The
AutoModeration enums TriggerType, ActionType, and EventType currently decode
unknown raw values to .unknown, but that value is then encoded back into
createAutoModerationRule/modifyAutoModerationRule request bodies as -1. Update
the Codable behavior so .unknown is never serialized for outbound Discord
writes, either by splitting request/response types or by adding encode(to:)
implementations that fail or otherwise reject .unknown explicitly in these enum
definitions.

Comment on lines +86 to +87
/// The type of channel.
public let type: ChannelType

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

This is a source-breaking API change for a 2.5.0 release.

Changing the public stored property and initializer from Int/Int? to ChannelType/ChannelFlags? will break existing client code at compile time, and the same migration pattern appears across this release. Either ship this as a major version or keep deprecated compatibility shims plus migration notes before tagging 2.5.0.

Also applies to: 193-216

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SwiftDisc/Models/Channel.swift` around lines 86 - 87, The `Channel`
API change is source-breaking because the public stored property and initializer
now require `ChannelType`/`ChannelFlags?` instead of `Int`/`Int?`. Keep
backward-compatible shims in `Channel` by preserving the old initializer and any
public integer-backed properties as deprecated overloads that bridge to the new
types, and add migration notes; if that compatibility cannot be maintained,
treat the change as a major-version bump. Apply the same compatibility approach
to the related API surface referenced in the same release, including the symbols
around `Channel` and `ChannelFlags`.

Comment on lines +3 to +13
/// The privacy level of a scheduled event.
public enum EventPrivacyLevel: Int, Codable, Sendable {
case guildOnly = 2
case unknown = -1

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(Int.self)
self = EventPrivacyLevel(rawValue: rawValue) ?? .unknown
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Prevent .unknown from being encoded onto REST payloads.

EventPrivacyLevel is reused by the create/modify scheduled-event and stage-instance endpoints, so .unknown currently serializes back out as -1. That turns a decode fallback into an invalid request value on four public APIs.

Suggested fix
 public enum EventPrivacyLevel: Int, Codable, Sendable {
     case guildOnly = 2
     case unknown = -1

     public init(from decoder: Decoder) throws {
         let container = try decoder.singleValueContainer()
         let rawValue = try container.decode(Int.self)
         self = EventPrivacyLevel(rawValue: rawValue) ?? .unknown
     }
+
+    public func encode(to encoder: Encoder) throws {
+        guard self != .unknown else {
+            throw EncodingError.invalidValue(
+                self,
+                .init(
+                    codingPath: encoder.codingPath,
+                    debugDescription: "EventPrivacyLevel.unknown cannot be sent to Discord."
+                )
+            )
+        }
+        var container = encoder.singleValueContainer()
+        try container.encode(rawValue)
+    }
 }

Also applies to: 52-52

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SwiftDisc/Models/ScheduledEvent.swift` around lines 3 - 13,
EventPrivacyLevel currently decodes unknown raw values into .unknown, but
because it still uses synthesized encoding that case can be sent back as -1 in
REST payloads. Update EventPrivacyLevel to use a custom Encodable implementation
(or a matching Codable conformance) so only valid cases like .guildOnly are
encoded, and make .unknown fail to encode or otherwise be omitted when used by
the scheduled-event and stage-instance request models.

Comment thread Sources/SwiftDisc/REST/HTTPClient.swift Resolved
Comment thread Sources/SwiftDisc/REST/HTTPClient.swift Resolved
M1tsumi added 4 commits June 29, 2026 21:43
… GuildMemberFlags, MessageFlags OptionSet types
… returns, maxUploadBytes guard, ShardingGatewayManager race condition, README wording
- Rename Application phantom tag to ApplicationTag (Snowflake.swift)
- Fix GuildFeature internal case syntax (Guild.swift)
- Map [String] features to [GuildFeature] in Guild convenience init
- Fix JSONValue.double -> .number in SlashCommandBuilder
- Fix FileAttachment argument order in HTTPClient.putFile
- Use ChannelType.text enum instead of raw 0 in Cache
- Replace MessageFlags | with .union() in MessagePayload
- Fix DefaultValue type in MentionableSelectMenuBuilder
- Refactor AsyncStream init to avoid self-capture in ShardingGatewayManager
- Update ShardingBot example for changed init signature and actor isolation

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/SwiftDisc/HighLevel/ComponentsBuilder.swift`:
- Line 419: `ActionRowBuilder.add` has an inconsistent API shape because it is
`mutating` while still being used in chaining and as a side-effect call. Update
`ActionRowBuilder.add(_:)` in `ComponentsBuilder` to either return a discardable
builder result for chaining or change it to a `Void`-returning API and adjust
the `row(_:)` helper plus the example in `MessagePayload`/docs to match. Ensure
the identifiers `ActionRowBuilder.add`, `row(_:)`, and `build()` continue to
work consistently across all call sites.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a8c3ffd4-1df6-445b-9735-6ad0a8f411b8

📥 Commits

Reviewing files that changed from the base of the PR and between f514642 and bf21ae7.

📒 Files selected for processing (14)
  • Examples/ShardingBot.swift
  • README.md
  • Sources/SwiftDisc/DiscordClient.swift
  • Sources/SwiftDisc/HighLevel/ComponentsBuilder.swift
  • Sources/SwiftDisc/HighLevel/MessagePayload.swift
  • Sources/SwiftDisc/HighLevel/ShardingGatewayManager.swift
  • Sources/SwiftDisc/HighLevel/SlashCommandBuilder.swift
  • Sources/SwiftDisc/Internal/Cache.swift
  • Sources/SwiftDisc/Models/Channel.swift
  • Sources/SwiftDisc/Models/Guild.swift
  • Sources/SwiftDisc/Models/GuildMember.swift
  • Sources/SwiftDisc/Models/Message.swift
  • Sources/SwiftDisc/Models/Snowflake.swift
  • Sources/SwiftDisc/REST/HTTPClient.swift
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (9)
  • Sources/SwiftDisc/Models/GuildMember.swift
  • Sources/SwiftDisc/HighLevel/MessagePayload.swift
  • Examples/ShardingBot.swift
  • Sources/SwiftDisc/Models/Message.swift
  • Sources/SwiftDisc/REST/HTTPClient.swift
  • Sources/SwiftDisc/Models/Channel.swift
  • Sources/SwiftDisc/Models/Guild.swift
  • Sources/SwiftDisc/HighLevel/SlashCommandBuilder.swift
  • Sources/SwiftDisc/DiscordClient.swift


/// Adds a component to the action row.
public func add(_ component: MessageComponent) -> ActionRowBuilder { var c = self; c.components.append(component); return c }
public mutating func add(_ component: MessageComponent) -> ActionRowBuilder { components.append(component); return self }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP 'ActionRowBuilder\s*\(\s*\)\s*\.\s*add\b' -g '!**/build/**'
rg -nP '\.add\s*\(' -C2 Examples Tests 2>/dev/null

Repository: M1tsumi/SwiftDisc

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files | rg '^(Sources|Tests|Examples)/.*\.(swift|md)$'
rg -n "ActionRowBuilder|row\(|build\(\)|add\(" Sources Tests Examples -g '*.swift' -g '*.md'
sed -n '390,450p' Sources/SwiftDisc/HighLevel/ComponentsBuilder.swift
sed -n '1,80p' Sources/SwiftDisc/HighLevel/MessagePayload.swift

Repository: M1tsumi/SwiftDisc

Length of output: 26763


ActionRowBuilder.add needs a consistent API shape. ActionRowBuilder().add(...).build() in Sources/SwiftDisc/HighLevel/MessagePayload.swift:13 and the example in Sources/SwiftDisc/HighLevel/ComponentsBuilder.swift no longer compile with a mutating method, and row.add(...) in row(_:) now drops a non-Void result. Make it @discardableResult, or switch it to a Void-returning closure-first API and update the docs/examples.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SwiftDisc/HighLevel/ComponentsBuilder.swift` at line 419,
`ActionRowBuilder.add` has an inconsistent API shape because it is `mutating`
while still being used in chaining and as a side-effect call. Update
`ActionRowBuilder.add(_:)` in `ComponentsBuilder` to either return a discardable
builder result for chaining or change it to a `Void`-returning API and adjust
the `row(_:)` helper plus the example in `MessagePayload`/docs to match. Ensure
the identifiers `ActionRowBuilder.add`, `row(_:)`, and `build()` continue to
work consistently across all call sites.

@M1tsumi
M1tsumi merged commit d858271 into main Jul 1, 2026
8 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant