{"id":50837309,"url":"https://github.com/byigitt/discord-zig","last_synced_at":"2026-06-14T04:33:02.418Z","repository":{"id":362664735,"uuid":"1256915323","full_name":"byigitt/discord-zig","owner":"byigitt","description":"Dependency-light Discord API library for Zig: REST, gateway, interactions, caching, and message, embed, and component builders inspired by discord.js.","archived":false,"fork":false,"pushed_at":"2026-06-05T10:31:25.000Z","size":1301,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T12:20:14.804Z","etag":null,"topics":["discord","discord-api","discord-bot","gateway","library","rest-api","websocket","zig"],"latest_commit_sha":null,"homepage":null,"language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/byigitt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-02T07:39:42.000Z","updated_at":"2026-06-05T10:31:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/byigitt/discord-zig","commit_stats":null,"previous_names":["byigitt/discord-zig"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/byigitt/discord-zig","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byigitt%2Fdiscord-zig","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byigitt%2Fdiscord-zig/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byigitt%2Fdiscord-zig/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byigitt%2Fdiscord-zig/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/byigitt","download_url":"https://codeload.github.com/byigitt/discord-zig/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byigitt%2Fdiscord-zig/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34309655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["discord","discord-api","discord-bot","gateway","library","rest-api","websocket","zig"],"created_at":"2026-06-14T04:32:58.814Z","updated_at":"2026-06-14T04:33:02.406Z","avatar_url":"https://github.com/byigitt.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# discord.zig\n\n`discord.zig` is a small Zig foundation for Discord bots inspired by the most common Discord.js primitives: client configuration, intents, permissions, REST routes, messages, and Gateway payloads.\n\nTarget Zig version: `0.16.0+`.\n\nThe current focus is a fast, dependency-light core:\n\n- Discord API v10 routes and payload types\n- `Snowflake` parsing, formatting, and timestamp helpers\n- Current `GatewayIntentBits`, `Events`, `Partials`, `PermissionBits`, `ChannelType`, and `Colors` helpers with Discord.js-compatible aliases\n- Mention scanner for user, role, channel, everyone, and here mentions\n- Discord message, channel, and invite link parsers plus OAuth2 authorization URL builder\n- Discord CDN asset URL builders with explicit hash and cached model helpers\n- Permission bit constants and overwrite resolution helpers\n- Configurable cache policy with allocation-free current identity IDs, cache membership checks, explicit cache eviction, global/guild/channel cache stats, message cache limits, current-user and current-application READY hydration, user profile metadata, guild configuration metadata, guild-create thread/emoji/stage-instance/presence/voice-state hydration, unavailable guild preservation, voice-state member association retention, channel/member/thread metadata and counters, voice channel status/start-time info, top-level and non-thread guild-channel list views, guild-wide and parent-channel thread list views, all/channel/guild message list views, all/guild member list views, all/scoped role, emoji, sticker, scheduled-event, stage-instance, invite, presence, and voice-state list views, guild member-count updates, channel pin timestamp and voice-info updates, guild and channel message cleanup, channel voice-state cleanup, child-thread cleanup on channel delete, thread list sync, thread member-count updates, scheduled-event user-count updates, role colors/metadata/tags and delete cleanup on members, member removal cleanup for presence and voice state, member flags/effective permissions, channel permission overwrites, forum/media tags, default reaction emoji, sort/layout, flags, thread tags, and message reaction count updates\n- REST client built around an injectable transport, with a `std.http.Client` transport for live API calls\n- Rate-limit state that follows Discord response headers instead of hard-coding limits\n- Message REST helpers for create, fetch, list, and delete\n- Message payload builders for embeds, embed helper chaining, allowed mentions, components, sticker ids, nonce enforcement, shared client themes, and flags\n- Message state, nonce, application id/object, activity, interaction metadata, call, role subscription data, shared client theme, member, reference, forward snapshot, thread position, mention, embed, attachment, sticker item, sticker, component, poll, and reaction model parsing with count details, super-reaction metadata, cache, and related-user retention\n- Multipart message uploads for memory buffers and streaming file paths\n- Message reply, forward, edit, embed suppress/unsuppress, bulk delete, typing, crosspost, reaction add/delete-own/remove aliases, poll voter fetch, pinned-message and current paginated channel pin fetch, and thread create/edit/archive/lock/tag/list REST helpers\n- Application metadata/SKU/entitlement consume/subscription, application emoji, activity instance, role-connection metadata, and event webhook payload types/fetch aliases, OAuth2 current bot application/current authorization/token exchange/revoke helpers, current-user fetch/edit/me/guild/member/connection/application-role-connection aliases, user, current-user guild leave, optioned guild fetch/edit, direct-message channel, guild audit-log fetch, auto-moderation rule management, guild member list/search, guild prune, guild template/widget/widget-image/welcome/onboarding/incident/vanity fetch aliases and management, scheduled-event fetch aliases and management, stage instance, voice region, and voice-state fetch aliases, guild channel fetch/position/permission aliases with forum/media tags, default reaction emoji, sort/layout, and flags, channel utility helpers, global/guild sticker fetch, guild role color/icon/emoji fetch aliases and management, guild emoji/sticker fetch aliases and management, soundboard fetch aliases and management, current-user nickname compatibility, member moderation, and guild member REST helpers\n- Channel and guild invite REST helpers with optioned invite fetch and target-user fetch/update aliases\n- Lobby create/fetch/edit/delete, member management, leave, channel link, and message moderation metadata aliases\n- Channel and guild webhook management plus webhook execution REST helpers\n- Global and guild application command helpers for list, create, bulk overwrite, fetch, edit, and delete\n- Application command permission helpers for guild command permission fetch and edit\n- Slash command option and choice JSON builders\n- Button, string/user/role/mentionable/channel select menu, and modal text input component builders with disabled-state and text-input convenience helpers\n- Interaction callback helpers for replies, deferred replies, deferred updates, message updates, autocomplete results, modals, original responses, and follow-up messages\n- Gateway URL and Gateway Bot shard/session metadata fetch aliases, documented opcode/close-code helpers, identify with optional initial presence, runtime presence/activity updates, soundboard sound and channel info requests, documented shard routing helpers, and dispatch payload parsing helpers\n- Gateway session state for identify, heartbeat, resume, hello, ack, ready, and reconnect handling\n- WebSocket handshake validation and frame codec for Gateway transport implementations\n- Socket-backed secure WebSocket Gateway transport using `std.http.Client`\n- High-level Gateway runner for hello, identify, resume, heartbeat, Gateway ping measurement, reconnect backoff, runner lifecycle inspection, client readiness tracking, cache updates, and event dispatch\n- Nonblocking Gateway runtime adapter for external event loops with lifecycle inspection\n- Blocking Gateway runtime adapter for connect, reconnect delay, transport replacement, runner stepping, and lifecycle inspection\n- Typed event dispatcher for common Gateway events like message, reaction, poll vote, typing, invite, webhook update, integration, auto moderation, entitlement, soundboard, user, presence, voice state, interaction, application command permissions, guild audit log, guild ban, guild member, member chunk, role, emoji, sticker, scheduled event, stage instance, voice server, channel pin, channel, channel info, voice channel status, and thread dispatches, with generic persistent, one-shot, and clearable listeners\n- Lightweight guild/channel/author/content message collectors and guild/channel/user interaction collectors for Discord.js-style flow control with max, time, and idle limits\n\nDiscord documents that Gateway intents are required and passed as bitwise values, and that REST rate limits should be read from response headers because limits can change. This library follows those constraints instead of baking in route limits.\n\nReferences:\n\n- https://docs.discord.com/developers/topics/gateway\n- https://docs.discord.com/developers/topics/rate-limits\n- https://docs.discord.com/developers/resources/message\n- https://docs.discord.com/developers/resources/poll\n\n## Source layout\n\nThe public module root stays at `src/discord.zig`. It re-exports the stable API surface while implementation code is grouped by domain:\n\n- `src/core/`: API constants, snowflakes, intents, permissions, JSON helpers, mentions, links, assets, collections, and formatters\n- `src/models/`: Discord resource and payload models\n- `src/rest/`: REST routes, client helpers, transports, uploads, and rate-limit handling\n- `src/gateway/`: Gateway payloads, sessions, transports, runtimes, events, sharding, and WebSocket framing\n- `src/interactions/`: application commands, components, callbacks, routers, and collectors\n- `src/client/`: high-level client orchestration and cache state\n- `src/voice/`: voice gateway, audio resource, player, receive, and codec adapter helpers\n\nImplementation files are kept below 1000 lines. File and subdirectory names use short kebab-case path segments with at most two words, such as `message-polls.zig`, `app-commands.zig`, `lifecycle-events.zig`, and `cache-test.zig`, so contributors can open a file by responsibility without changing the public `discord.zig` entrypoint.\n\n## Development checks\n\n```sh\nzig build check-unused\nzig build test\nzig build\n```\n\n`zig build check-unused` runs a small repository-local scanner for unused private\nZig `@import` and member alias `const` declarations.\n\n## Example\n\n```zig\nconst std = @import(\"std\");\nconst discord = @import(\"discord\");\n\npub fn main() !void {\n    var gpa: std.heap.DebugAllocator(.{}) = .init;\n    defer _ = gpa.deinit();\n    const allocator = gpa.allocator();\n\n    var transport = discord.Rest.MemoryTransport.init(allocator, .{\n        .status = 200,\n        .body = \"{\\\"id\\\":\\\"1\\\",\\\"content\\\":\\\"pong\\\"}\",\n    });\n    defer transport.deinit();\n\n    const startup_activities = [_]discord.Gateway.Activity{discord.Gateway.Activity.init(\"discord.zig\", .watching)};\n    var client = discord.Client.init(allocator, .{\n        .token = \"Bot token\",\n        .intents = discord.Intents.defaultNonPrivileged(),\n        .presence = discord.Gateway.Presence.init(.online).withActivities(\u0026startup_activities),\n        .transport = transport.transport(),\n    });\n\n    const channel_id = try discord.Snowflake.parse(\"123456789012345678\");\n    const channel_created_ms = channel_id.timestampMillis();\n    const channel_parts = channel_id.deconstruct();\n    const first_id_at_time = try discord.Snowflake.fromTimestampMillis(channel_created_ms);\n    _ = channel_parts;\n    _ = first_id_at_time;\n    _ = try client.sendMessage(channel_id, discord.Types.CreateMessage.init(\"pong\"));\n    _ = try client.sendText(channel_id, \"pong\");\n    _ = try client.listMessagesWithOptions(channel_id, discord.Types.ListMessages.init().withLimit(25));\n}\n```\n\nEmbeds and mention controls can be sent with the same message helper:\n\n```zig\nconst fields = [_]discord.Types.EmbedField{\n    discord.Types.EmbedField.inlineField(\"Runtime\", \"Zig\"),\n};\nconst single_field = discord.Types.EmbedField.init(\"Transport\", \"REST\");\nconst embeds = [_]discord.Types.Embed{\n    discord.Types.Embed.init()\n        .withTitle(\"discord.zig\")\n        .withDescription(\"Fast bot core\")\n        .withColor(discord.Colors.Blurple)\n        .withFields(\u0026fields),\n    discord.Types.Embed.init()\n        .withTitle(\"Status\")\n        .withField(\u0026single_field),\n};\n\n_ = try client.sendMessage(\n    channel_id,\n    discord.Types.CreateMessage.init(\"release notes\")\n        .withEmbeds(\u0026embeds)\n        .withAllowedMentions(discord.Types.AllowedMentions\n            .usersOnly()\n            .withUser(\u0026user_id)\n            .repliedUser(false)),\n);\nconst reply_only_mentions = discord.Types.AllowedMentions.repliedUserOnly();\n\nvar mentions = try discord.Mentions.parse(allocator, \"hi \u003c@123\u003e \u003c#456\u003e\");\ndefer mentions.deinit();\n\n_ = try discord.Mentions.scan(\"hi \u003c@123\u003e \u003c#456\u003e\", discord.Mentions.handler(\u0026state, State.onMention));\nconst guild_text_channel = discord.Types.ChannelType.GuildText;\nconst channel_type_name = guild_text_channel.discordJsName();\nconst channel_type = discord.Types.ChannelType.fromDiscordJsName(\"GuildVoice\").?;\nconst user_mention = try discord.Mentions.userMention(allocator, user_id);\ndefer allocator.free(user_mention);\nconst role_mention = try discord.Mentions.roleMention(allocator, role_id);\ndefer allocator.free(role_mention);\nconst channel_mention = try discord.Mentions.channelMention(allocator, channel_id);\ndefer allocator.free(channel_mention);\nconst emoji_mention = try discord.Mentions.emojiMention(allocator, \"zig\", emoji_id);\ndefer allocator.free(emoji_mention);\nconst relative_time = try discord.Mentions.relativeTimestampMention(allocator, now_seconds);\ndefer allocator.free(relative_time);\nconst parsed_mention = try discord.Mentions.format(allocator, mentions.items[0]);\ndefer allocator.free(parsed_mention);\nconst user_display_name = user.displayName();\nconst user_tag = try user.tag(allocator);\ndefer allocator.free(user_tag);\nconst member_display_name = member.displayName();\n\nconst link = try discord.Links.parseMessageLink(\"https://discord.com/channels/1/2/3\");\nconst message_url = try discord.Links.messageUrl(allocator, guild_id, channel_id, message_id);\ndefer allocator.free(message_url);\nconst channel_url = try discord.Links.channelUrl(allocator, guild_id, channel_id);\ndefer allocator.free(channel_url);\nconst invite_code = try discord.Links.parseInviteCode(\"https://discord.gg/abc123\");\nconst invite_link = try discord.Links.inviteLink(allocator, invite_code);\ndefer allocator.free(invite_link);\nconst invite_url = try discord.Links.authorizationUrl(allocator, .{\n    .client_id = application_id,\n    .scopes = \u0026.{ \"bot\", \"applications.commands\" },\n    .permissions = 2048,\n});\ndefer allocator.free(invite_url);\n\nconst avatar_url = (try discord.Assets.userAvatarUrlFor(allocator, user, .{ .size = 128 })).?;\ndefer allocator.free(avatar_url);\nconst default_avatar_url = try discord.Assets.defaultUserAvatarUrlFor(allocator, user);\ndefer allocator.free(default_avatar_url);\nconst display_avatar_url = try discord.Assets.userDisplayAvatarUrlFor(allocator, user, .{ .size = 128 });\ndefer allocator.free(display_avatar_url);\nconst application_icon_url = (try discord.Assets.applicationIconUrlFor(allocator, application, .{ .size = 256 })).?;\ndefer allocator.free(application_icon_url);\nconst member_avatar_url = (try discord.Assets.guildMemberAvatarUrlFor(allocator, guild_id, member, .{})).?;\ndefer allocator.free(member_avatar_url);\nconst role_icon_url = (try discord.Assets.roleIconUrlFor(allocator, role, .{ .format = .webp })).?;\ndefer allocator.free(role_icon_url);\n```\n\nSmall files can be uploaded from memory with multipart support:\n\n```zig\nconst files = [_]discord.Types.UploadFile{\n    discord.Types.UploadFile\n        .init(\"hello.txt\", \"hello\")\n        .withContentType(\"text/plain\")\n        .withDescription(\"Greeting\"),\n};\n\n_ = try client.sendFiles(channel_id, discord.Types.CreateMessage.init(\"with attachment\"), \u0026files);\n_ = try client.sendWithFiles(channel_id, discord.Types.CreateMessage.init(\"with attachment alias\"), \u0026files);\n```\n\nLarger files can be streamed from disk paths without building the entire multipart body in memory:\n\n```zig\nconst files = [_]discord.Types.UploadFilePath{\n    discord.Types.UploadFilePath\n        .init(\"report.txt\", \"tmp/report.txt\")\n        .withContentType(\"text/plain\")\n        .withDescription(\"Daily report\"),\n};\n\n_ = try client.sendFilePaths(channel_id, discord.Types.CreateMessage.init(\"streamed attachment\"), \u0026files);\n_ = try client.sendWithFilePaths(channel_id, discord.Types.CreateMessage.init(\"streamed attachment alias\"), \u0026files);\n```\n\nMessage management helpers cover common Discord.js flows:\n\n```zig\n_ = try client.send(\n    channel_id,\n    discord.Types.CreateMessage\n        .init(\"reply\")\n        .withReference(discord.Types.MessageReference.reply(original_message_id)),\n);\n_ = try client.sendMessageWithContent(channel_id, \"plain content\");\n_ = try client.sendContent(channel_id, \"content alias\");\n_ = try client.sendText(channel_id, \"text alias\");\n_ = try client.replyMessage(channel_id, original_message_id, discord.Types.CreateMessage.init(\"reply alias\"));\n_ = try client.replyWithContent(channel_id, original_message_id, \"reply shortcut\");\n_ = try client.replyMessageWithContent(channel_id, original_message_id, \"reply alias shortcut\");\n_ = try client.replyText(channel_id, original_message_id, \"reply text shortcut\");\n_ = try client.forwardMessage(target_channel_id, source_channel_id, original_message_id);\n_ = try client.forward(target_channel_id, source_channel_id, original_message_id);\n\n_ = try client.fetchMessage(channel_id, message_id);\n_ = try client.fetchMessages(channel_id, discord.Types.ListMessages.init().withLimit(25));\n_ = try client.edit(channel_id, message_id, discord.Types.EditMessage.init().withContent(\"edited\"));\n_ = try client.editMessageWithContent(channel_id, message_id, \"edited shortcut\");\n_ = try client.editWithContent(channel_id, message_id, \"edited content alias\");\n_ = try client.editText(channel_id, message_id, \"edited text alias\");\n_ = try client.delete(channel_id, message_id);\n_ = try client.crosspost(news_channel_id, message_id);\n_ = try client.publish(news_channel_id, message_id);\n_ = try client.bulkDelete(channel_id, \u0026.{ message_id_a, message_id_b });\n_ = try client.sendTyping(channel_id);\n_ = try client.triggerTyping(channel_id);\n_ = try client.react(channel_id, message_id, \"👍\");\n_ = try client.addReaction(channel_id, message_id, \"👍\");\n_ = try client.unreact(channel_id, message_id, \"👍\");\n_ = try client.deleteOwnReaction(channel_id, message_id, \"👍\");\n_ = try client.removeReaction(channel_id, message_id, \"👍\", user_id);\n_ = try client.fetchReactions(channel_id, message_id, \"👍\", discord.Types.ListReactions.init().withLimit(25).withType(.normal));\n_ = try client.deleteAllReactions(channel_id, message_id);\n_ = try client.removeAllReactions(channel_id, message_id);\n_ = try client.clearReactionsForEmoji(channel_id, message_id, \"👍\");\n_ = try client.deleteAllReactionsForEmoji(channel_id, message_id, \"👍\");\nconst poll_answers = [_]discord.Types.PollAnswer{\n    discord.Types.PollAnswer.text(\"Zig\"),\n    discord.Types.PollAnswer.text(\"Other\"),\n};\n_ = try client.sendMessage(channel_id, discord.Types.CreateMessage.empty()\n    .withPoll(discord.Types.CreatePoll\n        .init(\"Runtime?\", \u0026poll_answers)\n        .withDuration(24)\n        .multiselect(false)));\n_ = try client.fetchPollAnswerVoters(channel_id, message_id, 1, discord.Types.ListPollAnswerVoters.init().withLimit(25));\n_ = try client.endPoll(channel_id, message_id);\n_ = try client.pin(channel_id, message_id);\n_ = try client.unpin(channel_id, message_id);\n_ = try client.fetchPinnedMessages(channel_id);\n_ = try client.fetchPins(channel_id, discord.Types.ListChannelPins.init().withLimit(25));\n_ = try client.startThreadFromMessage(channel_id, message_id, discord.Types.CreateThreadFromMessage.init(\"debug\"));\n_ = try client.startThreadFromMessageWithName(channel_id, message_id, \"debug\");\n_ = try client.editChannel(thread_id, discord.Types.EditChannel.init().archivedState(true).lockedState(true));\n_ = try client.joinThread(thread_id);\n_ = try client.addThreadMember(thread_id, user_id);\n_ = try client.fetchThreadMember(thread_id, user_id);\n_ = try client.fetchThreadMembers(thread_id);\n_ = try client.fetchThreadMembersWithOptions(thread_id, discord.Types.ListThreadMembers.init()\n    .withMemberExpansion(true)\n    .afterMember(user_id)\n    .withLimit(100));\n_ = try client.fetchActiveThreads(guild_id);\n_ = try client.fetchPublicArchivedThreads(channel_id, discord.Types.ListArchivedThreads.init().withLimit(25));\n_ = try client.fetchPrivateArchivedThreads(channel_id, discord.Types.ListArchivedThreads.init().withLimit(25));\n_ = try client.fetchJoinedPrivateArchivedThreads(channel_id, discord.Types.ListArchivedThreads.init().withLimit(25));\n_ = try client.createDefaultInvite(channel_id);\n_ = try client.createInviteWithMaxUses(channel_id, 1);\n_ = try client.createInviteWithMaxAge(channel_id, 3600);\n_ = try client.createUniqueInvite(channel_id);\n```\n\nBasic entity fetch helpers are available for common cache misses:\n\n```zig\n_ = try client.fetchCurrentApplication();\n_ = try client.fetchCurrentBotApplication();\n_ = try client.editCurrentApplication(discord.Types.EditCurrentApplication.init().withDescription(\"Fast Zig bot\"));\n_ = try client.setCurrentApplicationDescription(\"Fast Zig bot\");\n_ = try client.setCurrentApplicationIcon(\"data:image/png;base64,...\");\n_ = try client.clearCurrentApplicationIcon();\n_ = try client.setCurrentApplicationCoverImage(\"data:image/png;base64,...\");\n_ = try client.clearCurrentApplicationCoverImage();\n_ = try client.fetchCurrentUser();\n_ = try client.fetchMe();\n_ = try client.fetchUser(user_id);\n_ = try client.createDm(user_id);\n_ = try client.createDM(user_id);\n_ = try client.editCurrentUser(discord.Types.EditCurrentUser.init().withUsername(\"zigbot\"));\n_ = try client.setCurrentUsername(\"zigbot\");\n_ = try client.setCurrentUserAvatar(\"data:image/png;base64,...\");\n_ = try client.clearCurrentUserAvatar();\n_ = try client.setCurrentUserBanner(\"data:image/png;base64,...\");\n_ = try client.clearCurrentUserBanner();\n_ = try client.editMe(discord.Types.EditCurrentUser.init().withUsername(\"aliasbot\"));\n_ = try client.fetchCurrentUserGuilds(discord.Types.ListCurrentUserGuilds.init().withLimit(100).withCounts(true));\n_ = try client.fetchCurrentUserGuildMember(guild_id);\n_ = try client.fetchCurrentUserConnections();\n_ = try client.fetchCurrentAuthorization(\"Bearer user-oauth-token\");\n_ = try client.exchangeOAuth2Token(\"Basic base64-client-credentials\", discord.Types.OAuth2TokenRequest.authorizationCode(\"oauth-code\")\n    .withRedirectUri(\"https://example.com/callback\"));\n_ = try client.revokeOAuth2Token(\"Basic base64-client-credentials\", discord.Types.OAuth2TokenRevocation.init(\"refresh-token\"));\n_ = try client.fetchCurrentUserApplicationRoleConnection(\"Bearer user-oauth-token\", application_id);\n_ = try client.setCurrentUserApplicationRoleConnection(\"Bearer user-oauth-token\", application_id, discord.Types.UpdateApplicationRoleConnection.init()\n    .withPlatformName(\"zig league\")\n    .withMetadata(\u0026.{.{ .key = \"level\", .value = \"42\" }}));\n_ = try client.fetchGuild(guild_id);\n_ = try client.fetchGuildWithOptions(guild_id, discord.Types.GetGuild.init().withCounts(true));\n_ = try client.fetchGuildPreview(guild_id);\n_ = try client.editGuild(guild_id, discord.Types.EditGuild.init().withDescription(\"Built with Zig\"));\n_ = try client.fetchChannel(channel_id);\n_ = try client.fetchMember(guild_id, user_id);\n_ = try client.fetchAuditLog(guild_id, discord.Types.ListAuditLog.init().withLimit(25));\n_ = try client.fetchGuildIntegrations(guild_id);\n_ = try client.deleteGuildIntegration(guild_id, integration_id);\n_ = try client.fetchApplicationSkus(application_id);\nconst role_metadata = [_]discord.Types.ApplicationRoleConnectionMetadata{\n    discord.Types.ApplicationRoleConnectionMetadata.init(.integer_greater_than_or_equal, \"level\", \"Level\", \"Player level\"),\n};\n_ = try client.fetchApplicationRoleConnectionMetadataRecords(application_id);\n_ = try client.setApplicationRoleConnectionMetadataRecords(\n    application_id,\n    discord.Types.UpdateApplicationRoleConnectionMetadataRecords.init(\u0026role_metadata),\n);\n_ = try client.fetchApplicationActivityInstance(application_id, \"activity-instance-id\");\n_ = try client.fetchApplicationEmojis(application_id);\n_ = try client.fetchApplicationEmoji(application_id, emoji_id);\n_ = try client.createApplicationEmoji(application_id, discord.Types.CreateApplicationEmoji.init(\"zig\", \"data:image/webp;base64,...\"));\n_ = try client.createApplicationEmojiWithImage(application_id, \"zig\", \"data:image/webp;base64,...\");\n_ = try client.editApplicationEmoji(application_id, emoji_id, discord.Types.EditApplicationEmoji.init(\"ziggy\"));\n_ = try client.renameApplicationEmoji(application_id, emoji_id, \"ziggy\");\n_ = try client.deleteApplicationEmoji(application_id, emoji_id);\n_ = try client.fetchEntitlements(application_id, discord.Types.ListEntitlements.init().forGuild(guild_id));\n_ = try client.fetchEntitlement(application_id, entitlement_id);\n_ = try client.markEntitlementConsumed(application_id, entitlement_id);\n_ = try client.createTestEntitlement(application_id, discord.Types.CreateTestEntitlement.init(sku_id, guild_id, .guild));\n_ = try client.fetchSkuSubscriptions(sku_id, discord.Types.ListSkuSubscriptions.init().forUser(user_id));\n_ = try client.fetchSkuSubscription(sku_id, subscription_id);\n_ = try client.fetchAutoModerationRules(guild_id);\n_ = try client.fetchAutoModerationRule(guild_id, rule_id);\n_ = try client.createAutoModerationRule(guild_id, discord.Types.CreateAutoModerationRule.init(\n    \"spam guard\",\n    .spam,\n    \u0026.{discord.Types.AutoModerationAction.blockMessage(null)},\n));\n_ = try client.getChannel(channel_id);\n_ = try client.getGuildMember(guild_id, user_id);\n_ = try client.leaveGuild(guild_id);\n```\n\nGuild template helpers cover common template management flows:\n\n```zig\n_ = try client.fetchGuildTemplate(\"template-code\");\n_ = try client.fetchGuildTemplates(guild_id);\n_ = try client.createGuildTemplate(guild_id, discord.Types.CreateGuildTemplate.init(\"starter\"));\n_ = try client.syncGuildTemplate(guild_id, \"template-code\");\n_ = try client.editGuildTemplate(guild_id, \"template-code\", discord.Types.EditGuildTemplate.init().withDescription(\"Updated\"));\n_ = try client.deleteGuildTemplate(guild_id, \"template-code\");\n_ = try client.fetchGuildWidgetSettings(guild_id);\n_ = try client.editGuildWidgetSettings(guild_id, discord.Types.EditGuildWidgetSettings.init().enabledState(true));\n_ = try client.fetchGuildWidget(guild_id);\n_ = try client.fetchGuildWidgetImage(guild_id, discord.Types.GetGuildWidgetImage.init().withStyle(.banner2));\n_ = try client.fetchGuildWelcomeScreen(guild_id);\nconst welcome_channels = [_]discord.Types.WelcomeScreenChannel{\n    discord.Types.WelcomeScreenChannel.init(channel_id, \"Read rules\").withEmojiName(\"wave\"),\n};\n_ = try client.editGuildWelcomeScreen(guild_id, discord.Types.EditWelcomeScreen.init()\n    .enabledState(true)\n    .withChannels(\u0026welcome_channels)\n    .withDescription(\"Start here\"));\n_ = try client.fetchGuildOnboarding(guild_id);\n_ = try client.editGuildOnboarding(guild_id, discord.Types.EditGuildOnboarding.init()\n    .withDefaultChannels(\u0026.{channel_id})\n    .enabledState(true)\n    .withMode(.onboarding_advanced));\n_ = try client.setGuildIncidentActions(guild_id, discord.Types.EditGuildIncidentActions.init().clearInvitesDisabledUntil());\n_ = try client.fetchGuildVanityUrl(guild_id);\n_ = try client.fetchGuildScheduledEvents(guild_id, discord.Types.ListGuildScheduledEvents.init().withUserCount(true));\n_ = try client.fetchGuildScheduledEvent(guild_id, event_id, discord.Types.GetGuildScheduledEvent.init().withUserCount(true));\n_ = try client.createGuildScheduledEvent(guild_id, discord.Types.CreateGuildScheduledEvent.init(\"Launch\", \"2026-06-02T10:00:00.000Z\", .voice).withChannel(channel_id));\n_ = try client.editGuildScheduledEvent(guild_id, event_id, discord.Types.EditGuildScheduledEvent.init().withStatus(.active));\n_ = try client.fetchGuildScheduledEventUsers(guild_id, event_id, discord.Types.ListGuildScheduledEventUsers.init().withLimit(25));\n_ = try client.createStageInstance(discord.Types.CreateStageInstance.init(stage_channel_id, \"Live Q\u0026A\").withScheduledEvent(event_id));\n_ = try client.fetchStageInstance(stage_channel_id);\n_ = try client.editStageInstance(stage_channel_id, discord.Types.EditStageInstance.init().withTopic(\"Aftershow\"));\n_ = try client.deleteStageInstance(stage_channel_id);\n_ = try client.fetchVoiceRegions();\n_ = try client.fetchGuildVoiceRegions(guild_id);\n_ = try client.fetchCurrentUserVoiceState(guild_id);\n_ = try client.fetchUserVoiceState(guild_id, user_id);\n_ = try client.editCurrentUserVoiceState(guild_id, discord.Types.EditCurrentUserVoiceState.init().suppressState(false));\n_ = try client.editUserVoiceState(guild_id, user_id, discord.Types.EditUserVoiceState.init().suppressState(true));\n```\n\nDirect messages use the same channel message helpers after opening the DM channel:\n\n```zig\n_ = try client.createDmChannel(user_id);\n```\n\nGuild channel management helpers cover common Discord.js channel flows:\n\n```zig\n_ = try client.fetchGuildChannels(guild_id);\n_ = try client.createGuildChannel(\n    guild_id,\n    discord.Types.CreateGuildChannel.init(\"general\")\n        .withType(.guild_text)\n        .withTopic(\"Project chat\"),\n);\n\n_ = try client.setGuildChannelPositions(guild_id, \u0026.{\n    discord.Types.GuildChannelPosition.init(channel_id).withPosition(2),\n});\n_ = try client.editChannel(channel_id, discord.Types.EditChannel.init().withName(\"announcements\"));\n_ = try client.deleteChannel(channel_id);\n_ = try client.setChannelPermission(channel_id, role_id, discord.Types.EditChannelPermission.init(.role)\n    .withAllow(discord.Permissions.view_channel)\n    .withDeny(discord.Permissions.send_messages));\n_ = try client.removeChannelPermission(channel_id, role_id);\n_ = try client.setVoiceChannelStatus(voice_channel_id, discord.Types.SetVoiceChannelStatus.init(\"Focus room\"));\n_ = try client.setVoiceChannelStatusText(voice_channel_id, \"Focus room\");\n_ = try client.clearVoiceChannelStatus(voice_channel_id);\n_ = try client.followAnnouncementChannel(news_channel_id, discord.Types.FollowAnnouncementChannel.init(target_channel_id));\n_ = try client.followAnnouncementChannelTo(news_channel_id, target_channel_id);\n_ = try client.followNewsChannelTo(news_channel_id, target_channel_id);\n\n_ = try client.createThread(channel_id, discord.Types.CreateThread.init(\"private-review\")\n    .withType(.private_thread)\n    .invitableState(false));\n_ = try client.createThreadWithName(channel_id, \"standalone\");\n_ = try client.leaveThread(thread_id);\n\n_ = try client.createInvite(channel_id, discord.Types.CreateChannelInvite.init().withMaxUses(1).uniqueState(true));\n_ = try client.createDefaultInvite(channel_id);\n_ = try client.createInviteWithMaxUses(channel_id, 1);\n_ = try client.createInviteWithMaxAge(channel_id, 3600);\n_ = try client.createUniqueInvite(channel_id);\n_ = try client.fetchChannelInvites(channel_id);\n_ = try client.fetchGuildInvites(guild_id);\n_ = try client.fetchInvite(\"invite-code\");\n_ = try client.fetchInviteWithOptions(\"invite-code\", discord.Types.GetInvite.init().withCounts(true));\n_ = try client.fetchInviteTargetUsers(\"invite-code\");\n_ = try client.updateInviteTargetUsers(\n    \"invite-code\",\n    discord.Types.UploadFile.init(\"targets.csv\", \"user_id\\n123\\n\").withContentType(\"text/csv\"),\n);\n_ = try client.fetchInviteTargetUsersJobStatus(\"invite-code\");\n_ = try client.deleteInvite(\"invite-code\");\n\nconst lobby_metadata = [_]discord.Types.StringPair{.{ .key = \"mode\", .value = \"duo\" }};\n_ = try client.createLobby(discord.Types.CreateLobby.init().withMetadata(\u0026lobby_metadata).withIdleTimeout(60));\n_ = try client.fetchLobby(lobby_id);\n_ = try client.editLobby(lobby_id, discord.Types.EditLobby.init().clearMetadata());\n_ = try client.setLobbyMember(lobby_id, user_id, discord.Types.UpdateLobbyMember.init().withMetadata(\u0026lobby_metadata).withFlags(1));\n_ = try client.bulkUpdateLobbyMembers(lobby_id, discord.Types.BulkUpdateLobbyMembers.init(\u0026.{discord.Types.LobbyMember.init(user_id).removeState(true)}));\n_ = try client.linkLobbyChannel(\"Bearer user-oauth-token\", lobby_id, discord.Types.LinkLobbyChannel.init(channel_id));\n_ = try client.unlinkLobbyChannel(\"Bearer user-oauth-token\", lobby_id);\n_ = try client.setLobbyMessageModerationMetadata(lobby_id, message_id, discord.Types.UpdateLobbyMessageModerationMetadata.init(\u0026.{\n    .{ .key = \"action\", .value = \"replace\" },\n    .{ .key = \"replacement\", .value = \"Be kind\" },\n}));\n_ = try client.leaveLobby(\"Bearer user-oauth-token\", lobby_id);\n_ = try client.deleteLobby(lobby_id);\n\n_ = try client.fetchChannelWebhooks(channel_id);\n_ = try client.createWebhook(channel_id, discord.Types.CreateWebhook.init(\"deploys\"));\n_ = try client.createWebhookWithName(channel_id, \"deploys\");\n_ = try client.createWebhookWithAvatar(channel_id, \"deploys\", \"data:image/png;base64,AAAA\");\n_ = try client.fetchGuildWebhooks(guild_id);\n_ = try client.fetchWebhook(webhook_id);\n_ = try client.editWebhook(webhook_id, discord.Types.EditWebhook.init().withName(\"deploys-updated\"));\n_ = try client.fetchWebhookWithToken(webhook_id, webhook_token);\n_ = try client.editWebhookWithToken(webhook_id, webhook_token, discord.Types.EditWebhookWithToken.init().withName(\"token-deploys\"));\n_ = try client.executeWebhook(webhook_id, webhook_token, discord.Types.ExecuteWebhook.init(\"deployed\").withUsername(\"deploys\"));\n_ = try client.executeWebhookWithContent(webhook_id, webhook_token, \"deployed\");\n_ = try client.fetchWebhookMessage(webhook_id, webhook_token, webhook_message_id);\n_ = try client.editWebhookMessage(webhook_id, webhook_token, webhook_message_id, discord.Types.EditMessage.init().withContent(\"updated\"));\n_ = try client.deleteWebhookMessage(webhook_id, webhook_token, webhook_message_id);\n_ = try client.deleteWebhookWithToken(webhook_id, webhook_token);\n_ = try client.deleteWebhook(webhook_id);\n```\n\nRole helpers cover common moderation setup flows:\n\n```zig\n_ = try client.fetchRoles(guild_id);\n_ = try client.fetchRole(guild_id, role_id);\n_ = try client.fetchRoleMemberCounts(guild_id);\n_ = try client.createGuildRole(\n    guild_id,\n    discord.Types.CreateGuildRole.init(\"moderator\")\n        .withPermissions(discord.Permissions.manage_messages)\n        .withColors(discord.Types.RoleColors.init(0x5865F2))\n        .withUnicodeEmoji(\"⚡\")\n        .mentionableState(true),\n);\n_ = try client.createRoleWithName(guild_id, \"moderator\");\n\n_ = try client.editGuildRolePositions(guild_id, \u0026.{\n    discord.Types.GuildRolePosition.init(role_id).withPosition(2),\n});\n_ = try client.editGuildRole(guild_id, role_id, discord.Types.EditGuildRole.init().hoisted(false));\n_ = try client.renameRole(guild_id, role_id, \"moderator-renamed\");\n_ = try client.deleteGuildRole(guild_id, role_id);\n_ = try client.addRole(guild_id, user_id, role_id);\n_ = try client.removeRole(guild_id, user_id, role_id);\n```\n\nGuild emoji helpers cover list, fetch, create, edit, and delete flows:\n\n```zig\n_ = try client.fetchEmojis(guild_id);\n_ = try client.fetchEmoji(guild_id, emoji_id);\n_ = try client.createGuildEmoji(guild_id, discord.Types.CreateGuildEmoji.init(\"zig\", \"data:image/webp;base64,...\"));\n_ = try client.createEmojiWithImage(guild_id, \"zig\", \"data:image/webp;base64,...\");\n_ = try client.editGuildEmoji(guild_id, emoji_id, discord.Types.EditGuildEmoji.init().withName(\"ziggy\"));\n_ = try client.renameEmoji(guild_id, emoji_id, \"ziggy\");\n_ = try client.deleteGuildEmoji(guild_id, emoji_id);\n```\n\nGuild sticker helpers support fetch, multipart create, edit, and delete flows:\n\n```zig\n_ = try client.fetchStickerById(sticker_id);\n_ = try client.fetchStickerPacks();\n_ = try client.fetchStickers(guild_id);\n_ = try client.fetchSticker(guild_id, sticker_id);\n_ = try client.createGuildSticker(\n    guild_id,\n    discord.Types.CreateGuildSticker.init(\"zig\", \"zap\").withDescription(\"Zig mascot\"),\n    .{ .filename = \"zig.png\", .content = png_bytes, .content_type = \"image/png\" },\n);\n_ = try client.editGuildSticker(guild_id, sticker_id, discord.Types.EditGuildSticker.init().withName(\"ziggy\"));\n_ = try client.renameSticker(guild_id, sticker_id, \"ziggy\");\n_ = try client.setStickerDescription(guild_id, sticker_id, \"Zig mascot\");\n_ = try client.setStickerTags(guild_id, sticker_id, \"zig,zap\");\n_ = try client.deleteGuildSticker(guild_id, sticker_id);\n```\n\nMember moderation helpers cover OAuth member add, kick, ban, unban, ban fetch/list, prune, and common member edits:\n\n```zig\n_ = try client.addGuildMember(guild_id, user_id, discord.Types.AddGuildMember.init(\"user-oauth-access-token\")\n    .withNick(\"helper\")\n    .withRoles(\u0026.{role_id}));\n_ = try client.editCurrentGuildMember(guild_id, discord.Types.EditCurrentGuildMember.init().withNick(\"ziggy\"));\n_ = try client.setCurrentGuildMemberNick(guild_id, \"ziggy\");\n_ = try client.clearCurrentGuildMemberNick(guild_id);\n_ = try client.setCurrentGuildMemberAvatar(guild_id, \"data:image/png;base64,...\");\n_ = try client.clearCurrentGuildMemberAvatar(guild_id);\n_ = try client.setCurrentGuildMemberBanner(guild_id, \"data:image/png;base64,...\");\n_ = try client.clearCurrentGuildMemberBanner(guild_id);\n_ = try client.setCurrentGuildMemberBio(guild_id, \"Built with Zig\");\n_ = try client.clearCurrentGuildMemberBio(guild_id);\n_ = try client.setNickname(guild_id, \"ziggy\");\n_ = try client.editGuildMember(guild_id, user_id, discord.Types.EditGuildMember.init()\n    .withNick(\"helper\")\n    .timeoutUntil(\"2026-06-02T10:00:00.000Z\"));\n_ = try client.setMemberNickname(guild_id, user_id, \"helper\");\n_ = try client.setMemberRoles(guild_id, user_id, \u0026.{role_id});\n_ = try client.muteMember(guild_id, user_id, true);\n_ = try client.deafenMember(guild_id, user_id, true);\n_ = try client.moveMemberToVoiceChannel(guild_id, user_id, voice_channel_id);\n_ = try client.timeoutMember(guild_id, user_id, \"2026-06-02T10:00:00.000Z\");\n_ = try client.clearMemberTimeout(guild_id, user_id);\n\n_ = try client.kick(guild_id, user_id);\n_ = try client.fetchMembers(guild_id, discord.Types.ListGuildMembers.init().withLimit(100));\n_ = try client.searchMembers(guild_id, discord.Types.SearchGuildMembers.init(\"helper\").withLimit(25));\n_ = try client.fetchBans(guild_id, discord.Types.ListGuildBans.init().withLimit(25));\n_ = try client.fetchBan(guild_id, user_id);\n_ = try client.fetchPruneCount(guild_id, discord.Types.GetGuildPruneCount.init().withDays(14));\n_ = try client.prune(guild_id, discord.Types.BeginGuildPrune.init().withDays(14).computeCount(false));\n_ = try client.pruneMembers(guild_id);\n_ = try client.pruneMembersWithDays(guild_id, 14);\n_ = try client.pruneMembersWithDaysAndCount(guild_id, 14, false);\n_ = try client.ban(guild_id, user_id, discord.Types.CreateGuildBan.init().deleteMessagesFor(3600));\n_ = try client.banUser(guild_id, user_id);\n_ = try client.banUserDeletingMessagesFor(guild_id, user_id, 3600);\n_ = try client.bulkBan(guild_id, discord.Types.BulkGuildBan.init(\u0026.{ user_id_a, user_id_b }).deleteMessagesFor(3600));\n_ = try client.bulkBanUsers(guild_id, \u0026.{ user_id_a, user_id_b });\n_ = try client.bulkBanUsersDeletingMessagesFor(guild_id, \u0026.{ user_id_a, user_id_b }, 3600);\n_ = try client.unban(guild_id, user_id);\n```\n\nSoundboard helpers cover default sounds, guild sounds, and sending a sound to a voice channel:\n\n```zig\n_ = try client.fetchDefaultSoundboardSounds();\n_ = try client.fetchGuildSoundboardSounds(guild_id);\n_ = try client.fetchGuildSoundboardSound(guild_id, sound_id);\n_ = try client.createGuildSoundboardSound(guild_id, discord.Types.CreateGuildSoundboardSound.init(\"launch\", \"data:audio/ogg;base64,T0dH\").withVolume(0.5));\n_ = try client.createSoundboardSoundWithData(guild_id, \"launch\", \"data:audio/ogg;base64,T0dH\");\n_ = try client.renameSoundboardSound(guild_id, sound_id, \"launch-renamed\");\n_ = try client.sendSoundboardSound(voice_channel_id, discord.Types.SendSoundboardSound.init(sound_id));\n_ = try client.sendSoundboardSoundById(voice_channel_id, sound_id);\n_ = try client.playSoundboardSound(voice_channel_id, sound_id);\n```\n\nThe top-level client also exposes small Discord.js-like convenience methods:\n\n```zig\nclient.onMessageCreate(discord.Events.rawHandler(\u0026state, State.onMessage));\nclient.onMessageUpdate(discord.Events.rawHandler(\u0026state, State.onMessageUpdate));\nclient.onMessageDelete(discord.Events.rawHandler(\u0026state, State.onMessageDelete));\nclient.onMessageReactionAdd(discord.Events.rawHandler(\u0026state, State.onMessageReactionAdd));\nclient.onMessagePollVoteAdd(discord.Events.rawHandler(\u0026state, State.onMessagePollVoteAdd));\nclient.onChannelPinsUpdate(discord.Events.rawHandler(\u0026state, State.onChannelPinsUpdate));\nclient.onTypingStart(discord.Events.rawHandler(\u0026state, State.onTypingStart));\nclient.onInviteCreate(discord.Events.rawHandler(\u0026state, State.onInviteCreate));\nclient.onWebhooksUpdate(discord.Events.rawHandler(\u0026state, State.onWebhooksUpdate));\nclient.onAutoModerationRuleCreate(discord.Events.rawHandler(\u0026state, State.onAutoModerationRuleCreate));\nclient.onAutoModerationActionExecution(discord.Events.rawHandler(\u0026state, State.onAutoModerationActionExecution));\nclient.onEntitlementCreate(discord.Events.rawHandler(\u0026state, State.onEntitlementCreate));\nclient.onEntitlementDelete(discord.Events.rawHandler(\u0026state, State.onEntitlementDelete));\nclient.onGuildSoundboardSoundCreate(discord.Events.rawHandler(\u0026state, State.onGuildSoundboardSoundCreate));\nclient.onSoundboardSounds(discord.Events.rawHandler(\u0026state, State.onSoundboardSounds));\nclient.onApplicationCommandPermissionsUpdate(discord.Events.rawHandler(\u0026state, State.onApplicationCommandPermissionsUpdate));\nclient.onUserUpdate(discord.Events.rawHandler(\u0026state, State.onUserUpdate));\nclient.onPresenceUpdate(discord.Events.rawHandler(\u0026state, State.onPresenceUpdate));\nclient.onVoiceStateUpdate(discord.Events.rawHandler(\u0026state, State.onVoiceStateUpdate));\nclient.onVoiceServerUpdate(discord.Events.rawHandler(\u0026state, State.onVoiceServerUpdate));\nclient.onGuildAuditLogEntryCreate(discord.Events.rawHandler(\u0026state, State.onGuildAuditLogEntryCreate));\nclient.onGuildBanAdd(discord.Events.rawHandler(\u0026state, State.onGuildBanAdd));\nclient.onGuildMembersChunk(discord.Events.rawHandler(\u0026state, State.onGuildMembersChunk));\nclient.onIntegrationCreate(discord.Events.rawHandler(\u0026state, State.onIntegrationCreate));\nclient.onGuildMemberAdd(discord.Events.rawHandler(\u0026state, State.onGuildMemberAdd));\nclient.onGuildEmojisUpdate(discord.Events.rawHandler(\u0026state, State.onGuildEmojisUpdate));\nclient.onGuildStickersUpdate(discord.Events.rawHandler(\u0026state, State.onGuildStickersUpdate));\nclient.onGuildScheduledEventCreate(discord.Events.rawHandler(\u0026state, State.onGuildScheduledEventCreate));\nclient.onStageInstanceCreate(discord.Events.rawHandler(\u0026state, State.onStageInstanceCreate));\nclient.onChannelDelete(discord.Events.rawHandler(\u0026state, State.onChannelDelete));\nclient.onChannelInfo(discord.Events.rawHandler(\u0026state, State.onChannelInfo));\nclient.onVoiceChannelStatusUpdate(discord.Events.rawHandler(\u0026state, State.onVoiceChannelStatusUpdate));\nclient.onVoiceChannelStartTimeUpdate(discord.Events.rawHandler(\u0026state, State.onVoiceChannelStartTimeUpdate));\nclient.onThreadCreate(discord.Events.rawHandler(\u0026state, State.onThreadCreate));\nclient.onApplicationCommand(discord.Events.rawHandler(\u0026state, State.onApplicationCommand));\nclient.onMessageComponent(discord.Events.rawHandler(\u0026state, State.onMessageComponent));\nclient.onModalSubmit(discord.Events.rawHandler(\u0026state, State.onModalSubmit));\nclient.onResumed(discord.Events.rawHandler(\u0026state, State.onResumed));\nclient.onUnknown(discord.Events.rawHandler(\u0026state, State.onUnknown));\nclient.onceReady(discord.Events.rawHandler(\u0026state, State.onReady));\nclient.onceMessageCreate(discord.Events.rawHandler(\u0026state, State.onMessage));\nclient.onceMessageUpdate(discord.Events.rawHandler(\u0026state, State.onMessageUpdate));\nclient.onceMessageDelete(discord.Events.rawHandler(\u0026state, State.onMessageDelete));\nclient.onceMessageDeleteBulk(discord.Events.rawHandler(\u0026state, State.onMessageDeleteBulk));\nclient.onceMessageReactionAdd(discord.Events.rawHandler(\u0026state, State.onMessageReactionAdd));\nclient.onceMessageReactionRemove(discord.Events.rawHandler(\u0026state, State.onMessageReactionRemove));\nclient.onceMessageReactionRemoveAll(discord.Events.rawHandler(\u0026state, State.onMessageReactionRemoveAll));\nclient.onceMessageReactionRemoveEmoji(discord.Events.rawHandler(\u0026state, State.onMessageReactionRemoveEmoji));\nclient.onceMessagePollVoteAdd(discord.Events.rawHandler(\u0026state, State.onMessagePollVoteAdd));\nclient.onceMessagePollVoteRemove(discord.Events.rawHandler(\u0026state, State.onMessagePollVoteRemove));\nclient.onceInteractionCreate(discord.Events.rawHandler(\u0026state, State.onInteractionCreate));\nclient.onceApplicationCommand(discord.Events.rawHandler(\u0026state, State.onApplicationCommand));\nclient.onceMessageComponent(discord.Events.rawHandler(\u0026state, State.onMessageComponent));\nclient.onceApplicationCommandAutocomplete(discord.Events.rawHandler(\u0026state, State.onApplicationCommandAutocomplete));\nclient.onceModalSubmit(discord.Events.rawHandler(\u0026state, State.onModalSubmit));\nclient.onceUserUpdate(discord.Events.rawHandler(\u0026state, State.onUserUpdate));\nclient.oncePresenceUpdate(discord.Events.rawHandler(\u0026state, State.onPresenceUpdate));\nclient.onceVoiceStateUpdate(discord.Events.rawHandler(\u0026state, State.onVoiceStateUpdate));\nclient.onceTypingStart(discord.Events.rawHandler(\u0026state, State.onTypingStart));\nclient.onceWebhooksUpdate(discord.Events.rawHandler(\u0026state, State.onWebhooksUpdate));\nclient.onceInviteCreate(discord.Events.rawHandler(\u0026state, State.onInviteCreate));\nclient.onceInviteDelete(discord.Events.rawHandler(\u0026state, State.onInviteDelete));\nclient.onceAutoModerationRuleCreate(discord.Events.rawHandler(\u0026state, State.onAutoModerationRuleCreate));\nclient.onceAutoModerationActionExecution(discord.Events.rawHandler(\u0026state, State.onAutoModerationActionExecution));\nclient.onceEntitlementCreate(discord.Events.rawHandler(\u0026state, State.onEntitlementCreate));\nclient.onceEntitlementDelete(discord.Events.rawHandler(\u0026state, State.onEntitlementDelete));\nclient.onceGuildSoundboardSoundCreate(discord.Events.rawHandler(\u0026state, State.onGuildSoundboardSoundCreate));\nclient.onceSoundboardSounds(discord.Events.rawHandler(\u0026state, State.onSoundboardSounds));\nclient.onceGuildRoleCreate(discord.Events.rawHandler(\u0026state, State.onGuildRoleCreate));\nclient.onceGuildEmojisUpdate(discord.Events.rawHandler(\u0026state, State.onGuildEmojisUpdate));\nclient.onceGuildStickersUpdate(discord.Events.rawHandler(\u0026state, State.onGuildStickersUpdate));\nclient.onceGuildScheduledEventCreate(discord.Events.rawHandler(\u0026state, State.onGuildScheduledEventCreate));\nclient.onceStageInstanceCreate(discord.Events.rawHandler(\u0026state, State.onStageInstanceCreate));\nclient.onceGuildAuditLogEntryCreate(discord.Events.rawHandler(\u0026state, State.onGuildAuditLogEntryCreate));\nclient.onceGuildBanAdd(discord.Events.rawHandler(\u0026state, State.onGuildBanAdd));\nclient.onceIntegrationCreate(discord.Events.rawHandler(\u0026state, State.onIntegrationCreate));\nclient.onceGuildMembersChunk(discord.Events.rawHandler(\u0026state, State.onGuildMembersChunk));\nclient.onceVoiceServerUpdate(discord.Events.rawHandler(\u0026state, State.onVoiceServerUpdate));\nclient.onceChannelPinsUpdate(discord.Events.rawHandler(\u0026state, State.onChannelPinsUpdate));\nclient.onceThreadCreate(discord.Events.rawHandler(\u0026state, State.onThreadCreate));\nclient.onceGuildCreate(discord.Events.rawHandler(\u0026state, State.onGuildCreate));\nclient.onceGuildMemberAdd(discord.Events.rawHandler(\u0026state, State.onGuildMemberAdd));\nclient.onceChannelCreate(discord.Events.rawHandler(\u0026state, State.onChannelCreate));\nclient.on(.MESSAGE_CREATE, discord.Events.rawHandler(\u0026state, State.onMessage));\nclient.once(.READY, discord.Events.rawHandler(\u0026state, State.onReady));\nclient.on(discord.Events.MessageCreate, discord.Events.rawHandler(\u0026state, State.onMessage));\nclient.once(discord.Events.ClientReady, discord.Events.rawHandler(\u0026state, State.onReady));\nconst event_name = discord.Events.discordJsName(discord.Events.MessageBulkDelete).?;\nconst event = discord.Events.fromDiscordJsName(\"messageDeleteBulk\").?;\nconst message_listener_count = client.listenerCount(.MESSAGE_CREATE);\nconst has_ready_listener = client.hasListener(.READY);\nconst active_events = try client.eventNames(allocator);\ndefer allocator.free(active_events);\nclient.clearListener(.MESSAGE_CREATE);\nclient.off(.MESSAGE_UPDATE);\nclient.removeListener(.INTERACTION_CREATE);\nclient.removeAllListeners();\n\nconst ready = client.isReady();\nconst ready_timestamp_ms = client.readyTimestampMs();\nconst uptime_ms = client.uptimeMs(now_ms);\nconst gateway_ping_ms = client.gatewayPingMs();\nconst last_sequence = client.lastGatewaySequence();\nconst last_event = client.lastGatewayEvent();\nclient.resetGatewayState();\n\n_ = try client.sendMessage(channel_id, discord.Types.CreateMessage.init(\"pong\"));\n_ = try client.reply(channel_id, original_message_id, discord.Types.CreateMessage.init(\"reply\"));\n_ = try client.react(channel_id, message_id, \"👍\");\n\nconst cached = client.getCachedMessage(message_id);\nconst cached_message = client.cachedMessage(message_id);\nconst has_cached_message = client.hasCachedMessage(message_id);\nconst current_user_id = client.currentUserId();\nconst current_user = client.getCurrentCachedUser();\nconst me = client.me();\nconst current_user_alias = client.currentUser();\nconst has_current_user = client.hasCurrentCachedUser();\nconst current_application_id = client.currentApplicationId();\nconst current_application = client.getCurrentCachedApplication();\nconst current_application_alias = client.currentApplication();\nconst has_current_application = client.hasCurrentCachedApplication();\nconst cached_channel = client.getCachedChannel(channel_id);\nconst cached_channel_alias = client.cachedChannel(channel_id);\nconst has_cached_channel = client.hasCachedChannel(channel_id);\nconst cached_member = client.getCachedMember(guild_id, user_id);\nconst cached_member_alias = client.cachedMember(guild_id, user_id);\nconst has_cached_member = client.hasCachedMember(guild_id, user_id);\nconst cached_user = client.cachedUser(user_id);\nconst cached_guild = client.cachedGuild(guild_id);\nconst cached_role = client.getCachedRole(guild_id, role_id);\nconst cached_role_alias = client.cachedRole(guild_id, role_id);\nconst cached_emoji = client.getCachedEmoji(guild_id, emoji_id);\nconst cached_emoji_alias = client.cachedEmoji(guild_id, emoji_id);\nconst cached_sticker = client.getCachedSticker(guild_id, sticker_id);\nconst cached_sticker_alias = client.cachedSticker(guild_id, sticker_id);\nconst cached_scheduled_event = client.getCachedScheduledEvent(guild_id, event_id);\nconst cached_scheduled_event_alias = client.cachedScheduledEvent(guild_id, event_id);\nconst cached_stage_instance = client.getCachedStageInstance(guild_id, stage_instance_id);\nconst cached_stage_instance_alias = client.cachedStageInstance(guild_id, stage_instance_id);\nconst cached_invite = client.getCachedInvite(\"invite-code\");\nconst cached_invite_alias = client.cachedInvite(\"invite-code\");\nconst cached_presence = client.getCachedPresence(guild_id, user_id);\nconst cached_presence_alias = client.cachedPresence(guild_id, user_id);\nconst cached_voice_state = client.getCachedVoiceState(guild_id, user_id);\nconst cached_voice_state_alias = client.cachedVoiceState(guild_id, user_id);\nconst cache_stats = client.cacheStats();\nconst cached_user_count = client.cachedUserCount();\nconst cached_guild_count = client.cachedGuildCount();\nconst cached_channel_count = client.cachedChannelCount();\nconst cached_member_count = client.cachedMemberCount();\nconst cached_role_count = client.cachedRoleCount();\nconst cached_emoji_count = client.cachedEmojiCount();\nconst cached_sticker_count = client.cachedStickerCount();\nconst cached_message_count = client.cachedMessageCount();\nconst guild_cache_stats = client.guildCacheStats(guild_id);\nconst channel_cache_stats = client.channelCacheStats(channel_id);\n\nclient.evictCachedMessage(message_id);\nclient.evictCachedChannel(channel_id);\nclient.evictCachedMember(guild_id, user_id);\nclient.evictCachedInvite(\"invite-code\");\nclient.clearCache();\n\nconst cached_users = try client.listCachedUsers(allocator);\ndefer allocator.free(cached_users);\nconst cached_users_alias = try client.cachedUsers(allocator);\ndefer allocator.free(cached_users_alias);\nconst cached_guilds = try client.listCachedGuilds(allocator);\ndefer allocator.free(cached_guilds);\nconst cached_guilds_alias = try client.cachedGuilds(allocator);\ndefer allocator.free(cached_guilds_alias);\nconst cached_all_channels = try client.listCachedChannels(allocator);\ndefer allocator.free(cached_all_channels);\nconst cached_all_channels_alias = try client.cachedChannels(allocator);\ndefer allocator.free(cached_all_channels_alias);\nconst cached_top_level_channels = try client.listCachedTopLevelChannels(allocator);\ndefer allocator.free(cached_top_level_channels);\nconst cached_top_level_channels_alias = try client.cachedTopLevelChannels(allocator);\ndefer allocator.free(cached_top_level_channels_alias);\nconst cached_all_messages = try client.listCachedMessages(allocator);\ndefer allocator.free(cached_all_messages);\nconst cached_all_messages_alias = try client.cachedMessages(allocator);\ndefer allocator.free(cached_all_messages_alias);\nconst cached_guild_messages = try client.listCachedGuildMessages(allocator, guild_id);\ndefer allocator.free(cached_guild_messages);\nconst cached_guild_messages_alias = try client.cachedGuildMessages(allocator, guild_id);\ndefer allocator.free(cached_guild_messages_alias);\nconst cached_messages = try client.listCachedChannelMessages(allocator, channel_id);\ndefer allocator.free(cached_messages);\nconst cached_messages_alias = try client.cachedChannelMessages(allocator, channel_id);\ndefer allocator.free(cached_messages_alias);\nconst cached_channels = try client.listCachedGuildChannels(allocator, guild_id);\ndefer allocator.free(cached_channels);\nconst cached_channels_alias = try client.cachedGuildChannels(allocator, guild_id);\ndefer allocator.free(cached_channels_alias);\nconst cached_threads = try client.listCachedChannelThreads(allocator, channel_id);\ndefer allocator.free(cached_threads);\nconst cached_threads_alias = try client.cachedChannelThreads(allocator, channel_id);\ndefer allocator.free(cached_threads_alias);\nconst cached_guild_threads = try client.listCachedGuildThreads(allocator, guild_id);\ndefer allocator.free(cached_guild_threads);\nconst cached_guild_threads_alias = try client.cachedGuildThreads(allocator, guild_id);\ndefer allocator.free(cached_guild_threads_alias);\nconst cached_all_members = try client.listCachedMembers(allocator);\ndefer allocator.free(cached_all_members);\nconst cached_all_members_alias = try client.cachedMembers(allocator);\ndefer allocator.free(cached_all_members_alias);\nconst cached_members = try client.listCachedGuildMembers(allocator, guild_id);\ndefer allocator.free(cached_members);\nconst cached_members_alias = try client.cachedGuildMembers(allocator, guild_id);\ndefer allocator.free(cached_members_alias);\nconst cached_all_roles = try client.listCachedRoles(allocator);\ndefer allocator.free(cached_all_roles);\nconst cached_all_roles_alias = try client.cachedRoles(allocator);\ndefer allocator.free(cached_all_roles_alias);\nconst cached_roles = try client.listCachedGuildRoles(allocator, guild_id);\ndefer allocator.free(cached_roles);\nconst cached_roles_alias = try client.cachedGuildRoles(allocator, guild_id);\ndefer allocator.free(cached_roles_alias);\nconst cached_all_emojis = try client.listCachedEmojis(allocator);\ndefer allocator.free(cached_all_emojis);\nconst cached_all_emojis_alias = try client.cachedEmojis(allocator);\ndefer allocator.free(cached_all_emojis_alias);\nconst cached_emojis = try client.listCachedGuildEmojis(allocator, guild_id);\ndefer allocator.free(cached_emojis);\nconst cached_emojis_alias = try client.cachedGuildEmojis(allocator, guild_id);\ndefer allocator.free(cached_emojis_alias);\nconst cached_all_stickers = try client.listCachedStickers(allocator);\ndefer allocator.free(cached_all_stickers);\nconst cached_all_stickers_alias = try client.cachedStickers(allocator);\ndefer allocator.free(cached_all_stickers_alias);\nconst cached_stickers = try client.listCachedGuildStickers(allocator, guild_id);\ndefer allocator.free(cached_stickers);\nconst cached_stickers_alias = try client.cachedGuildStickers(allocator, guild_id);\ndefer allocator.free(cached_stickers_alias);\nconst cached_all_events = try client.listCachedScheduledEvents(allocator);\ndefer allocator.free(cached_all_events);\nconst cached_all_events_alias = try client.cachedScheduledEvents(allocator);\ndefer allocator.free(cached_all_events_alias);\nconst cached_events = try client.listCachedGuildScheduledEvents(allocator, guild_id);\ndefer allocator.free(cached_events);\nconst cached_events_alias = try client.cachedGuildScheduledEvents(allocator, guild_id);\ndefer allocator.free(cached_events_alias);\nconst cached_all_stage_instances = try client.listCachedStageInstances(allocator);\ndefer allocator.free(cached_all_stage_instances);\nconst cached_all_stage_instances_alias = try client.cachedStageInstances(allocator);\ndefer allocator.free(cached_all_stage_instances_alias);\nconst cached_stage_instances = try client.listCachedGuildStageInstances(allocator, guild_id);\ndefer allocator.free(cached_stage_instances);\nconst cached_stage_instances_alias = try client.cachedGuildStageInstances(allocator, guild_id);\ndefer allocator.free(cached_stage_instances_alias);\nconst cached_invites = try client.listCachedInvites(allocator);\ndefer allocator.free(cached_invites);\nconst cached_invites_alias = try client.cachedInvites(allocator);\ndefer allocator.free(cached_invites_alias);\nconst cached_guild_invites = try client.listCachedGuildInvites(allocator, guild_id);\ndefer allocator.free(cached_guild_invites);\nconst cached_guild_invites_alias = try client.cachedGuildInvites(allocator, guild_id);\ndefer allocator.free(cached_guild_invites_alias);\nconst cached_channel_invites = try client.listCachedChannelInvites(allocator, channel_id);\ndefer allocator.free(cached_channel_invites);\nconst cached_channel_invites_alias = try client.cachedChannelInvites(allocator, channel_id);\ndefer allocator.free(cached_channel_invites_alias);\nconst cached_all_presences = try client.listCachedPresences(allocator);\ndefer allocator.free(cached_all_presences);\nconst cached_all_presences_alias = try client.cachedPresences(allocator);\ndefer allocator.free(cached_all_presences_alias);\nconst cached_presences = try client.listCachedGuildPresences(allocator, guild_id);\ndefer allocator.free(cached_presences);\nconst cached_presences_alias = try client.cachedGuildPresences(allocator, guild_id);\ndefer allocator.free(cached_presences_alias);\nconst cached_all_voice_states = try client.listCachedVoiceStates(allocator);\ndefer allocator.free(cached_all_voice_states);\nconst cached_all_voice_states_alias = try client.cachedVoiceStates(allocator);\ndefer allocator.free(cached_all_voice_states_alias);\nconst cached_voice_states = try client.listCachedGuildVoiceStates(allocator, guild_id);\ndefer allocator.free(cached_voice_states);\nconst cached_voice_states_alias = try client.cachedGuildVoiceStates(allocator, guild_id);\ndefer allocator.free(cached_voice_states_alias);\n```\n\nCollectors provide small stateful filters for message and interaction flows:\n\n```zig\nvar collector = discord.Collectors.MessageCollector.init(.{\n    .guild_id = guild_id,\n    .channel_id = channel_id,\n    .author_id = user_id,\n    .contains = \"confirm\",\n    .max = 1,\n    .time_ms = 30_000,\n    .idle_ms = 10_000,\n});\n\nif (client.getCachedMessage(message_id)) |message| {\n    _ = collector.collectAt(message, now_ms);\n}\n\n_ = try collector.collectDispatchAt(gateway_dispatch, now_ms);\n_ = collector.tick(now_ms);\nconst last_message_id = collector.last_message_id;\n\nvar interaction_collector = discord.Collectors.InteractionCollector.init(.{\n    .guild_id = guild_id,\n    .channel_id = channel_id,\n    .user_id = user_id,\n    .custom_id = \"confirm\",\n    .max = 1,\n    .time_ms = 30_000,\n    .idle_ms = 10_000,\n});\n\n_ = try interaction_collector.collectDispatchAt(gateway_dispatch, now_ms);\n_ = interaction_collector.tick(now_ms);\nconst last_interaction_id = interaction_collector.last_interaction_id;\n```\n\nCollectors keep only counters, status, timestamps, and the last collected snowflake so callers can drive simple flows without retaining payloads.\n\n`examples/ping-bot.zig` uses an in-memory transport so it can run without a real bot token. `examples/slash-bot.zig` is an offline cookbook for slash commands: it defines and validates a command, routes an interaction through the `InteractionRouter` to a typed handler, and builds an ephemeral reply and a colored embed.\n\nFor live REST calls, initialize the client with its owned HTTP transport:\n\n```zig\nvar client = try discord.Client.initHttp(allocator, .{\n    .token = \"Bot real-token\",\n    .intents = discord.Intents.add(discord.Intents.defaultNonPrivileged(), discord.Intents.message_content),\n    .cache_policy = .{ .max_messages = 500 },\n});\ndefer client.deinit();\n\nconst has_required_intents = discord.Intents.hasAll(client.config.intents, discord.Intents.guilds | discord.Intents.message_content);\nconst has_any_dm_intent = discord.Intents.hasAny(client.config.intents, discord.Intents.direct_messages | discord.Intents.direct_message_reactions);\nconst missing_intents = discord.Intents.missing(client.config.intents, discord.Intents.guild_members | discord.Intents.message_content);\n```\n\nCache can also be minimized for memory-sensitive bots:\n\n```zig\nvar client = discord.Client.init(allocator, .{\n    .token = \"Bot token\",\n    .cache_policy = discord.CachePolicy.noMessages(),\n});\ndefer client.deinit();\n// `destroy()` is available as a Discord.js-style alias for `deinit()`.\n```\n\nResponses returned by the live `HttpTransport` own their body and copied headers. Release them with `discord.Http.responseDeinit(allocator, response)` after you are done with the raw response.\n\nPermission helpers can resolve guild role permissions and channel overwrites without allocating:\n\n```zig\nconst guild_roles = [_]discord.Permissions.RolePermissions{\n    .{ .id = role_id.value, .permissions = discord.Permissions.add(discord.Permissions.send_messages, discord.Permissions.pin_messages) },\n};\nconst guild_permissions = discord.Permissions.resolveGuild(\u0026.{role_id.value}, \u0026guild_roles);\n\nconst overwrites = [_]discord.Permissions.Overwrite{\n    discord.Permissions.Overwrite.role(guild_id.value, 0, discord.Permissions.send_messages),\n    discord.Permissions.Overwrite.member(user_id.value, discord.Permissions.send_messages, 0),\n};\n\nconst effective = discord.Permissions.resolveChannel(\n    guild_permissions,\n    guild_id.value,\n    user_id.value,\n    \u0026.{role_id.value},\n    \u0026overwrites,\n);\nconst can_send_and_pin = discord.Permissions.hasAll(effective, discord.Permissions.send_messages | discord.Permissions.pin_messages);\nconst can_moderate_any = discord.Permissions.hasAny(effective, discord.Permissions.manage_messages | discord.Permissions.moderate_members);\nconst missing_permissions = discord.Permissions.missing(effective, discord.Permissions.send_messages | discord.Permissions.pin_messages);\n```\n\nGateway sessions use an injectable text-frame transport, so the protocol state machine stays testable and can be backed by any WebSocket implementation:\n\n```zig\nvar session = client.createGatewaySession(websocket_transport);\n_ = try client.fetchGateway();\n_ = try client.fetchGatewayBot();\nconst shard_id = try discord.Gateway.shardIdForGuild(guild_id, 8);\nconst identify_bucket = try discord.Gateway.identifyRateLimitKey(shard_id, 2);\n_ = identify_bucket;\ntry session.identify();\ntry session.sendHeartbeat();\ntry client.updatePresence(\u0026session, discord.Gateway.Presence.init(.idle));\ntry client.setPresence(\u0026session, discord.Gateway.Presence.init(.dnd));\ntry client.setActivity(\u0026session, \"Zig bots\", discord.SetActivityOptions.init(.watching));\ntry client.updateVoiceState(\n    \u0026session,\n    discord.Gateway.VoiceStateUpdate.init(guild_id).withChannel(voice_channel_id),\n);\ntry client.requestGuildMembers(\n    \u0026session,\n    discord.Gateway.RequestGuildMembers.init(guild_id).withQuery(\"helper\").withLimit(25),\n);\ntry client.requestSoundboardSounds(\u0026session, discord.Gateway.RequestSoundboardSounds.init(\u0026.{guild_id}));\ntry client.requestChannelInfo(\n    \u0026session,\n    discord.Gateway.RequestChannelInfo.init(guild_id, \u0026.{ .status, .voice_start_time }),\n);\n```\n\nFor live Gateway connections, use the built-in WSS transport:\n\n```zig\nvar gateway_transport = discord.GatewayTransport.Transport.init(allocator);\ndefer gateway_transport.deinit();\n\ntry gateway_transport.connect(.{});\n\nvar gateway = client.createGatewayRunner(gateway_transport.sessionTransport());\ndefer gateway.deinit();\n\n_ = try gateway.step(now_ms); // handles HELLO, sends IDENTIFY, schedules heartbeat\n_ = try gateway.step(later_ms); // sends heartbeat or dispatches events/cache updates\n\nif (!gateway.reconnectReady(now_ms)) {\n    const retry_at = gateway.reconnectAfterMs().?;\n    _ = retry_at;\n}\nconst can_resume = gateway.canResume();\nconst session_id = gateway.sessionId();\nconst sequence = gateway.sequence();\nconst next_heartbeat = gateway.nextHeartbeatMs();\nconst heartbeat_interval = gateway.heartbeatIntervalMs();\nconst waiting_for_ack = gateway.awaitingHeartbeatAck();\n```\n\nFor event loops that should not sleep inside the library, use the nonblocking runtime adapter. It reports whether the caller should connect, wait, or handle a Gateway step:\n\n```zig\nvar runtime = discord.GatewayRuntime.NonblockingRuntime.init(\u0026client, gateway_transport.sessionTransport());\ndefer runtime.deinit();\n\nconst runtime_connected = runtime.isConnected();\nconst reconnect_pending = runtime.isReconnectPending();\nconst reconnect_at = runtime.reconnectAfterMs();\n\nswitch (try runtime.step(now_ms)) {\n    .stopped =\u003e return,\n    .connect_required =\u003e {\n        try gateway_transport.connect(.{});\n        runtime.markConnected();\n    },\n    .wait_reconnect_ms =\u003e |delay_ms| _ = delay_ms,\n    .gateway =\u003e |step| _ = step,\n}\n```\n\nFor a simple blocking bot process, the runtime adapter owns the live Gateway transport and reconnect sleeps:\n\n```zig\ntry discord.GatewayRuntime.login(allocator, \u0026client, .{});\n```\n\nUse the explicit runtime when you need lifecycle hooks or a shutdown signal:\n\n```zig\nvar runtime = discord.GatewayRuntime.BlockingRuntime.init(allocator, \u0026client);\ndefer runtime.deinit();\n\n// Call runtime.stop() from your shutdown hook to break the run loop.\ntry runtime.run(.{});\n```\n\nThe WebSocket codec exposes deterministic helpers for transport implementations:\n\n```zig\nvar request = std.Io.Writer.Allocating.init(allocator);\ndefer request.deinit();\n\ntry discord.WebSocket.writeClientHandshakeRequest(\n    \"gateway.discord.gg\",\n    \"/?v=10\u0026encoding=json\",\n    websocket_key,\n    \u0026request.writer,\n);\n```\n\nIt also includes client frame writers and server frame decoding:\n\n```zig\ntry discord.WebSocket.writeTextFrame(payload, mask_key, \u0026writer);\nconst decoded = try discord.WebSocket.decodeFramePrefix(allocator, bytes);\ndefer discord.WebSocket.frameDeinit(allocator, decoded.frame);\n```\n\nSlash command definitions can be streamed directly into the REST command sync helpers:\n\n```zig\nconst choices = [_]discord.Interactions.CommandChoice{\n    discord.Interactions.CommandChoice.string(\"Public\", \"public\"),\n    discord.Interactions.CommandChoice.string(\"Private\", \"private\"),\n};\n\nconst options = [_]discord.Interactions.ApplicationCommandOption{\n    discord.Interactions.ApplicationCommandOption\n        .string(\"visibility\", \"Who can see the reply\", false)\n        .requiredOption()\n        .withChoices(\u0026choices),\n    discord.Interactions.ApplicationCommandOption.number(\"ratio\", \"Scale factor\", false),\n    discord.Interactions.ApplicationCommandOption.role(\"role\", \"Role to notify\", false),\n    discord.Interactions.ApplicationCommandOption.mentionable(\"target\", \"User or role\", false),\n    discord.Interactions.ApplicationCommandOption.attachment(\"file\", \"File to process\", false),\n};\n\nconst name_localizations = [_]discord.Interactions.Localization{\n    .{ .locale = \"tr\", .value = \"profil\" },\n};\n\nconst user_option = discord.Interactions.ApplicationCommandOption\n    .user(\"target\", \"User to inspect\", false)\n    .withNameLocalizations(\u0026name_localizations)\n    .requiredOption();\n\n_ = try client.registerGlobalApplicationCommand(\n    application_id,\n    discord.Interactions.ApplicationCommand\n        .chatInput(\"ping\", \"Replies with pong\")\n        .withDefaultMemberPermissions(8)\n        .dmPermissionState(false),\n);\n_ = try client.setGlobalApplicationCommands(application_id, \u0026.{\n    discord.Interactions.ApplicationCommand\n        .chatInput(\"echo\", \"Echoes text\")\n        .withOptions(\u0026options),\n    discord.Interactions.ApplicationCommand\n        .chatInput(\"profile\", \"Shows a profile\")\n        .withOption(\u0026user_option)\n        .withNameLocalizations(\u0026name_localizations),\n});\n_ = try client.fetchGlobalApplicationCommand(application_id, command_id);\n_ = try client.updateGlobalApplicationCommand(\n    application_id,\n    command_id,\n    discord.Interactions.EditApplicationCommand\n        .init()\n        .withDescription(\"Updated command\")\n        .clearDefaultMemberPermissions(),\n);\n_ = try client.renameGlobalApplicationCommand(application_id, command_id, \"pong\");\n_ = try client.setGlobalApplicationCommandDescription(application_id, command_id, \"Replies with pong\");\n_ = try client.removeGlobalApplicationCommand(application_id, command_id);\n\n_ = try client.registerGuildApplicationCommand(\n    application_id,\n    guild_id,\n    discord.Interactions.ApplicationCommand.chatInput(\"ping\", \"Replies with pong\"),\n);\n_ = try client.setGuildApplicationCommands(application_id, guild_id, \u0026.{\n    discord.Interactions.ApplicationCommand\n        .chatInput(\"echo\", \"Echoes text\")\n        .withOptions(\u0026options),\n});\n_ = try client.fetchGuildApplicationCommand(application_id, guild_id, command_id);\n\n_ = try client.editGuildApplicationCommand(\n    application_id,\n    guild_id,\n    command_id,\n    discord.Interactions.EditApplicationCommand.init()\n        .withDescription(\"Echoes the supplied text\")\n        .withOptions(\u0026options),\n);\n_ = try client.updateGuildApplicationCommand(\n    application_id,\n    guild_id,\n    command_id,\n    discord.Interactions.EditApplicationCommand.init().withDescription(\"Updated guild command\"),\n);\n_ = try client.renameGuildApplicationCommand(application_id, guild_id, command_id, \"guild-pong\");\n_ = try client.setGuildApplicationCommandDescription(application_id, guild_id, command_id, \"Guild command\");\n_ = try client.removeGuildApplicationCommand(application_id, guild_id, command_id);\n```\n\nBuilder and message payload limits can be checked locally before a REST\nround-trip, turning a guaranteed Discord `400` into an allocation-free error:\n\n```zig\ntry discord.Interactions.ApplicationCommand\n    .chatInput(\"echo\", \"Echoes text\")\n    .withOptions(\u0026options)\n    .validate();\n\nconst row_components = [_]discord.Interactions.Component{\n    .{ .button = discord.Interactions.Button.primary(\"confirm\", \"Confirm\") },\n};\nconst rows = [_]discord.Interactions.Component{\n    discord.Interactions.Component.actionRow(\u0026row_components),\n};\ntry discord.Interactions.Component.validateLayout(\u0026rows);\n\nconst embed_fields = [_]discord.Types.EmbedField{\n    discord.Types.EmbedField.init(\"Status\", \"ok\"),\n};\nconst embeds = [_]discord.Types.Embed{\n    discord.Types.Embed.init().withFields(\u0026embed_fields),\n};\ntry embeds[0].validate();\n\ntry discord.Types.CreateMessage.init(\"ready\")\n    .withEmbeds(\u0026embeds)\n    .withComponents(\u0026rows)\n    .validate();\ntry discord.Types.EditMessage.init()\n    .withContent(\"updated\")\n    .validate();\n```\n\nApplication command permissions use Discord's Bearer-token flow:\n\n```zig\nconst permissions = [_]discord.Interactions.ApplicationCommandPermission{\n    discord.Interactions.ApplicationCommandPermission.role(role_id, true),\n    discord.Interactions.ApplicationCommandPermission.channel(channel_id, false),\n};\n\n_ = try client.editApplicationCommandPermissions(\n    \"Bearer user-oauth-token\",\n    application_id,\n    guild_id,\n    command_id,\n    \u0026permissions,\n);\n_ = try client.fetchGuildApplicationCommandPermissions(\n    \"Bearer user-oauth-token\",\n    application_id,\n    guild_id,\n);\n_ = try client.fetchApplicationCommandPermissions(\n    \"Bearer user-oauth-token\",\n    application_id,\n    guild_id,\n    command_id,\n);\n```\n\nIncoming interaction payloads expose typed option accessors:\n\n```zig\nvar interaction = try discord.Interactions.parseInteraction(allocator, payload);\ndefer interaction.deinit();\n\nconst data = interaction.data.?;\nconst text = try data.getString(\"text\");\nconst target = try data.getSnowflake(\"target\");\n```\n\nSmall interaction routers can dispatch parsed commands and components by name or `custom_id`:\n\n```zig\nconst commands = [_]discord.Interactions.CommandRoute{\n    .{ .name = \"echo\", .handler = discord.Interactions.parsedHandler(\u0026state, State.onEcho) },\n};\nconst components = [_]discord.Interactions.ComponentRoute{\n    .{ .custom_id = \"confirm\", .handler = discord.Interactions.parsedHandler(\u0026state, State.onConfirm) },\n};\nconst router = discord.Interactions.InteractionRouter{\n    .commands = \u0026commands,\n    .components = \u0026components,\n};\n\n_ = try router.dispatch(\u0026interaction);\n```\n\nAutocomplete responses reuse the same command choice model:\n\n```zig\nconst suggestions = [_]discord.Interactions.CommandChoice{\n    discord.Interactions.CommandChoice.string(\"Public\", \"public\"),\n    discord.Interactions.CommandChoice.string(\"Private\", \"private\"),\n};\n\n_ = try client.createInteractionResponse(\n    interaction_id,\n    token,\n    discord.Interactions.InteractionResponse.autocomplete(\u0026suggestions),\n);\n_ = try client.autocompleteInteraction(interaction_id, token, \u0026suggestions);\n\n_ = try client.replyInteraction(\n    interaction_id,\n    token,\n    discord.Interactions.InteractionResponse.message(\"Done\").ephemeralState(true),\n);\n_ = try client.deferInteractionReply(interaction_id, token, true);\n_ = try client.deferInteractionUpdate(interaction_id, token);\n_ = try client.updateInteractionMessage(\n    interaction_id,\n    token,\n    discord.Interactions.InteractionResponse.updateMessage(\"Updated\"),\n);\n```\n\nMessage components and modal responses are built with the same streaming JSON model:\n\n```zig\nconst confirm_style = discord.Interactions.ButtonStyle.Success;\nconst text_input_style_name = discord.Interactions.TextInputStyle.Short.discordJsName();\nconst component_type = discord.Interactions.ComponentType.fromDiscordJsName(\"Button\").?;\nconst buttons = [_]discord.Interactions.Component{\n    .{ .button = discord.Interactions.Button.primary(\"confirm\", \"Confirm\").withStyle(confirm_style) },\n    .{ .button = discord.Interactions.Button.danger(\"cancel\", \"Cancel\") },\n};\nconst rows = [_]discord.Interactions.Component{\n    discord.Interactions.Component.actionRow(\u0026buttons),\n};\n\n_ = try client.createInteractionResponse(\n    interaction_id,\n    token,\n    discord.Interactions.InteractionResponse.message(\"Choose an action\").withComponents(\u0026rows),\n);\n\nconst visibility_options = [_]discord.Interactions.SelectOption{\n    discord.Interactions.SelectOption.init(\"Public\", \"public\").withDescription(\"Visible to everyone\"),\n    discord.Interactions.SelectOption.init(\"Private\", \"private\").defaultState(true),\n};\nconst visibility_select = [_]discord.Interactions.Component{\n    .{ .string_select = discord.Interactions.StringSelect.init(\"visibility\", \u0026visibility_options) },\n};\nconst visibility_rows = [_]discord.Interactions.Component{\n    discord.Interactions.Component.actionRow(\u0026visibility_select),\n};\n_ = try client.updateInteractionMessage(\n    interaction_id,\n    token,\n    discord.Interactions.InteractionResponse.updateMessage(\"Pick visibility\").withComponents(\u0026visibility_rows),\n);\n\nconst name_input = [_]discord.Interactions.Component{\n    .{ .text_input = discord.Interactions.TextInput.short(\"name\", \"Name\") },\n};\nconst modal_rows = [_]discord.Interactions.Component{\n    discord.Interactions.Component.actionRow(\u0026name_input),\n};\n_ = try client.showModal(\n    interaction_id,\n    token,\n    discord.Interactions.InteractionResponse.modal(\"profile\", \"Edit profile\", \u0026modal_rows),\n);\n\n_ = try client.fetchOriginalInteractionResponse(application_id, token);\n_ = try client.editOriginalInteractionResponse(application_id, token, discord.Types.EditMessage.init().withContent(\"Updated\"));\n_ = try client.createFollowupMessage(application_id, token, discord.Types.ExecuteWebhook.init(\"Follow-up\"));\n_ = try client.createFollowupMessageWithContent(application_id, token, \"Follow-up\");\n_ = try client.fetchFollowupMessage(application_id, token, followup_message_id);\n_ = try client.deleteFollowupMessage(application_id, token, followup_message_id);\n_ = try client.fetchInteractionReply(token);\n_ = try client.editInteractionReply(token, discord.Types.EditMessage.init().withContent(\"Updated\"));\n_ = try client.followUpInteraction(token, discord.Types.ExecuteWebhook.init(\"Follow-up\"));\n_ = try client.followUpInteractionWithContent(token, \"Follow-up\");\n_ = try client.fetchFollowUpInteraction(token, followup_message_id);\n_ = try client.editFollowUpInteraction(token, followup_message_id, discord.Types.EditMessage.init().withContent(\"Edited follow-up\"));\n_ = try client.deleteFollowUpInteraction(token, followup_message_id);\n_ = try client.deleteInteractionReply(token);\n```\n\nComponent and modal submit payloads expose `component_data`, including selected values and text input values:\n\n```zig\nvar interaction = try discord.Interactions.parseInteraction(allocator, payload);\ndefer interaction.deinit();\n\nconst value = interaction.component_data.?.find(\"name\").?.firstValue().?;\n```\n\n## Components V2\n\nComponents V2 lets a message be built entirely from layout components (text\ndisplays, sections, media galleries, files, separators, and containers) instead\nof plain content. Set the components-v2 flag and validate the layout before\nsending:\n\n```zig\nconst headings = [_]discord.Interactions.TextDisplay{\n    discord.Interactions.TextDisplay.init(\"Ships with batteries included.\"),\n};\nconst shots = [_]discord.Interactions.MediaGalleryItem{\n    discord.Interactions.MediaGalleryItem.init(\"https://cdn.example/shot.png\"),\n};\nconst layout = [_]discord.Interactions.Component{\n    .{ .text_display = discord.Interactions.TextDisplay.init(\"# Changelog\") },\n    .{ .section = discord.Interactions.Section.withThumbnail(\n        \u0026headings,\n        discord.Interactions.Thumbnail.init(\"https://cdn.example/icon.png\"),\n    ) },\n    .{ .media_gallery = discord.Interactions.MediaGallery.init(\u0026shots) },\n    .{ .separator = discord.Interactions.Separator.init() },\n};\nconst body = [_]discord.Interactions.Component{\n    .{ .container = discord.Interactions.Container.init(\u0026layout).withAccentColor(0x5865F2) },\n};\ntry discord.Interactions.Component.validateLayout(\u0026body);\n_ = try client.send(channel_id, discord.Types.CreateMessage.init(\"\")\n    .withFlags(discord.Types.MessageFlags.is_components_v2)\n    .withComponents(\u0026body));\n```\n\n## Collections\n\n`discord.Collection(K, V)` is an insertion-ordered map with the functional\nhelpers a Discord.js `Collection` provides:\n\n```zig\nvar members = discord.Collection(u64, []const u8).init(allocator);\ndefer members.deinit();\ntry members.set(1, \"ada\");\ntry members.set(2, \"linus\");\n\n_ = members.get(1);\n_ = members.first();\n_ = members.size();\nvar bots = try members.filter(allocator, {}, isBot);\ndefer bots.deinit();\n```\n\n## Markdown formatting\n\nMarkdown helpers return caller-owned strings, matching the mention builders:\n\n```zig\nconst title = try discord.Formatters.heading(allocator, 1, \"Welcome\");\ndefer allocator.free(title);\nconst link = try discord.Formatters.hyperlink(allocator, \"docs\", \"https://discord.com/developers\");\ndefer allocator.free(link);\nconst command = try discord.Formatters.slashCommandMention(allocator, \"ping\", command_id);\ndefer allocator.free(command);\n```\n\n## Sharding\n\nThe shard manager parses the `GET /gateway/bot` payload and coordinates per-shard\nlifecycle state and guild routing:\n\n```zig\nvar info = try discord.Sharding.GatewayBotInfo.parse(allocator, gateway_bot_json);\ndefer info.deinit(allocator);\n\nvar shards = try discord.Sharding.ShardManager.initFromGatewayBot(allocator, info);\ndefer shards.deinit();\n\nconst shard_id = try shards.shardIdForGuild(guild_id);\ntry shards.setState(shard_id, .ready);\n_ = shards.allReady();\n```\n\n## Voice gateway\n\nThe voice gateway protocol layer builds and parses control-plane payloads. The\naudio media plane (Opus + libsodium + UDP) requires external native dependencies\nand is intentionally left to a separate integration:\n\n```zig\nvar identify = std.Io.Writer.Allocating.init(allocator);\ndefer identify.deinit();\ntry discord.Voice.writeIdentify(server_id, user_id, \"session-id\", \"voice-token\", \u0026identify.writer);\n\nconst offered = [_][]const u8{\"aead_aes256_gcm_rtpsize\"};\nconst mode = discord.Voice.EncryptionMode.preferredMode(\u0026offered);\n```\n\n## Status\n\nThis is a dependency-light Zig foundation for common Discord.js bot workloads rather than a one-to-one class-model port. REST, Gateway, interactions, interaction router registries, command registries/modules/manifests/annotations, components, cache updates, validation helpers, forum thread starters, group DM recipients, webhook multipart sends, richer collections, subscription/rate-limit events, Discord.js-style builder aliases, template/webhook link helpers, expanded CDN asset helpers, voice gateway bootstrap/control-plane helpers, encrypted RTP receive helpers, SSRC-to-user receive buffering, Opus frame validation, PCM mixing, pluggable Opus codec adapters, owned encoded-Opus resources, pre-encoded Opus audio player packetization, shard identify/cluster/latency/process-spec/supervisor/IPC router/broadcast helpers, and emoji/premium component builders are covered for practical bot usage; bundled native Opus codecs, advanced process-manager clustering, decorators, and exhaustive framework-style Discord.js class parity stay outside the core boundary. See `STATUS.md` and `DISCORDJS_COMPARISON.md` for the current coverage map.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyigitt%2Fdiscord-zig","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyigitt%2Fdiscord-zig","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyigitt%2Fdiscord-zig/lists"}