{"id":47863914,"url":"https://github.com/M1tsumi/SwiftDisc","last_synced_at":"2026-04-04T03:01:05.720Z","repository":{"id":323745981,"uuid":"1094534433","full_name":"M1tsumi/SwiftDisc","owner":"M1tsumi","description":"SwiftDisc is a lightweight, native Swift library designed for building powerful Discord bots on iOS and macOS","archived":false,"fork":false,"pushed_at":"2026-03-20T04:34:52.000Z","size":1195,"stargazers_count":38,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-03-20T16:19:56.734Z","etag":null,"topics":["bot-framework","discord","discord-api-wrapper","discordapi","discordlibrary","library","swift","swiftpm"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/M1tsumi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":"quefep","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2025-11-11T20:51:45.000Z","updated_at":"2026-03-20T04:34:54.000Z","dependencies_parsed_at":"2026-02-23T06:02:56.134Z","dependency_job_id":null,"html_url":"https://github.com/M1tsumi/SwiftDisc","commit_stats":null,"previous_names":["m1tsumi/swiftdisc"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/M1tsumi/SwiftDisc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M1tsumi%2FSwiftDisc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M1tsumi%2FSwiftDisc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M1tsumi%2FSwiftDisc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M1tsumi%2FSwiftDisc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/M1tsumi","download_url":"https://codeload.github.com/M1tsumi/SwiftDisc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M1tsumi%2FSwiftDisc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31385935,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T01:22:39.193Z","status":"online","status_checked_at":"2026-04-04T02:00:07.569Z","response_time":60,"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":["bot-framework","discord","discord-api-wrapper","discordapi","discordlibrary","library","swift","swiftpm"],"created_at":"2026-04-04T00:00:29.927Z","updated_at":"2026-04-04T03:01:05.712Z","avatar_url":"https://github.com/M1tsumi.png","language":"Swift","funding_links":["https://ko-fi.com/quefep"],"categories":["Swift","Libraries","Networking","Libs"],"sub_categories":["Swift","API"],"readme":"\u003cdiv align=\"center\"\u003e\n\n![SwiftDisc Typing](https://raw.githubusercontent.com/M1tsumi/M1tsumi/main/assets/typing-swiftdisc.svg)\n\n# SwiftDisc\n\n[![Discord](https://img.shields.io/discord/1439300942167146508?color=5865F2\u0026label=Discord\u0026logo=discord\u0026logoColor=white)](https://discord.gg/6nS2KqxQtj)\n[![Swift Version](https://img.shields.io/badge/Swift-6.2-F05138?logo=swift\u0026logoColor=white)](https://swift.org)\n[![CI](https://github.com/M1tsumi/SwiftDisc/actions/workflows/ci.yml/badge.svg)](https://github.com/M1tsumi/SwiftDisc/actions/workflows/ci.yml)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\n**A modern Swift library for building Discord bots and integrations.**\n\nBuilt with async/await, strongly typed, and cross-platform.\n\n[Documentation](https://github.com/M1tsumi/SwiftDisc/wiki) · [Examples](https://github.com/M1tsumi/SwiftDisc/tree/main/Examples) · [Discord Server](https://discord.gg/6nS2KqxQtj)\n\n\u003c/div\u003e\n\n---\n\n## About\n\nSwiftDisc is a powerful Swift library for interacting with the Discord API. It embraces modern Swift concurrency with async/await throughout, provides fully typed models for Discord's data structures, and handles common pain points like rate limiting, reconnection, and sharding automatically.\n\nWhether you're building a simple bot or a complex integration, SwiftDisc gives you the tools you need while staying out of your way.\n\n## Installation\n\nAdd SwiftDisc to your Swift package dependencies in `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/M1tsumi/SwiftDisc.git\", from: \"2.0.0\")\n]\n```\n\nThen add it to your target:\n\n```swift\ntargets: [\n    .target(name: \"YourBot\", dependencies: [\"SwiftDisc\"])\n]\n```\n\n### Platform Requirements\n\n| Platform | Minimum Version |\n|----------|----------------|\n| macOS | 11.0+ |\n| iOS | 14.0+ |\n| tvOS | 14.0+ |\n| watchOS | 7.0+ |\n| Windows | Swift 5.9+ |\n\n## Quick Start\n\nHere's a simple bot that responds to messages using the v2.0 callback API:\n\n```swift\nimport SwiftDisc\n\n@main\nstruct MyBot {\n    static func main() async {\n        let token = ProcessInfo.processInfo.environment[\"DISCORD_BOT_TOKEN\"] ?? \"\"\n        let client = DiscordClient(token: token)\n\n        // Assign callbacks — no switch statement needed\n        client.onReady = { info in\n            print(\"✅ Logged in as \\(info.user.username)\")\n        }\n\n        client.onMessage = { message in\n            guard message.content == \"!ping\" else { return }\n            // reply() sets message_reference automatically\n            try? await message.reply(client: client, content: \"🏓 Pong!\")\n        }\n\n        do {\n            try await client.loginAndConnect(intents: [.guilds, .guildMessages, .messageContent])\n        } catch {\n            print(\"❌ Error: \\(error)\")\n        }\n    }\n}\n```\n\nOr use typed filtered streams when you need an event loop:\n\n```swift\nfor await message in await client.messageEvents() {\n    if message.content == \"!ping\" {\n        try? await message.reply(client: client, content: \"🏓 Pong!\")\n    }\n}\n```\n\n## Features\n\n### Core Capabilities\n\n- **Gateway Connection**: WebSocket with automatic heartbeat, session resume, and event streaming\n- **REST API**: Comprehensive coverage of Discord's HTTP endpoints\n- **Rate Limiting**: Automatic per-route and global rate limit handling\n- **Sharding**: Built-in support for large bots with health monitoring\n- **Type Safety**: Strongly typed models throughout with compile-time safety\n- **Cross-Platform**: Works on macOS, iOS, tvOS, watchOS, and Windows\n\n### High-Level Features\n\n- **Command Framework**: Built-in router for prefix and slash commands\n- **Component Builders**: Fluent API for buttons, select menus, embeds, and modals\n- **View Manager**: Persistent UI views with automatic lifecycle management\n- **Collectors**: AsyncStream-based message and component collectors\n- **Extensions/Cogs**: Modular architecture for organizing bot features\n- **Utilities**: Mention formatters, emoji helpers, timestamp formatting, and more\n\n### v2.0 Developer Experience\n\n- **`message.reply()`** — reply to any message in one line, mention control included\n- **`client.sendDM()`** — open a DM and send a message in a single call\n- **Typed slash option accessors** — `ctx.user()`, `ctx.channel()`, `ctx.role()`, `ctx.attachment()`\n- **Filtered event streams** — `client.messageEvents()`, `client.interactionEvents()`, etc.\n- **`EmbedBuilder.timestamp(Date)`** — pass `Date()` directly, no ISO 8601 string needed\n- **Public `CooldownManager`** — use it anywhere in your bot, not just command routers\n- **32 event callbacks** — one `@Sendable` closure per event, no `switch` boilerplate\n- **Background cache eviction** — TTL expiry runs automatically, no manual calls needed\n\n### What's Included\n\nThe REST API covers all essential Discord features:\n\n✅ Messages, embeds, reactions, threads  \n✅ Channels, permissions, webhooks  \n✅ Guilds, members, roles, bans  \n✅ Slash commands, autocomplete, modals  \n✅ Components (buttons, select menus, radio groups, checkbox groups)  \n✅ Modal components: Label, RadioGroup, CheckboxGroup, Checkbox  \n✅ Scheduled events, stage instances  \n✅ Auto-moderation rules  \n✅ Application commands and interactions  \n✅ Gradient role colors and guild tags  \n✅ Voice state REST endpoints  \n✅ Community invite target user management  \n\nFor a complete API checklist, see the [REST API Coverage](#rest-api-coverage) section below.\n\n## Examples\n\n### Command Framework\n\nCreate command-based bots easily with the built-in router:\n\n```swift\nlet router = CommandRouter(prefix: \"!\")\nrouter.register(\"ping\") { ctx in\n    try? await ctx.reply(\"Pong!\")\n}\n\nclient.onMessageCreate { message in\n    await router.processMessage(message)\n}\n```\n\nAdd checks and cooldowns to commands:\n\n```swift\nrouter.register(\"ban\", checks: [isAdminCheck], cooldown: 10.0) { ctx in\n    // Command logic here\n}\n```\n\n[See full example →](Examples/CommandFrameworkBot.swift)\n\n### Slash Commands\n\n```swift\nlet slash = SlashCommandRouter()\nslash.register(\"greet\") { interaction in\n    try await interaction.reply(\"Hello from SwiftDisc!\")\n}\n```\n\n[See full example →](Examples/SlashBot.swift)\n\n### Components \u0026 Embeds\n\nUse the fluent builders to create rich messages:\n\n```swift\nlet embed = EmbedBuilder()\n    .title(\"Welcome!\")\n    .description(\"Thanks for joining our server\")\n    .color(0x5865F2)\n    .timestamp(Date())\n    .build()\n\nlet button = ButtonBuilder()\n    .style(.primary)\n    .label(\"Click me!\")\n    .customId(\"welcome_button\")\n    .build()\n\nlet row = ActionRowBuilder()\n    .addButton(button)\n    .build()\n\ntry await client.sendMessage(\n    channelId: channelId,\n    embeds: [embed],\n    components: [row]\n)\n```\n\n[See full example →](Examples/ComponentsExample.swift)\n\n### Message Collectors\n\nCollect messages or component interactions using AsyncStreams:\n\n```swift\nlet collector = client.createMessageCollector(\n    filter: { $0.author.id == userId \u0026\u0026 $0.channel_id == channelId },\n    timeout: 60.0,\n    max: 1\n)\n\nfor await message in collector {\n    print(\"Received: \\(message.content)\")\n}\n```\n\n### View Manager\n\nCreate persistent interactive UIs with automatic lifecycle management:\n\n```swift\nlet view = BasicView(timeout: 300) { customId, interaction in\n    if customId == \"confirm_button\" {\n        try? await interaction.reply(\"Confirmed!\")\n        return true // Remove view after use\n    }\n    return false\n}\n\nclient.viewManager?.register(view: view, for: messageId)\n```\n\n[See full example →](Examples/ViewExample.swift)\n\n### Extensions/Cogs\n\nOrganize your bot into modular extensions:\n\n```swift\nstruct ModerationCog: Cog {\n    func onLoad(client: DiscordClient) async {\n        print(\"Moderation module loaded\")\n        // Register commands, set up listeners\n    }\n    \n    func onUnload(client: DiscordClient) async {\n        print(\"Moderation module unloaded\")\n    }\n}\n\nlet extensionManager = ExtensionManager()\nawait extensionManager.load(cog: ModerationCog(), client: client)\n```\n\n[See full example →](Examples/CogExample.swift)\n\n## Advanced Features\n\n### Sharding\n\nFor large bots, SwiftDisc handles sharding automatically:\n\n```swift\nlet manager = await ShardingGatewayManager(\n    token: token,\n    configuration: .init(\n        shardCount: .automatic,\n        connectionDelay: .staggered(interval: 1.5)\n    ),\n    intents: [.guilds, .guildMessages]\n)\n\ntry await manager.connect()\n\nlet health = await manager.healthCheck()\nprint(\"Shards: \\(health.readyShards)/\\(health.totalShards) ready\")\n```\n\n### Voice Support (Experimental)\n\nConnect to voice channels and send audio:\n\n```swift\n// Enable voice in config\nlet config = DiscordConfiguration(enableVoiceExperimental: true)\nlet client = DiscordClient(token: token, configuration: config)\n\n// Join a voice channel\ntry await client.joinVoice(guildId: guildId, channelId: voiceChannelId)\n\n// Send Opus audio\ntry await client.playVoiceOpus(guildId: guildId, data: opusPacket)\n\n// Or use an audio source\ntry await client.play(source: audioSource, guildId: guildId)\n```\n\n### File Uploads\n\nSend files with messages and interactions:\n\n```swift\nlet file = FileAttachment(\n    filename: \"image.png\",\n    data: imageData,\n    contentType: \"image/png\"\n)\n\ntry await client.sendMessage(\n    channelId: channelId,\n    content: \"Check out this image!\",\n    files: [file]\n)\n```\n\nFor interaction responses:\n\n```swift\ntry await client.createInteractionResponseWithFiles(\n    applicationId: appId,\n    interactionToken: token,\n    payload: responsePayload,\n    files: [file]\n)\n```\n\n### Utilities\n\nSwiftDisc includes helpful utilities for common tasks:\n\n```swift\n// Mention formatting\nMentions.user(userId)           // \u003c@123456\u003e\nMentions.channel(channelId)     // \u003c#123456\u003e\nMentions.role(roleId)           // \u003c@\u0026123456\u003e\n\n// Custom emoji\nEmojiUtils.custom(name: \"party\", id: emojiId, animated: true)\n\n// Timestamp formatting\nDiscordTimestamp.format(date: Date(), style: .relative)\n\n// Escape special characters\nMessageFormat.escapeSpecialCharacters(userInput)\n```\n\n## REST API Coverage\n\n### Messages\n✅ Send, edit, delete messages  \n✅ Reactions (add, remove, remove all)  \n✅ Embeds, components, attachments  \n✅ Pins, bulk delete  \n✅ Crosspost, polls  \n✅ Forward messages  \n\n### Channels\n✅ Create, modify, delete channels  \n✅ Permissions, invites  \n✅ Webhooks (full CRUD + execute)  \n✅ Typing indicators  \n✅ Threads (create, archive, members)  \n\n### Guilds\n✅ Create, modify, delete guilds  \n✅ Channels, roles, emojis  \n✅ Members (add, remove, modify, timeout)  \n✅ Bans, prune, audit logs  \n✅ Widget, preview, vanity URL  \n✅ Templates  \n\n### Roles\n✅ List, create, modify, delete roles  \n✅ Fetch individual role (`getGuildRole`)  \n✅ Gradient role colors (`RoleColors`, `RoleColorStop`)  \n\n### Interactions\n✅ Slash commands (global \u0026 guild)  \n✅ Autocomplete  \n✅ Modals and components  \n✅ Interaction responses (including `launchActivity` type 12)  \n✅ Follow-up messages  \n✅ Command localization  \n✅ Modal components: Label (21), RadioGroup (22), CheckboxGroup (23), Checkbox (24)  \n\n### Users \u0026 Members\n✅ Get/modify current user  \n✅ Guild tag (`primary_guild`), banner, accent color, flags  \n✅ Modify current member (nick, avatar, banner, bio)  \n\n### Invites\n✅ Create, list, get, delete invites  \n✅ Community invite role assigning (`role_ids`)  \n✅ Target user list management (`getInviteTargetUsers`, `updateInviteTargetUsers`, `getInviteTargetUsersJobStatus`)  \n\n### Voice\n✅ Join/leave voice channels (Gateway)  \n✅ Get voice states via REST (`getCurrentUserVoiceState`, `getUserVoiceState`)  \n✅ Opus audio playback (experimental)  \n\n### Subscriptions \u0026 Monetization\n✅ App subscriptions with `renewal_sku_ids`  \n\n### Other Features\n✅ Scheduled events  \n✅ Stage instances  \n✅ Auto-moderation rules  \n✅ Application emojis  \n✅ Role connections (linked roles)  \n✅ Sticker info (read-only)  \n\n### Soundboard\n✅ Send soundboard sounds  \n✅ List, create, modify, delete guild soundboard sounds  \n✅ Soundboard gateway events (`SOUNDBOARD_SOUND_CREATE/UPDATE/DELETE`)  \n\n### Not Yet Implemented\n❌ Guild sticker creation/modification  \n\nFor unsupported endpoints, use the raw HTTP methods: `rawGET`, `rawPOST`, `rawPATCH`, `rawDELETE`\n\n## More Examples\n\nCheck out the [Examples](Examples) directory for complete, runnable examples:\n\n- [PingBot.swift](Examples/PingBot.swift) - Simple message responder\n- [CommandFrameworkBot.swift](Examples/CommandFrameworkBot.swift) - Command routing with checks\n- [SlashBot.swift](Examples/SlashBot.swift) - Slash command handling\n- [AutocompleteBot.swift](Examples/AutocompleteBot.swift) - Autocomplete interactions\n- [ComponentsExample.swift](Examples/ComponentsExample.swift) - Buttons, selects, and embeds\n- [ViewExample.swift](Examples/ViewExample.swift) - Persistent interactive views\n- [CogExample.swift](Examples/CogExample.swift) - Modular bot architecture\n- [FileUploadBot.swift](Examples/FileUploadBot.swift) - Sending files\n- [ThreadsAndScheduledEventsBot.swift](Examples/ThreadsAndScheduledEventsBot.swift) - Thread and event handling\n- [VoiceStdin.swift](Examples/VoiceStdin.swift) - Voice playback (experimental)\n- [LinkedRolesBot.swift](Examples/LinkedRolesBot.swift) - Role connections\n\n## Documentation\n\n- **[Wiki](https://github.com/M1tsumi/SwiftDisc/wiki)** - Setup guides, concepts, and deployment tips\n- **[Examples](https://github.com/M1tsumi/SwiftDisc/tree/main/Examples)** - Complete working examples\n- **[Discord Server](https://discord.gg/6nS2KqxQtj)** - Get help and discuss the library\n- **[Changelog](CHANGELOG.md)** - Version history and migration guides\n\n## Building \u0026 Testing\n\n```bash\n# Build the library\nswift build\n\n# Run tests\nswift test\n\n# With code coverage (macOS)\nswift test --enable-code-coverage\n```\n\nCI runs on macOS (Xcode 16.4) and Windows (Swift 6.2). Requires Swift 6.2+ toolchain.\n\n## Contributing\n\nWe welcome contributions! Whether it's bug reports, feature requests, or pull requests, we'd love your help making SwiftDisc better.\n\nBefore contributing, please:\n- Check existing issues and PRs to avoid duplicates\n- Open a discussion issue for significant changes before starting work\n- Join our [Discord server](https://discord.gg/6nS2KqxQtj) if you have questions\n\n## Roadmap\n\n### Current Version: v2.0.0\n\nMajor release delivering Swift 6 strict-concurrency, typed throws throughout the\nREST layer, 32 new event callbacks, full Guild model, critical bug fixes, and\nhigh-impact DX improvements (`message.reply()`, `sendDM()`, typed slash accessors,\nfiltered event streams, background cache eviction). See [CHANGELOG.md](CHANGELOG.md).\n\n### Upcoming\n\n- Full Components V2 fluent builders (MediaGallery, Section, Container, Separator)\n- Guild sticker creation and modification\n- Enhanced and stable voice support\n- Expanded test coverage across REST and Gateway layers\n\nHave ideas? Open an issue or join the discussion on Discord!\n\n## Help\n\nIf you need help or have questions:\n\n- Check the [Wiki](https://github.com/M1tsumi/SwiftDisc/wiki) for guides and documentation\n- Browse [Examples](https://github.com/M1tsumi/SwiftDisc/tree/main/Examples) for code samples\n- Join our [Discord server](https://discord.gg/6nS2KqxQtj) for live help\n- Search [existing issues](https://github.com/M1tsumi/SwiftDisc/issues) for similar questions\n\n## License\n\nSwiftDisc is released under the MIT License. See [LICENSE](LICENSE) for details.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**Built with ❤️ using Swift**\n\n[Documentation](https://github.com/M1tsumi/SwiftDisc/wiki) · [Discord Server](https://discord.gg/6nS2KqxQtj) · [Examples](https://github.com/M1tsumi/SwiftDisc/tree/main/Examples)\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FM1tsumi%2FSwiftDisc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FM1tsumi%2FSwiftDisc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FM1tsumi%2FSwiftDisc/lists"}