├── .gitignore ├── Examples ├── Basic Commands.swift ├── Buttons.swift ├── Select Menu.swift └── Slash Commands.swift ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── Swiftcord │ ├── Gateway │ ├── EventHandler.swift │ ├── Gateway.swift │ ├── GatewayHandler.swift │ ├── Heartbeat.swift │ ├── ListenerAdapter.swift │ ├── Payload.swift │ ├── Shard.swift │ └── ShardManager.swift │ ├── Rest │ ├── EndpointInfo.swift │ ├── Endpoints.swift │ ├── MultipartBody.swift │ ├── RateLimit.swift │ └── Request.swift │ ├── Swiftcord.swift │ ├── Types │ ├── Activities.swift │ ├── AuditLog.swift │ ├── Channel.swift │ ├── DM.swift │ ├── EmbedBuilder.swift │ ├── Emoji.swift │ ├── GroupDM.swift │ ├── Guild.swift │ ├── GuildCategory.swift │ ├── GuildText.swift │ ├── GuildVoice.swift │ ├── Icon.swift │ ├── Interactions │ │ ├── ActionRow.swift │ │ ├── ApplicationCommands.swift │ │ ├── Button.swift │ │ ├── Component.swift │ │ ├── Events │ │ │ ├── ButtonEvent.swift │ │ │ ├── InteractionEvent.swift │ │ │ ├── InteractionResponse.swift │ │ │ ├── MessageCommandEvent.swift │ │ │ ├── SelectMenuEvent.swift │ │ │ ├── SlashCommandEvent.swift │ │ │ ├── TextInputEvent.swift │ │ │ └── UserCommandEvent.swift │ │ ├── MessageCommand.swift │ │ ├── Modal.swift │ │ ├── SelectMenu.swift │ │ ├── SlashCommands.swift │ │ ├── TextInput.swift │ │ └── UserCommand.swift │ ├── Invite.swift │ ├── Member.swift │ ├── Message.swift │ ├── Options.swift │ ├── Role.swift │ ├── ScheduledEvent.swift │ ├── Snowflake.swift │ ├── Stage.swift │ ├── Sticker.swift │ ├── Thread.swift │ ├── User.swift │ ├── VoiceState.swift │ └── Webhook.swift │ └── Utils │ ├── Bucket.swift │ ├── Enums.swift │ ├── Error.swift │ ├── Imageable.swift │ ├── JSON.swift │ ├── Log.swift │ ├── Updatable.swift │ └── Utils.swift ├── Tests └── SwiftcordTests │ └── SwiftcordTests.swift ├── credits.md ├── docs ├── css │ └── site.css ├── documentation │ └── swiftcord │ │ ├── actionrow │ │ ├── components.html │ │ ├── index.html │ │ ├── init(components:).html │ │ └── type.html │ │ ├── activities │ │ ├── index.html │ │ ├── init(name:type:url:).html │ │ ├── name.html │ │ ├── type.html │ │ └── url.html │ │ ├── activitytype │ │ ├── !=(_:_:).html │ │ ├── competing.html │ │ ├── custom.html │ │ ├── encode(to:).html │ │ ├── equatable-implementations.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── listening.html │ │ ├── playing.html │ │ ├── rawrepresentable-implementations.html │ │ ├── streaming.html │ │ └── watching.html │ │ ├── applicationchoices │ │ ├── index.html │ │ ├── name.html │ │ └── value.html │ │ ├── applicationcommandoptions │ │ ├── addchoice(name:value:).html │ │ ├── addchoices(choices:).html │ │ ├── autocomplete.html │ │ ├── channeltypes.html │ │ ├── choices.html │ │ ├── description.html │ │ ├── index.html │ │ ├── init(name:description:type:).html │ │ ├── name.html │ │ ├── required.html │ │ ├── setrequired(required:).html │ │ └── type.html │ │ ├── applicationcommandtype │ │ ├── !=(_:_:).html │ │ ├── bool.html │ │ ├── channel.html │ │ ├── encode(to:).html │ │ ├── equatable-implementations.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── int.html │ │ ├── mentionable.html │ │ ├── number.html │ │ ├── rawrepresentable-implementations.html │ │ ├── role.html │ │ ├── string.html │ │ ├── subcommand.html │ │ ├── subcommandgroup.html │ │ └── user.html │ │ ├── applicationtype │ │ ├── !=(_:_:).html │ │ ├── encode(to:).html │ │ ├── equatable-implementations.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── messagecommand.html │ │ ├── rawrepresentable-implementations.html │ │ ├── slashcommand.html │ │ └── usercommand.html │ │ ├── attachment │ │ ├── filename.html │ │ ├── height.html │ │ ├── id.html │ │ ├── index.html │ │ ├── proxyurl.html │ │ ├── size.html │ │ ├── url.html │ │ └── width.html │ │ ├── auditlog │ │ ├── entries.html │ │ ├── entry │ │ │ ├── actiontype.html │ │ │ ├── change │ │ │ │ ├── index.html │ │ │ │ ├── key.html │ │ │ │ ├── newvalue.html │ │ │ │ └── oldvalue.html │ │ │ ├── changes.html │ │ │ ├── event │ │ │ │ ├── !=(_:_:).html │ │ │ │ ├── botadd.html │ │ │ │ ├── channelcreate.html │ │ │ │ ├── channeldelete.html │ │ │ │ ├── channeloverwritecreate.html │ │ │ │ ├── channeloverwritedelete.html │ │ │ │ ├── channeloverwriteupdate.html │ │ │ │ ├── channelupdate.html │ │ │ │ ├── emojicreate.html │ │ │ │ ├── emojidelete.html │ │ │ │ ├── emojiupdate.html │ │ │ │ ├── equatable-implementations.html │ │ │ │ ├── guildscheduledeventcreate.html │ │ │ │ ├── guildscheduledeventdelete.html │ │ │ │ ├── guildscheduledeventupdate.html │ │ │ │ ├── guildupdate.html │ │ │ │ ├── hash(into:).html │ │ │ │ ├── hashvalue.html │ │ │ │ ├── index.html │ │ │ │ ├── integrationcreate.html │ │ │ │ ├── integrationdelete.html │ │ │ │ ├── integrationupdate.html │ │ │ │ ├── invitecreate.html │ │ │ │ ├── invitedelete.html │ │ │ │ ├── inviteupdate.html │ │ │ │ ├── memberbanadd.html │ │ │ │ ├── memberbanremove.html │ │ │ │ ├── memberdisconnect.html │ │ │ │ ├── memberkick.html │ │ │ │ ├── membermove.html │ │ │ │ ├── memberprune.html │ │ │ │ ├── memberroleupdate.html │ │ │ │ ├── memberupdate.html │ │ │ │ ├── messagebulkdelete.html │ │ │ │ ├── messagedelete.html │ │ │ │ ├── messagepin.html │ │ │ │ ├── messageunpin.html │ │ │ │ ├── rawrepresentable-implementations.html │ │ │ │ ├── rolecreate.html │ │ │ │ ├── roledelete.html │ │ │ │ ├── roleupdate.html │ │ │ │ ├── stagecreate.html │ │ │ │ ├── stagedelete.html │ │ │ │ ├── stageupdate.html │ │ │ │ ├── stickercreate.html │ │ │ │ ├── stickerdelete.html │ │ │ │ ├── stickerupdate.html │ │ │ │ ├── threadcreate.html │ │ │ │ ├── threaddelete.html │ │ │ │ ├── threadupdate.html │ │ │ │ ├── webhookcreate.html │ │ │ │ ├── webhookdelete.html │ │ │ │ └── webhookupdate.html │ │ │ ├── id.html │ │ │ ├── index.html │ │ │ ├── options.html │ │ │ ├── reason.html │ │ │ ├── targetid.html │ │ │ └── userid.html │ │ ├── guildscheduledevents.html │ │ ├── index.html │ │ ├── threads.html │ │ ├── users.html │ │ └── webhooks.html │ │ ├── button │ │ ├── customid.html │ │ ├── disabled.html │ │ ├── emoji.html │ │ ├── index.html │ │ ├── init(customid:disabled:style:label:emoji:url:).html │ │ ├── label.html │ │ ├── style.html │ │ ├── type.html │ │ └── url.html │ │ ├── buttonbuilder │ │ ├── addcomponent(component:).html │ │ ├── components.html │ │ ├── content.html │ │ ├── embeds.html │ │ ├── index.html │ │ └── init(message:embed:).html │ │ ├── buttoncomponentdata │ │ ├── componenttype.html │ │ ├── customid.html │ │ └── index.html │ │ ├── buttonevent │ │ ├── channelid.html │ │ ├── deferreply(_:).html │ │ ├── delete(_:).html │ │ ├── edit(message:then:).html │ │ ├── editwithbuttons(buttons:then:).html │ │ ├── editwithembeds(embeds:then:).html │ │ ├── editwithselectmenu(menu:then:).html │ │ ├── ephemeral.html │ │ ├── guild.html │ │ ├── index.html │ │ ├── interactionevent-implementations.html │ │ ├── interactionid.html │ │ ├── isdefered.html │ │ ├── member.html │ │ ├── reply(message:then:).html │ │ ├── replybuttons(buttons:then:).html │ │ ├── replyembeds(embeds:then:).html │ │ ├── replyselectmenu(menu:then:).html │ │ ├── selectedbutton.html │ │ ├── setephemeral(isephermeral:).html │ │ ├── swiftcord.html │ │ ├── token.html │ │ └── user.html │ │ ├── buttonstyles │ │ ├── !=(_:_:).html │ │ ├── blurple.html │ │ ├── encode(to:).html │ │ ├── equatable-implementations.html │ │ ├── green.html │ │ ├── grey.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── rawrepresentable-implementations.html │ │ ├── red.html │ │ └── url.html │ │ ├── channel │ │ ├── delete(then:).html │ │ ├── id.html │ │ ├── index.html │ │ ├── swiftcord.html │ │ └── type.html │ │ ├── channeltype │ │ ├── !=(_:_:).html │ │ ├── dm.html │ │ ├── equatable-implementations.html │ │ ├── groupdm.html │ │ ├── guildcategory.html │ │ ├── guildnews.html │ │ ├── guildnewsthread.html │ │ ├── guildprivatethread.html │ │ ├── guildpublicthread.html │ │ ├── guildstage.html │ │ ├── guildstore.html │ │ ├── guildtext.html │ │ ├── guildvoice.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ └── rawrepresentable-implementations.html │ │ ├── commandable │ │ ├── execute(_:_:).html │ │ ├── index.html │ │ ├── name.html │ │ └── options.html │ │ ├── commandoptions │ │ ├── aliases.html │ │ ├── description.html │ │ ├── index.html │ │ ├── init(aliases:description:iscasesensitive:requirements:).html │ │ ├── iscasesensitive.html │ │ └── requirements.html │ │ ├── commandrequirements │ │ ├── channels.html │ │ ├── guilds.html │ │ ├── index.html │ │ ├── init(channels:guilds:permissions:users:).html │ │ ├── permissions.html │ │ └── users.html │ │ ├── component │ │ ├── index.html │ │ └── type.html │ │ ├── componenttypes │ │ ├── !=(_:_:).html │ │ ├── actionrow.html │ │ ├── button.html │ │ ├── encode(to:).html │ │ ├── equatable-implementations.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── rawrepresentable-implementations.html │ │ └── selectmenu.html │ │ ├── dm │ │ ├── addreaction(_:to:then:).html │ │ ├── channel-implementations.html │ │ ├── delete(then:).html │ │ ├── deletemessage(_:then:).html │ │ ├── deletemessages(_:then:).html │ │ ├── deletereaction(_:from:by:then:).html │ │ ├── editmessage(_:with:then:).html │ │ ├── getmessage(_:then:).html │ │ ├── getmessages(with:then:).html │ │ ├── getpinnedmessages(then:).html │ │ ├── getreaction(_:from:then:).html │ │ ├── id.html │ │ ├── index.html │ │ ├── lastmessageid.html │ │ ├── pin(_:then:).html │ │ ├── recipient.html │ │ ├── send(_:then:)-1ym1q.html │ │ ├── send(_:then:)-39qc9.html │ │ ├── send(_:then:)-4gome.html │ │ ├── send(_:then:)-7gex2.html │ │ ├── send(_:then:)-97v6d.html │ │ ├── swiftcord.html │ │ ├── textchannel-implementations.html │ │ ├── type.html │ │ └── unpin(_:then:).html │ │ ├── embed │ │ ├── addfield(_:value:isinline:).html │ │ ├── author-swift.property.html │ │ ├── author-swift.struct │ │ │ ├── iconurl.html │ │ │ ├── index.html │ │ │ ├── init(iconurl:name:url:).html │ │ │ ├── name.html │ │ │ └── url.html │ │ ├── color.html │ │ ├── description.html │ │ ├── encode().html │ │ ├── field │ │ │ ├── index.html │ │ │ ├── init(isinline:name:value:).html │ │ │ ├── isinline.html │ │ │ ├── name.html │ │ │ └── value.html │ │ ├── fields.html │ │ ├── footer-swift.property.html │ │ ├── footer-swift.struct │ │ │ ├── iconurl.html │ │ │ ├── index.html │ │ │ ├── init(text:iconurl:proxyiconurl:).html │ │ │ ├── proxyiconurl.html │ │ │ └── text.html │ │ ├── image-swift.property.html │ │ ├── image-swift.struct │ │ │ ├── height.html │ │ │ ├── index.html │ │ │ ├── init(height:proxyurl:url:width:).html │ │ │ ├── proxyurl.html │ │ │ ├── url.html │ │ │ └── width.html │ │ ├── index.html │ │ ├── init().html │ │ ├── provider-swift.property.html │ │ ├── provider-swift.struct │ │ │ ├── index.html │ │ │ ├── init(name:url:).html │ │ │ ├── name.html │ │ │ └── url.html │ │ ├── thumbnail-swift.property.html │ │ ├── thumbnail-swift.struct │ │ │ ├── height.html │ │ │ ├── index.html │ │ │ ├── init(height:proxyurl:url:width:).html │ │ │ ├── proxyurl.html │ │ │ ├── url.html │ │ │ └── width.html │ │ ├── title.html │ │ ├── type.html │ │ ├── url.html │ │ ├── video-swift.property.html │ │ └── video-swift.struct │ │ │ ├── height.html │ │ │ ├── index.html │ │ │ ├── url.html │ │ │ └── width.html │ │ ├── embedbuilder │ │ ├── addfield(_:value:isinline:).html │ │ ├── author-swift.property.html │ │ ├── author-swift.struct │ │ │ ├── iconurl.html │ │ │ ├── index.html │ │ │ ├── init(iconurl:name:url:).html │ │ │ ├── name.html │ │ │ └── url.html │ │ ├── color.html │ │ ├── description.html │ │ ├── field │ │ │ ├── index.html │ │ │ ├── init(name:value:isinline:).html │ │ │ ├── isinline.html │ │ │ ├── name.html │ │ │ └── value.html │ │ ├── fields.html │ │ ├── footer-swift.property.html │ │ ├── footer-swift.struct │ │ │ ├── iconurl.html │ │ │ ├── index.html │ │ │ ├── init(text:iconurl:).html │ │ │ └── text.html │ │ ├── image-swift.property.html │ │ ├── image-swift.struct │ │ │ ├── height.html │ │ │ ├── index.html │ │ │ ├── init(url:height:width:).html │ │ │ ├── url.html │ │ │ └── width.html │ │ ├── index.html │ │ ├── init().html │ │ ├── setauthor(name:url:iconurl:).html │ │ ├── setcolor(color:).html │ │ ├── setdescription(description:).html │ │ ├── setfooter(text:url:).html │ │ ├── setimage(url:height:width:).html │ │ ├── setthumbnail(url:height:width:).html │ │ ├── settitle(title:).html │ │ ├── setvideo(url:height:width:).html │ │ ├── thumbnail-swift.property.html │ │ ├── thumbnail-swift.struct │ │ │ ├── height.html │ │ │ ├── index.html │ │ │ ├── init(url:height:width:).html │ │ │ ├── url.html │ │ │ └── width.html │ │ ├── title.html │ │ ├── type.html │ │ ├── url.html │ │ ├── video-swift.property.html │ │ └── video-swift.struct │ │ │ ├── height.html │ │ │ ├── index.html │ │ │ ├── init(url:height:width:).html │ │ │ ├── url.html │ │ │ └── width.html │ │ ├── emoji │ │ ├── id.html │ │ ├── imageurl(format:).html │ │ ├── index.html │ │ ├── init(_:id:).html │ │ ├── isanimated.html │ │ ├── ismanaged.html │ │ ├── name.html │ │ ├── requirescolons.html │ │ ├── rolesarray.html │ │ └── tag.html │ │ ├── event │ │ ├── !=(_:_:).html │ │ ├── audiodata.html │ │ ├── buttonevent.html │ │ ├── channelcreate.html │ │ ├── channeldelete.html │ │ ├── channelpinsupdate.html │ │ ├── channelupdate.html │ │ ├── connectionclose.html │ │ ├── disconnect.html │ │ ├── equatable-implementations.html │ │ ├── guildavailable.html │ │ ├── guildbanadd.html │ │ ├── guildbanremove.html │ │ ├── guildcreate.html │ │ ├── guilddelete.html │ │ ├── guildemojisupdate.html │ │ ├── guildintegrationsupdate.html │ │ ├── guildmemberadd.html │ │ ├── guildmemberremove.html │ │ ├── guildmemberschunk.html │ │ ├── guildmemberupdate.html │ │ ├── guildrolecreate.html │ │ ├── guildroledelete.html │ │ ├── guildroleupdate.html │ │ ├── guildunavailable.html │ │ ├── guildupdate.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── interaction.html │ │ ├── messagecommandevent.html │ │ ├── messagecreate.html │ │ ├── messagedelete.html │ │ ├── messagedeletebulk.html │ │ ├── messagereactionremoveall.html │ │ ├── messageupdate.html │ │ ├── payload.html │ │ ├── presenceupdate.html │ │ ├── rawrepresentable-implementations.html │ │ ├── reactionadd.html │ │ ├── reactionremove.html │ │ ├── ready.html │ │ ├── resume.html │ │ ├── resumed.html │ │ ├── selectmenuevent.html │ │ ├── shardready.html │ │ ├── slashcommandevent.html │ │ ├── threadcreate.html │ │ ├── threaddelete.html │ │ ├── threadupdate.html │ │ ├── typingstart.html │ │ ├── usercommandevent.html │ │ ├── userupdate.html │ │ ├── voicechanneljoin.html │ │ ├── voicechannelleave.html │ │ ├── voiceserverupdate.html │ │ └── voicestateupdate.html │ │ ├── eventable │ │ ├── emit(_:with:)-27txi.html │ │ ├── emit(_:with:)-72bfs.html │ │ ├── index.html │ │ ├── listeners.html │ │ ├── on(_:do:)-7cw7c.html │ │ └── on(_:do:)-8w6i1.html │ │ ├── fileextension │ │ ├── !=(_:_:).html │ │ ├── equatable-implementations.html │ │ ├── gif.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── jpg.html │ │ ├── png.html │ │ ├── rawrepresentable-implementations.html │ │ └── webp.html │ │ ├── genericcommand │ │ ├── execute(_:_:).html │ │ ├── function.html │ │ ├── index.html │ │ ├── name.html │ │ └── options.html │ │ ├── groupdm │ │ ├── addreaction(_:to:then:).html │ │ ├── channel-implementations.html │ │ ├── delete(then:).html │ │ ├── deletemessage(_:then:).html │ │ ├── deletemessages(_:then:).html │ │ ├── deletereaction(_:from:by:then:).html │ │ ├── editmessage(_:with:then:).html │ │ ├── getmessage(_:then:).html │ │ ├── getmessages(with:then:).html │ │ ├── getpinnedmessages(then:).html │ │ ├── getreaction(_:from:then:).html │ │ ├── id.html │ │ ├── index.html │ │ ├── lastmessageid.html │ │ ├── pin(_:then:).html │ │ ├── recipients.html │ │ ├── send(_:then:)-1j6ip.html │ │ ├── send(_:then:)-3mu58.html │ │ ├── send(_:then:)-6jg3z.html │ │ ├── send(_:then:)-7oxq8.html │ │ ├── send(_:then:)-9cs2g.html │ │ ├── swiftcord.html │ │ ├── textchannel-implementations.html │ │ ├── type.html │ │ └── unpin(_:then:).html │ │ ├── guild │ │ ├── afkchannelid.html │ │ ├── afktimeout.html │ │ ├── ban(_:for:with:then:).html │ │ ├── botmember.html │ │ ├── channels.html │ │ ├── createchannel(with:then:).html │ │ ├── createevent(_:then:).html │ │ ├── createintegration(with:then:).html │ │ ├── createrole(with:then:).html │ │ ├── defaultmessagenotifications.html │ │ ├── delete(then:).html │ │ ├── deleteapplicationcommand(commandid:then:).html │ │ ├── deleteemoji(_:reason:then:).html │ │ ├── deleteintegration(_:then:).html │ │ ├── deleterole(_:then:).html │ │ ├── discoverysplash.html │ │ ├── embedchannelid.html │ │ ├── emojis.html │ │ ├── feature │ │ │ ├── !=(_:_:).html │ │ │ ├── equatable-implementations.html │ │ │ ├── hash(into:).html │ │ │ ├── hashvalue.html │ │ │ ├── index.html │ │ │ ├── invitesplash.html │ │ │ ├── rawrepresentable-implementations.html │ │ │ ├── vanityurl.html │ │ │ ├── verified.html │ │ │ └── vipregions.html │ │ ├── features.html │ │ ├── getauditlog(with:then:).html │ │ ├── getbans(then:).html │ │ ├── getembed(then:).html │ │ ├── getemoji(emojiid:then:).html │ │ ├── getemojis(then:).html │ │ ├── getintegrations(then:).html │ │ ├── getinvites(then:).html │ │ ├── getmembers(with:then:).html │ │ ├── getprunecount(for:then:).html │ │ ├── getroles(then:).html │ │ ├── getscheduledevents(then:).html │ │ ├── getsticker(stickerid:then:).html │ │ ├── getstickers(then:).html │ │ ├── getvoiceregions(then:).html │ │ ├── getwebhooks(then:).html │ │ ├── icon.html │ │ ├── id.html │ │ ├── imageurl(format:).html │ │ ├── index.html │ │ ├── isembedenabled.html │ │ ├── islarge.html │ │ ├── iswidgetenabled.html │ │ ├── joinedat.html │ │ ├── kick(_:for:then:).html │ │ ├── membercount.html │ │ ├── members.html │ │ ├── mfalevel-swift.enum │ │ │ ├── !=(_:_:).html │ │ │ ├── elevated.html │ │ │ ├── equatable-implementations.html │ │ │ ├── hash(into:).html │ │ │ ├── hashvalue.html │ │ │ ├── index.html │ │ │ ├── none.html │ │ │ └── rawrepresentable-implementations.html │ │ ├── mfalevel-swift.property.html │ │ ├── modify(with:then:).html │ │ ├── modifychannelpositions(with:then:).html │ │ ├── modifyembed(with:then:).html │ │ ├── modifyemoji(emojiid:with:reason:then:).html │ │ ├── modifyintegration(_:with:then:).html │ │ ├── modifymember(_:with:then:).html │ │ ├── modifyrole(_:with:then:).html │ │ ├── modifyrolepositions(with:then:).html │ │ ├── movemember(_:to:then:).html │ │ ├── name.html │ │ ├── ownerid.html │ │ ├── prunemembers(for:then:).html │ │ ├── removetimeoutfromuser(_:then:).html │ │ ├── roles.html │ │ ├── shard.html │ │ ├── splash.html │ │ ├── swiftcord.html │ │ ├── syncintegration(_:then:).html │ │ ├── threads.html │ │ ├── timeoutuser(_:until:reason:then:).html │ │ ├── unbanmember(_:then:).html │ │ ├── uploademoji(name:emoji:roles:then:).html │ │ ├── uploadmessagecommand(commanddata:then:).html │ │ ├── uploadslashcommand(commanddata:then:).html │ │ ├── uploadusercommand(commanddata:then:).html │ │ ├── verificationlevel-swift.enum │ │ │ ├── !=(_:_:).html │ │ │ ├── equatable-implementations.html │ │ │ ├── hash(into:).html │ │ │ ├── hashvalue.html │ │ │ ├── high.html │ │ │ ├── index.html │ │ │ ├── low.html │ │ │ ├── medium.html │ │ │ ├── none.html │ │ │ ├── rawrepresentable-implementations.html │ │ │ └── veryhigh.html │ │ ├── verificationlevel-swift.property.html │ │ ├── voicestates.html │ │ └── widgetchannelid.html │ │ ├── guildcategory │ │ ├── category.html │ │ ├── channel-implementations.html │ │ ├── channels.html │ │ ├── delete(then:).html │ │ ├── guild.html │ │ ├── id.html │ │ ├── index.html │ │ ├── name.html │ │ ├── parentid.html │ │ ├── permissionoverwrites.html │ │ ├── position.html │ │ ├── swiftcord.html │ │ └── type.html │ │ ├── guildchannel │ │ ├── category.html │ │ ├── guild.html │ │ ├── index.html │ │ ├── name.html │ │ ├── parentid.html │ │ ├── permissionoverwrites.html │ │ └── position.html │ │ ├── guildtext │ │ ├── addreaction(_:to:then:).html │ │ ├── category.html │ │ ├── channel-implementations.html │ │ ├── createwebhook(with:then:).html │ │ ├── delete(then:).html │ │ ├── deletemessage(_:then:).html │ │ ├── deletemessages(_:then:).html │ │ ├── deletereaction(_:from:by:then:).html │ │ ├── deletereactions(from:then:).html │ │ ├── editmessage(_:with:then:).html │ │ ├── getmessage(_:then:).html │ │ ├── getmessages(with:then:).html │ │ ├── getpinnedmessages(then:).html │ │ ├── getreaction(_:from:then:).html │ │ ├── getwebhooks(then:).html │ │ ├── guild.html │ │ ├── id.html │ │ ├── index.html │ │ ├── isnsfw.html │ │ ├── lastmessageid.html │ │ ├── lastpintimestamp.html │ │ ├── name.html │ │ ├── parentid.html │ │ ├── permissionoverwrites.html │ │ ├── pin(_:then:).html │ │ ├── position.html │ │ ├── send(_:then:)-1taxk.html │ │ ├── send(_:then:)-6474b.html │ │ ├── send(_:then:)-6lqug.html │ │ ├── send(_:then:)-6zf4v.html │ │ ├── send(_:then:)-8u9f9.html │ │ ├── swiftcord.html │ │ ├── textchannel-implementations.html │ │ ├── topic.html │ │ ├── type.html │ │ └── unpin(_:then:).html │ │ ├── guildvoice │ │ ├── bitrate.html │ │ ├── category.html │ │ ├── channel-implementations.html │ │ ├── delete(then:).html │ │ ├── guild.html │ │ ├── id.html │ │ ├── index.html │ │ ├── movemember(_:then:).html │ │ ├── name.html │ │ ├── parentid.html │ │ ├── permissionoverwrites.html │ │ ├── position.html │ │ ├── swiftcord.html │ │ ├── type.html │ │ └── userlimit.html │ │ ├── icon │ │ ├── base64image.html │ │ ├── imagetype.html │ │ ├── index.html │ │ └── init(imagetype:image:).html │ │ ├── imageable │ │ ├── imageurl(format:).html │ │ └── index.html │ │ ├── imagetype │ │ ├── !=(_:_:).html │ │ ├── equatable-implementations.html │ │ ├── gif.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── jpeg.html │ │ ├── png.html │ │ ├── rawrepresentable-implementations.html │ │ └── webp.html │ │ ├── index.html │ │ ├── intents │ │ ├── !=(_:_:).html │ │ ├── directmessages.html │ │ ├── directmessagesreactions.html │ │ ├── directmessagestyping.html │ │ ├── equatable-implementations.html │ │ ├── guildbans.html │ │ ├── guildemojisandstickers.html │ │ ├── guildintegrations.html │ │ ├── guildinvites.html │ │ ├── guildmembers.html │ │ ├── guildmessagereactions.html │ │ ├── guildmessages.html │ │ ├── guildmessagetyping.html │ │ ├── guildpresences.html │ │ ├── guilds.html │ │ ├── guildscheduledevents.html │ │ ├── guildvoicestates.html │ │ ├── guildwebhooks.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ └── rawrepresentable-implementations.html │ │ ├── interactioncallbacktype │ │ ├── !=(_:_:).html │ │ ├── defer.html │ │ ├── defersilently.html │ │ ├── encode(to:).html │ │ ├── equatable-implementations.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── pong.html │ │ ├── rawrepresentable-implementations.html │ │ ├── sendmessage.html │ │ └── updatemessage.html │ │ ├── interactionevent │ │ ├── deferreply(_:).html │ │ ├── delete(_:).html │ │ ├── edit(message:then:).html │ │ ├── editwithbuttons(buttons:then:).html │ │ ├── editwithembeds(embeds:then:).html │ │ ├── editwithselectmenu(menu:then:).html │ │ ├── ephemeral.html │ │ ├── index.html │ │ ├── interactionid.html │ │ ├── isdefered.html │ │ ├── reply(message:then:).html │ │ ├── replybuttons(buttons:then:).html │ │ ├── replyembeds(embeds:then:).html │ │ ├── replyselectmenu(menu:then:).html │ │ ├── setephemeral(isephermeral:).html │ │ ├── swiftcord.html │ │ └── token.html │ │ ├── invite │ │ ├── channel.html │ │ ├── code.html │ │ ├── guild.html │ │ └── index.html │ │ ├── member │ │ ├── avatar.html │ │ ├── communicationdisableduntil.html │ │ ├── guild.html │ │ ├── haspermission(_:).html │ │ ├── index.html │ │ ├── isdeaf.html │ │ ├── ismuted.html │ │ ├── ispending.html │ │ ├── joinedat.html │ │ ├── nick.html │ │ ├── permissions.html │ │ ├── premiumsince.html │ │ ├── presence.html │ │ ├── roles.html │ │ ├── user.html │ │ └── voicestate.html │ │ ├── message │ │ ├── addreaction(_:then:).html │ │ ├── attachments.html │ │ ├── author.html │ │ ├── channel.html │ │ ├── content.html │ │ ├── delete(then:).html │ │ ├── deletereaction(_:from:then:).html │ │ ├── deletereactions(then:).html │ │ ├── edit(with:then:).html │ │ ├── editedtimestamp.html │ │ ├── embeds.html │ │ ├── flags.html │ │ ├── getreaction(_:then:).html │ │ ├── guild.html │ │ ├── id.html │ │ ├── index.html │ │ ├── iseveryonementioned.html │ │ ├── ispinned.html │ │ ├── istts.html │ │ ├── member.html │ │ ├── mentionedroles.html │ │ ├── mentions.html │ │ ├── nonce.html │ │ ├── pin(then:).html │ │ ├── reactions.html │ │ ├── refrencedmessage.html │ │ ├── reply(with:then:)-42ei2.html │ │ ├── reply(with:then:)-52j8c.html │ │ ├── reply(with:then:)-7cwsq.html │ │ ├── reply(with:then:)-8hgfd.html │ │ ├── reply(with:then:)-9k1ua.html │ │ ├── swiftcord.html │ │ ├── timestamp.html │ │ ├── type-swift.enum │ │ │ ├── !=(_:_:).html │ │ │ ├── call.html │ │ │ ├── channelfollowadd.html │ │ │ ├── channeliconchange.html │ │ │ ├── channelnamechange.html │ │ │ ├── channelpinnedmessage.html │ │ │ ├── chatinputcommand.html │ │ │ ├── contextmenucommand.html │ │ │ ├── default.html │ │ │ ├── equatable-implementations.html │ │ │ ├── guilddiscoverydisqualified.html │ │ │ ├── guilddiscoverygraceperiodfinalwarning.html │ │ │ ├── guilddiscoverygraceperiodinitialwarning.html │ │ │ ├── guilddiscoveryrequalified.html │ │ │ ├── guildinvitereminder.html │ │ │ ├── guildmemberjoin.html │ │ │ ├── hash(into:).html │ │ │ ├── hashvalue.html │ │ │ ├── index.html │ │ │ ├── memberboost.html │ │ │ ├── memberboostlvl1.html │ │ │ ├── memberboostlvl2.html │ │ │ ├── memberboostlvl3.html │ │ │ ├── rawrepresentable-implementations.html │ │ │ ├── recipientadd.html │ │ │ ├── recipientremove.html │ │ │ ├── reply.html │ │ │ ├── threadcreated.html │ │ │ └── threadstartermessage.html │ │ ├── type-swift.property.html │ │ └── webhookid.html │ │ ├── messagecommandbuilder │ │ ├── defaultpermission.html │ │ ├── index.html │ │ ├── init(name:).html │ │ ├── name.html │ │ └── type.html │ │ ├── messagecommandevent │ │ ├── channelid.html │ │ ├── deferreply(_:).html │ │ ├── delete(_:).html │ │ ├── edit(message:then:).html │ │ ├── editwithbuttons(buttons:then:).html │ │ ├── editwithembeds(embeds:then:).html │ │ ├── editwithselectmenu(menu:then:).html │ │ ├── ephemeral.html │ │ ├── guild.html │ │ ├── guildid.html │ │ ├── index.html │ │ ├── interactionevent-implementations.html │ │ ├── interactionid.html │ │ ├── isdefered.html │ │ ├── member.html │ │ ├── message.html │ │ ├── name.html │ │ ├── reply(message:then:).html │ │ ├── replybuttons(buttons:then:).html │ │ ├── replyembeds(embeds:then:).html │ │ ├── replyselectmenu(menu:then:).html │ │ ├── setephemeral(isephermeral:).html │ │ ├── swiftcord.html │ │ ├── token.html │ │ └── user.html │ │ ├── overwrite │ │ ├── allow.html │ │ ├── deny.html │ │ ├── id.html │ │ ├── index.html │ │ ├── overwritetype │ │ │ ├── !=(_:_:).html │ │ │ ├── equatable-implementations.html │ │ │ ├── hash(into:).html │ │ │ ├── hashvalue.html │ │ │ ├── index.html │ │ │ ├── member.html │ │ │ ├── rawrepresentable-implementations.html │ │ │ └── role.html │ │ └── type.html │ │ ├── permission │ │ ├── !=(_:_:).html │ │ ├── addreactions.html │ │ ├── administrator.html │ │ ├── attachfiles.html │ │ ├── banmembers.html │ │ ├── changenickname.html │ │ ├── connect.html │ │ ├── createinstantinvite.html │ │ ├── deafenmembers.html │ │ ├── embedlinks.html │ │ ├── equatable-implementations.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── kickmembers.html │ │ ├── managechannels.html │ │ ├── manageemojis.html │ │ ├── manageguild.html │ │ ├── managemessages.html │ │ ├── managenicknames.html │ │ ├── manageroles.html │ │ ├── managewebhooks.html │ │ ├── mentioneveryone.html │ │ ├── movemembers.html │ │ ├── mutemembers.html │ │ ├── rawrepresentable-implementations.html │ │ ├── readmessagehistory.html │ │ ├── sendmessages.html │ │ ├── sendttsmessages.html │ │ ├── speak.html │ │ ├── useexternalemojis.html │ │ ├── usevad.html │ │ ├── viewauditlog.html │ │ └── viewchannel.html │ │ ├── presence │ │ ├── game.html │ │ ├── index.html │ │ ├── init(status:playing:).html │ │ └── status.html │ │ ├── requesterror │ │ ├── code.html │ │ ├── customstringconvertible-implementations.html │ │ ├── description.html │ │ ├── error-implementations.html │ │ ├── error.html │ │ ├── index.html │ │ ├── localizeddescription.html │ │ ├── message.html │ │ └── statuscode.html │ │ ├── role │ │ ├── color.html │ │ ├── id.html │ │ ├── index.html │ │ ├── ishoisted.html │ │ ├── ismanaged.html │ │ ├── ismentionable.html │ │ ├── name.html │ │ ├── permissions.html │ │ └── position.html │ │ ├── scheduledevent │ │ ├── channelid.html │ │ ├── creator.html │ │ ├── description.html │ │ ├── entityid.html │ │ ├── entitytype │ │ │ ├── !=(_:_:).html │ │ │ ├── equatable-implementations.html │ │ │ ├── external.html │ │ │ ├── hash(into:).html │ │ │ ├── hashvalue.html │ │ │ ├── index.html │ │ │ ├── rawrepresentable-implementations.html │ │ │ ├── stage.html │ │ │ └── voice.html │ │ ├── eventtype.html │ │ ├── id.html │ │ ├── index.html │ │ ├── init(channelid:name:description:location:type:starttime:endtime:).html │ │ ├── location.html │ │ ├── membercount.html │ │ ├── name.html │ │ ├── scheduledendtime.html │ │ ├── scheduledstarttime.html │ │ ├── status-swift.enum │ │ │ ├── !=(_:_:).html │ │ │ ├── active.html │ │ │ ├── canceled.html │ │ │ ├── completed.html │ │ │ ├── equatable-implementations.html │ │ │ ├── hash(into:).html │ │ │ ├── hashvalue.html │ │ │ ├── index.html │ │ │ ├── rawrepresentable-implementations.html │ │ │ └── scheduled.html │ │ └── status-swift.property.html │ │ ├── selectmenu │ │ ├── customid.html │ │ ├── index.html │ │ ├── init(customid:placeholder:options:).html │ │ ├── options.html │ │ ├── placeholder.html │ │ └── type.html │ │ ├── selectmenubuilder │ │ ├── addcomponent(component:).html │ │ ├── components.html │ │ ├── content.html │ │ ├── index.html │ │ └── init(message:).html │ │ ├── selectmenucomponentdata │ │ ├── componenttype.html │ │ ├── customid.html │ │ ├── index.html │ │ └── value.html │ │ ├── selectmenuevent │ │ ├── channelid.html │ │ ├── deferreply(_:).html │ │ ├── delete(_:).html │ │ ├── edit(message:then:).html │ │ ├── editwithbuttons(buttons:then:).html │ │ ├── editwithembeds(embeds:then:).html │ │ ├── editwithselectmenu(menu:then:).html │ │ ├── ephemeral.html │ │ ├── guild.html │ │ ├── index.html │ │ ├── interactionevent-implementations.html │ │ ├── interactionid.html │ │ ├── isdefered.html │ │ ├── member.html │ │ ├── reply(message:then:).html │ │ ├── replybuttons(buttons:then:).html │ │ ├── replyembeds(embeds:then:).html │ │ ├── replyselectmenu(menu:then:).html │ │ ├── selectedvalue.html │ │ ├── setephemeral(isephermeral:).html │ │ ├── swiftcord.html │ │ ├── token.html │ │ └── user.html │ │ ├── selectmenuoptions │ │ ├── description.html │ │ ├── emoji.html │ │ ├── index.html │ │ ├── init(label:value:description:emoji:).html │ │ ├── label.html │ │ └── value.html │ │ ├── shield │ │ ├── commandaliases.html │ │ ├── commands.html │ │ ├── index.html │ │ ├── init(token:swiftcordoptions:shieldoptions:).html │ │ ├── register(_:).html │ │ ├── register(_:with:_:).html │ │ ├── register(_:with:message:).html │ │ ├── shieldoptions.html │ │ └── unregister(_:).html │ │ ├── shieldoptions │ │ ├── index.html │ │ ├── init(prefixes:requirements:willbecasesensitive:willdefaulthelp:willignorebots:).html │ │ ├── prefixes.html │ │ ├── requirements.html │ │ ├── willbecasesensitive.html │ │ ├── willdefaulthelp.html │ │ └── willignorebots.html │ │ ├── slashcommandbuilder │ │ ├── addoption(option:).html │ │ ├── defaultpermission.html │ │ ├── description.html │ │ ├── index.html │ │ ├── init(name:description:).html │ │ ├── name.html │ │ ├── options.html │ │ └── type.html │ │ ├── slashcommandevent │ │ ├── channelid.html │ │ ├── deferreply(_:).html │ │ ├── delete(_:).html │ │ ├── edit(message:then:).html │ │ ├── editwithbuttons(buttons:then:).html │ │ ├── editwithembeds(embeds:then:).html │ │ ├── editwithselectmenu(menu:then:).html │ │ ├── ephemeral.html │ │ ├── getoptionasbool(optionname:).html │ │ ├── getoptionaschannel(optionname:).html │ │ ├── getoptionasdouble(optionname:).html │ │ ├── getoptionasint(optionname:).html │ │ ├── getoptionasmember(optionname:).html │ │ ├── getoptionasrole(optionname:).html │ │ ├── getoptionasstring(optionname:).html │ │ ├── getoptionasuser(optionname:).html │ │ ├── guild.html │ │ ├── index.html │ │ ├── interactionevent-implementations.html │ │ ├── interactionid.html │ │ ├── isdefered.html │ │ ├── member.html │ │ ├── name.html │ │ ├── options.html │ │ ├── reply(message:then:).html │ │ ├── replybuttons(buttons:then:).html │ │ ├── replyembeds(embeds:then:).html │ │ ├── replyselectmenu(menu:then:).html │ │ ├── setephemeral(isephermeral:).html │ │ ├── swiftcord.html │ │ ├── token.html │ │ └── user.html │ │ ├── slashcommandeventoptions │ │ ├── index.html │ │ ├── name.html │ │ ├── type.html │ │ └── value.html │ │ ├── snowflake │ │ ├── !=(_:_:).html │ │ ├── '...(_:)-3w0ig.html │ │ ├── '...(_:)-5zctv.html │ │ ├── '...(_:_:).html │ │ ├── '.._(_:).html │ │ ├── '.._(_:_:).html │ │ ├── -implementations.html │ │ ├── _(_:_:)-1ndpt.html │ │ ├── _(_:_:)-1plx4.html │ │ ├── _=(_:_:)-2wbgt.html │ │ ├── _=(_:_:)-5igch.html │ │ ├── comparable-implementations.html │ │ ├── customstringconvertible-implementations.html │ │ ├── description.html │ │ ├── encode(to:).html │ │ ├── epoch.html │ │ ├── equatable-implementations.html │ │ ├── expressiblebyintegerliteral-implementations.html │ │ ├── fakesnowflake(date:).html │ │ ├── hash(into:).html │ │ ├── hashable-implementations.html │ │ ├── hashvalue-8gymi.html │ │ ├── hashvalue-90cu.html │ │ ├── index.html │ │ ├── init(_:)-3hv6d.html │ │ ├── init(_:)-68ghg.html │ │ ├── init(from:).html │ │ ├── init(integerliteral:).html │ │ ├── init(rawvalue:).html │ │ ├── integerliteraltype.html │ │ ├── numberinprocess.html │ │ ├── processid.html │ │ ├── rawrepresentable-implementations.html │ │ ├── rawvalue-swift.property.html │ │ ├── rawvalue-swift.typealias.html │ │ ├── timestamp.html │ │ └── workerid.html │ │ ├── stage │ │ ├── channelid.html │ │ ├── guild.html │ │ ├── id.html │ │ ├── index.html │ │ ├── isdiscoverydisabled.html │ │ ├── privacylevel.html │ │ ├── swiftcord.html │ │ └── topic.html │ │ ├── stageprivacylevel │ │ ├── !=(_:_:).html │ │ ├── equatable-implementations.html │ │ ├── guildonly.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── public.html │ │ └── rawrepresentable-implementations.html │ │ ├── status │ │ ├── !=(_:_:).html │ │ ├── dnd.html │ │ ├── equatable-implementations.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── idle.html │ │ ├── index.html │ │ ├── invisible.html │ │ ├── offline.html │ │ ├── online.html │ │ └── rawrepresentable-implementations.html │ │ ├── sticker │ │ ├── description.html │ │ ├── format.html │ │ ├── id.html │ │ ├── index.html │ │ ├── isavailable.html │ │ ├── name.html │ │ ├── packid.html │ │ └── type.html │ │ ├── stickerformat │ │ ├── !=(_:_:).html │ │ ├── apng.html │ │ ├── encode(to:).html │ │ ├── equatable-implementations.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── init(from:).html │ │ ├── lottie.html │ │ ├── png.html │ │ └── rawrepresentable-implementations.html │ │ ├── stickertypes │ │ ├── !=(_:_:).html │ │ ├── encode(to:).html │ │ ├── equatable-implementations.html │ │ ├── guild.html │ │ ├── hash(into:).html │ │ ├── hashvalue.html │ │ ├── index.html │ │ ├── init(from:).html │ │ ├── rawrepresentable-implementations.html │ │ └── standard.html │ │ ├── swiftcord │ │ ├── addreaction(_:to:in:then:).html │ │ ├── ban(_:from:for:with:then:).html │ │ ├── connect().html │ │ ├── createchannel(for:with:then:).html │ │ ├── createguild(with:then:).html │ │ ├── createintegration(for:with:then:).html │ │ ├── createinvite(for:with:then:).html │ │ ├── createrole(for:with:then:).html │ │ ├── createwebhook(for:with:then:).html │ │ ├── deleteapplicationcommand(commandid:then:).html │ │ ├── deletechannel(_:then:).html │ │ ├── deleteguild(_:then:).html │ │ ├── deleteguildemoji(_:emojiid:reason:then:).html │ │ ├── deleteintegration(_:from:then:).html │ │ ├── deleteinvite(_:then:).html │ │ ├── deletemessage(_:from:then:).html │ │ ├── deletemessages(_:from:then:).html │ │ ├── deletepermission(from:with:then:).html │ │ ├── deletereaction(_:from:by:in:then:).html │ │ ├── deletereactions(from:in:then:).html │ │ ├── deleterole(_:from:then:).html │ │ ├── deletewebhook(_:token:then:).html │ │ ├── disconnect().html │ │ ├── dms.html │ │ ├── editmessage(_:with:in:then:).html │ │ ├── editpermissions(_:for:with:then:).html │ │ ├── editstatus(status:activity:).html │ │ ├── emit(_:with:).html │ │ ├── eventable-implementations.html │ │ ├── executeslackwebhook(_:token:with:then:).html │ │ ├── executewebhook(_:token:with:then:).html │ │ ├── getauditlog(from:with:then:).html │ │ ├── getbans(from:then:).html │ │ ├── getchannel(_:rest:then:).html │ │ ├── getchannel(for:).html │ │ ├── getchannelinvites(from:then:).html │ │ ├── getchannels(from:rest:then:).html │ │ ├── getconnections(then:).html │ │ ├── getdm(for:).html │ │ ├── getdm(for:then:).html │ │ ├── getgateway(then:).html │ │ ├── getguild(_:rest:then:).html │ │ ├── getguild(for:).html │ │ ├── getguildembed(from:then:).html │ │ ├── getguildemoji(from:with:then:).html │ │ ├── getguildemojis(from:then:).html │ │ ├── getguildinvites(from:then:).html │ │ ├── getguildsticker(from:stickerid:then:).html │ │ ├── getguildstickers(from:then:).html │ │ ├── getguildwebhooks(from:then:).html │ │ ├── getintegrations(from:then:).html │ │ ├── getinvite(_:then:).html │ │ ├── getmember(_:from:then:).html │ │ ├── getmembers(from:with:then:).html │ │ ├── getmessage(_:from:then:).html │ │ ├── getmessages(from:with:then:).html │ │ ├── getpinnedmessages(from:then:).html │ │ ├── getprunecount(from:for:then:).html │ │ ├── getreaction(_:from:in:then:).html │ │ ├── getroles(from:then:).html │ │ ├── getshard(for:).html │ │ ├── getsticker(stickerid:then:).html │ │ ├── getthreads(for:).html │ │ ├── getuser(_:then:).html │ │ ├── getuserguilds(with:then:).html │ │ ├── getvoiceregions(from:then:).html │ │ ├── getwebhook(_:token:then:).html │ │ ├── getwebhooks(from:then:).html │ │ ├── groups.html │ │ ├── guilds.html │ │ ├── index.html │ │ ├── init(token:options:).html │ │ ├── kick(_:from:for:then:).html │ │ ├── kill(_:).html │ │ ├── leaveguild(_:then:).html │ │ ├── listeners.html │ │ ├── modifychannel(_:with:then:).html │ │ ├── modifychannelpositions(for:with:then:).html │ │ ├── modifyembed(for:with:then:).html │ │ ├── modifyemoji(for:emojiid:with:reason:then:).html │ │ ├── modifyguild(_:with:then:).html │ │ ├── modifyintegration(_:for:with:then:).html │ │ ├── modifymember(_:in:with:for:then:).html │ │ ├── modifyrole(_:for:with:then:).html │ │ ├── modifyrolepositions(for:with:then:).html │ │ ├── modifywebhook(_:token:with:then:).html │ │ ├── movemember(_:in:to:then:).html │ │ ├── on(_:do:).html │ │ ├── pin(_:in:then:).html │ │ ├── prunemembers(in:for:then:).html │ │ ├── readytimestamp.html │ │ ├── removeuser(_:fromgroupdm:then:).html │ │ ├── send(_:to:then:)-1tsgd.html │ │ ├── send(_:to:then:)-2f6es.html │ │ ├── send(_:to:then:)-3g5yf.html │ │ ├── send(_:to:then:)-69802.html │ │ ├── send(_:to:then:)-9cixw.html │ │ ├── setintents(intents:).html │ │ ├── settyping(for:then:).html │ │ ├── setusername(to:then:).html │ │ ├── shardcount.html │ │ ├── spawn(_:).html │ │ ├── syncintegration(_:for:then:).html │ │ ├── unavailableguilds.html │ │ ├── unbanmember(_:from:then:).html │ │ ├── unpin(_:from:then:).html │ │ ├── uploademoji(name:emoji:roles:guildid:then:).html │ │ ├── uploadmessagecommand(commanddata:then:).html │ │ ├── uploadslashcommand(commanddata:then:).html │ │ ├── uploadusercommand(commanddata:then:).html │ │ ├── uptime.html │ │ └── user.html │ │ ├── swiftcordoptions │ │ ├── index.html │ │ ├── init(isbot:isdistributed:willcacheallmembers:willlog:willshard:).html │ │ ├── isbot.html │ │ ├── isdistributed.html │ │ ├── willcacheallmembers.html │ │ ├── willlog.html │ │ └── willshard.html │ │ ├── textchannel │ │ ├── addreaction(_:to:then:).html │ │ ├── deletemessage(_:then:).html │ │ ├── deletemessages(_:then:).html │ │ ├── deletereaction(_:from:by:then:).html │ │ ├── editmessage(_:with:then:).html │ │ ├── getmessage(_:then:).html │ │ ├── getmessages(with:then:).html │ │ ├── getpinnedmessages(then:).html │ │ ├── getreaction(_:from:then:).html │ │ ├── index.html │ │ ├── lastmessageid.html │ │ ├── pin(_:then:).html │ │ ├── send(_:then:)-1urc0.html │ │ ├── send(_:then:)-48nc0.html │ │ ├── send(_:then:)-65yws.html │ │ ├── send(_:then:)-6tls6.html │ │ ├── send(_:then:)-7f0x2.html │ │ └── unpin(_:then:).html │ │ ├── thread │ │ ├── addreaction(_:to:then:).html │ │ ├── archivetimestamp.html │ │ ├── autoarchiveduration.html │ │ ├── category.html │ │ ├── channel-implementations.html │ │ ├── delete(then:).html │ │ ├── deletemessage(_:then:).html │ │ ├── deletemessages(_:then:).html │ │ ├── deletereaction(_:from:by:then:).html │ │ ├── editmessage(_:with:then:).html │ │ ├── getmessage(_:then:).html │ │ ├── getmessages(with:then:).html │ │ ├── getpinnedmessages(then:).html │ │ ├── getreaction(_:from:then:).html │ │ ├── guild.html │ │ ├── id.html │ │ ├── index.html │ │ ├── isarchived.html │ │ ├── islocked.html │ │ ├── lastmessageid.html │ │ ├── membercount.html │ │ ├── messagecount.html │ │ ├── name.html │ │ ├── ownerid.html │ │ ├── parentid.html │ │ ├── permissionoverwrites.html │ │ ├── pin(_:then:).html │ │ ├── position.html │ │ ├── ratelimitperuser.html │ │ ├── send(_:then:)-303xq.html │ │ ├── send(_:then:)-4opmv.html │ │ ├── send(_:then:)-4yyzv.html │ │ ├── send(_:then:)-8bil4.html │ │ ├── send(_:then:)-cycs.html │ │ ├── swiftcord.html │ │ ├── textchannel-implementations.html │ │ ├── type.html │ │ └── unpin(_:then:).html │ │ ├── unavailableguild │ │ ├── id.html │ │ ├── index.html │ │ └── shard.html │ │ ├── user │ │ ├── avatar.html │ │ ├── discriminator.html │ │ ├── email.html │ │ ├── getdm(then:).html │ │ ├── id.html │ │ ├── imageurl(format:).html │ │ ├── index.html │ │ ├── isbot.html │ │ ├── ismfaenabled.html │ │ ├── isverified.html │ │ ├── swiftcord.html │ │ └── username.html │ │ ├── usercommandbuilder │ │ ├── defaultpermission.html │ │ ├── index.html │ │ ├── init(name:).html │ │ ├── name.html │ │ └── type.html │ │ ├── usercommandevent │ │ ├── channelid.html │ │ ├── deferreply(_:).html │ │ ├── delete(_:).html │ │ ├── edit(message:then:).html │ │ ├── editwithbuttons(buttons:then:).html │ │ ├── editwithembeds(embeds:then:).html │ │ ├── editwithselectmenu(menu:then:).html │ │ ├── ephemeral.html │ │ ├── guild.html │ │ ├── guildid.html │ │ ├── index.html │ │ ├── interactionevent-implementations.html │ │ ├── interactionid.html │ │ ├── isdefered.html │ │ ├── member.html │ │ ├── name.html │ │ ├── reply(message:then:).html │ │ ├── replybuttons(buttons:then:).html │ │ ├── replyembeds(embeds:then:).html │ │ ├── replyselectmenu(menu:then:).html │ │ ├── setephemeral(isephermeral:).html │ │ ├── swiftcord.html │ │ ├── targetmember.html │ │ ├── targetuser.html │ │ ├── token.html │ │ └── user.html │ │ ├── userguild │ │ ├── icon.html │ │ ├── id.html │ │ ├── index.html │ │ ├── isowner.html │ │ ├── name.html │ │ └── permissions.html │ │ ├── voicestate │ │ ├── channelid.html │ │ ├── index.html │ │ ├── isdeafend.html │ │ ├── ismuted.html │ │ ├── isselfdeafend.html │ │ ├── isselfmuted.html │ │ ├── issuppressed.html │ │ └── sessionid.html │ │ └── webhook │ │ ├── avatar.html │ │ ├── channel.html │ │ ├── delete(then:).html │ │ ├── execute(with:then:).html │ │ ├── executeslack(with:then:).html │ │ ├── guild.html │ │ ├── id.html │ │ ├── index.html │ │ ├── modify(with:then:).html │ │ ├── name.html │ │ ├── swiftcord.html │ │ ├── token.html │ │ └── user.html ├── favicon.ico ├── favicon.svg ├── img │ ├── added-icon.svg │ ├── deprecated-icon.svg │ └── modified-icon.svg └── index.html └── images ├── step1.png ├── step2.png └── step3.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /*.xcodeproj 4 | /build 5 | /docs/undocumented.json 6 | /docs/docsets 7 | /Tests 8 | .swift-version 9 | .swiftpm -------------------------------------------------------------------------------- /Examples/Basic Commands.swift: -------------------------------------------------------------------------------- 1 | import Swiftcord 2 | 3 | class MessageListener: ListenerAdapter { 4 | override func onMessageCreate(event: Message) async { 5 | if event.content == "+test" { 6 | _ = try! await event.reply(with: "Testing Swiftcord!") 7 | } 8 | } 9 | } 10 | 11 | let bot = Swiftcord(token: "token") 12 | bot.setIntents(intents: .guildMessages) 13 | 14 | let activity = Activities(name: "with Swiftcord", type: .playing) 15 | bot.editStatus(status: .online, activity: activity) 16 | 17 | bot.addListeners(MessageListener()) 18 | 19 | bot.connect() 20 | -------------------------------------------------------------------------------- /Examples/Buttons.swift: -------------------------------------------------------------------------------- 1 | import Swiftcord 2 | 3 | class Buttons: ListenerAdapter { 4 | override func onMessageCreate(event: Message) async { 5 | if event.content == "+button" { 6 | let buttonBuilder = ButtonBuilder(message: "testing buttons with Swiftcord!") 7 | // Emojis are optional. 8 | .addComponent(component: ActionRow(components: Button(customId: "test", style: .green, label: "Button with Emoji", emoji: Emoji("🎟️")))) 9 | // To add another action row, call add component. Remember that Discord allows up to 5 action rows in 1 message 10 | // You can have multiple buttons in 1 action row by making a `Button` struct as many times as needed. 11 | .addComponent(component: ActionRow(components: Button( 12 | customId: "test_2", 13 | style: .blurple, 14 | label: "Blurple Button" 15 | ), 16 | Button( 17 | customId: "test_3", 18 | style: .red, 19 | lable: "Danger!" 20 | ))) 21 | 22 | event.reply(with: buttonBuilder) 23 | } 24 | } 25 | 26 | override func onButtonClickEvent(event: ButtonEvent) async { 27 | // If the command may take a while to complete, we can make the bot display the `is thinking...` text 28 | event.deferReply() 29 | 30 | // If we would like to only allow the user who invoked the command to see the output, we can set ephemeral 31 | event.setEphemeral(true) 32 | 33 | if button.selectedButton.customId == "test" { 34 | try! await event.reply(message: "Test button was clicked!") 35 | } else if button.selectedButton.customId == "test_2" { 36 | try! await event.reply(message: "Blurple button was clicked!") 37 | } else if button.selectedButton.customId == "test_3" { 38 | try! await event.reply(message: "Stranger Danger!") 39 | } 40 | } 41 | } 42 | 43 | let bot = Swiftcord(token: "token") 44 | 45 | bot.setIntents(intents: .guildMessages) 46 | bot.addListeners(Buttons()) 47 | 48 | bot.connect() 49 | -------------------------------------------------------------------------------- /Examples/Select Menu.swift: -------------------------------------------------------------------------------- 1 | import Swiftcord 2 | 3 | class SelectMenuListener: ListenerAdapter { 4 | override func onMessageCreate(event: Message) async { 5 | if event.content == "+menu" { 6 | // You can send multiple Select Menu's by calling .addComponent again. 7 | // customID is the ID for the entire SelectMenu object, while value is the customID for that selection. 8 | let menu = SelectMenuBuilder(message: "Testing Select Menu's in Sword!") 9 | .addComponent(component: ActionRow(components: 10 | SelectMenu( 11 | customId: "test", 12 | placeholder: "Test 1", 13 | options: SelectMenuOptions( 14 | label: "Testing with Sword", 15 | value: "test_section", 16 | description: "We are testing a select menu with Sword")) 17 | ) 18 | ) 19 | 20 | try! await event.reply(with: menu) 21 | } 22 | } 23 | 24 | override func onSelectMenuEvent(event: SelectMenuEvent) { 25 | // If the command may take a while to complete, we can make the bot display the `is thinking...` text 26 | // It returns a ResponseError in the closure which should never really be needed 27 | event.deferReply() 28 | 29 | // If we would like to only allow the user who invoked the command to see the output, we can set ephemeral 30 | event.setEphemeral(true) 31 | 32 | // First we check if the selected option is in the SelectMenu we would like to respond to 33 | if event.selectedValue.customId == "test" { 34 | // Now we check for the actual selected option 35 | if event.selectedValue.value == "test_section" { 36 | try! await event.reply(message: "We interacted with a select menu!") 37 | } 38 | } 39 | } 40 | } 41 | 42 | let bot = Swiftcord(token: "token") 43 | 44 | bot.setIntents(intents: .guildMessages) 45 | bot.addListeners(SelectMenuListener()) 46 | 47 | bot.connect() 48 | -------------------------------------------------------------------------------- /Examples/Slash Commands.swift: -------------------------------------------------------------------------------- 1 | import Swiftcord 2 | 3 | class SlashCommandListener: ListenerAdapter { 4 | override func onSlashCommandEvent(event: SlashCommandEvent) { 5 | if event.name == "test" { 6 | // If the command may take a while to complete, we can make the bot display the `is thinking...` text 7 | // It returns a ResponseError in the closure which should never really be needed 8 | event.deferReply() 9 | 10 | // If we would like to only allow the user who invoked the command to see the output, we can set ephemeral 11 | event.setEphemeral(true) 12 | 13 | // Retriving options is extremely simple! 14 | // For Boolean: event.getOptionAsBool(optionName: "optionName") NOTE: You will need to cast the `Channel` type to the channel type you want. 15 | // For Channels: event.getOptionAsChannel(optionName: "optionName") 16 | // For Double: event.getOptionAsDouble(optionName: "optionName") 17 | // For Integer: event.getOptionAsInt(optionName: "optionName") 18 | // For Member: event.getOptionAsMember(optionName: "optionName") 19 | // For String: event.getOptionAsString(optionName: "optionName") 20 | // For Role: event.getOptionAsRole(optionName: "optionName") 21 | // For User: event.getOptionAsUser(optionName: "optionName") 22 | let selectedUser = event.getOptionAsUser(optionName: "user")!.username! 23 | 24 | // Sending messages are simple as well! 25 | // All InteractionEvent types have the same functions for sending, editing and deleting messages. 26 | // For sending a normal message: event.reply(message: "message") 27 | // For sending an embed(s): event.replyEmbeds(embeds: embedBuilderVar, embedBuilderVar2). You can also pass an `[EmbedBuilder]` type 28 | // For sending a message with buttons: event.replyButtons(buttons: buttonBuilderVar) 29 | // For sending a message with a select menu: event.replySelectMenu(menu: selectMenuBuilderVar) 30 | try! await event.reply(message: "User \(invokedUser) was selected!") 31 | } 32 | } 33 | } 34 | 35 | let bot = Swiftcord(token: "Super secret token here") 36 | 37 | // Create slash command 38 | // NOTE: This creates a guild only slash command. To create a global slash command, call `bot.uploadSlashCommand(commandData: command) 39 | let command = SlashCommandBuilder(name: "test", description: "Testing slash commands in Sword") 40 | .addOption(option: ApplicationCommandOptions(name: "user", description: "User to select", type: .user)) 41 | 42 | bot.guilds[guildId].uploadSlashCommand(commandData: command) 43 | 44 | bot.addListeners(SlashCommandListener()) 45 | 46 | bot.connect() 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alejandro Alonso 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "MimeType", 6 | "repositoryURL": "https://github.com/SketchMaster2001/MimeType.git", 7 | "state": { 8 | "branch": "master", 9 | "revision": "102b3769ea8dc7ed0bd50b932bdd21cd4e89215c", 10 | "version": null 11 | } 12 | }, 13 | { 14 | "package": "swift-log", 15 | "repositoryURL": "https://github.com/apple/swift-log.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "5d66f7ba25daf4f94100e7022febf3c75e37a6c7", 19 | "version": "1.4.2" 20 | } 21 | }, 22 | { 23 | "package": "swift-nio", 24 | "repositoryURL": "https://github.com/apple/swift-nio.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "51c3fc2e4a0fcdf4a25089b288dd65b73df1b0ef", 28 | "version": "2.37.0" 29 | } 30 | }, 31 | { 32 | "package": "swift-nio-ssl", 33 | "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "52a486ff6de9bc3e26bf634c5413c41c5fa89ca5", 37 | "version": "2.17.2" 38 | } 39 | }, 40 | { 41 | "package": "websocket-kit", 42 | "repositoryURL": "https://github.com/vapor/websocket-kit", 43 | "state": { 44 | "branch": "main", 45 | "revision": "ff8fbce837ef01a93d49c6fb49a72be0f150dac7", 46 | "version": null 47 | } 48 | } 49 | ] 50 | }, 51 | "version": 1 52 | } 53 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Swiftcord", 6 | platforms: [.macOS("12") ], 7 | products: [ 8 | .library(name: "Swiftcord", targets: ["Swiftcord"]) 9 | ], 10 | dependencies: [ 11 | // WebSockets for Linux and macOS 12 | .package(url: "https://github.com/vapor/websocket-kit", .branch("main")), 13 | // Logging for Swift 14 | .package(url: "https://github.com/apple/swift-log.git", from: "1.4.2"), 15 | // Library that contains common mimetypes 16 | .package(url: "https://github.com/SketchMaster2001/MimeType.git", .branch("master")) 17 | ], 18 | targets: [ 19 | .target( 20 | name: "Swiftcord", 21 | dependencies: [.product(name: "WebSocketKit", package: "websocket-kit"), .product(name: "Logging", package: "swift-log"), "MimeType"] 22 | ), 23 | .testTarget( 24 | name: "SwiftcordTests", 25 | dependencies: [.target(name: "Swiftcord")] 26 | ) 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Swift Version](https://img.shields.io/badge/Swift-5.3-orange.svg?style=flat-square)](https://swift.org) 2 | [![Tag](https://img.shields.io/github/tag/Azoy/Sword.svg?style=flat-square&label=release&colorB=)](https://github.com/Azoy/Sword/releases) 3 | 4 | 5 | 6 | # Swiftcord - A Discord Library for Swift 7 | 8 | ## Requirements 9 | 1. macOS, Linux, iOS, watchOS, tvOS (no voice for iOS, watchOS, or tvOS) 10 | 2. At least Swift 5.3 11 | 12 | ## Adding Sword 13 | ### Swift Package Manager 14 | In order to add Sword as a dependency, you must first create a Swift executable in a designated folder, like so `swift package init --type executable`. Then in the newly created Package.swift, open it and add Sword as a dependency 15 | 16 | ```swift 17 | // swift-tools-version: 5.3 18 | 19 | import PackageDescription 20 | 21 | let package = Package( 22 | name: "yourswiftexecutablehere", 23 | dependencies: [ 24 | .package(url: "https://github.com/SketchMaster2001/Swiftcord", .branch("master")) 25 | ], 26 | targets: [ 27 | .target( 28 | name: "yourswiftexecutablehere", 29 | dependencies: ["Swiftcord"] 30 | ) 31 | ] 32 | ) 33 | ``` 34 | 35 | After that, open Sources/main.swift and remove everything and replace it with the example below. 36 | 37 | ```swift 38 | import Swiftcord 39 | 40 | let bot = Swiftcord(token: "Your bot token here") 41 | 42 | // Set activity if wanted 43 | let activity = Activities(name: "with Swiftcord!", type: .playing) 44 | bot.editStatus(status: .online, activity: activity) 45 | 46 | // Set intents which are required 47 | bot.setIntents(intents: .guildMessages) 48 | 49 | class MyBot: ListenerAdapter { 50 | override func onMessageCreate(event: Message) async { 51 | if msg.content == "!ping" { 52 | try! await msg.reply(with: "Pong!") 53 | } 54 | } 55 | } 56 | 57 | bot.addListeners(MyBot()) 58 | bot.connect() 59 | ``` 60 | For more examples, look in the examples folder or in the Wiki. 61 | 62 | 63 | ## Running the bot (SPM) 64 | First make sure you are in the directory with the `Package.swift` file. To build the executable, run `swift build`. To build the executable and run it immediately, run `swift run` 65 | 66 | ## Running the bot in Xcode (SPM) 67 | To run the bot in Xcode, all you need to do is open the directory the `Package.swift` file is located in Xcode. Click the play button at the top left corner and it will run! 68 | 69 | Then click the play button! 70 | 71 | ## Links 72 | [Documentation](https://sketchmaster2001.github.io/Swiftcord) - Created using Apple [docc](https://github.com/apple/swift-docc) and converted to HTML with [docc2html](https://github.com/DoccZz/docc2html) 73 | 74 | [Swiftcord Discord server](https://discord.gg/cE2Cpn4r9X) 75 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Gateway/GatewayHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GatewayHandler.swift 3 | // Swiftcord 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2017 Alejandro Alonso. All rights reserved. 7 | // 8 | import Foundation 9 | 10 | /// Gateway Handler 11 | extension Shard { 12 | 13 | /** 14 | Handles all gateway events (except op: 0) 15 | - parameter payload: Payload sent with event 16 | */ 17 | func handleGateway(_ payload: Payload) async { 18 | 19 | guard let op = OP(rawValue: payload.op) else { 20 | self.swiftcord.log( 21 | "Received unknown gateway\nOP: \(payload.op)\nData: \(payload.d)" 22 | ) 23 | return 24 | } 25 | 26 | switch op { 27 | 28 | /// OP: 1 29 | case .heartbeat: 30 | self.send(self.heartbeatPayload.encode()) 31 | 32 | /// OP: 11 33 | case .heartbeatACK: 34 | self.heartbeatQueue.sync { self.acksMissed = 0 } 35 | 36 | /// OP: 10 37 | case .hello: 38 | self.heartbeat(at: (payload.d as! [String: Any])["heartbeat_interval"] as! Int) 39 | 40 | guard !self.isReconnecting else { 41 | self.isReconnecting = false 42 | let data: [String: Any] = [ 43 | "token": self.swiftcord.token, 44 | "session_id": self.sessionId!, 45 | "seq": self.lastSeq ?? NSNull() 46 | ] 47 | 48 | let payload = Payload(op: .resume, data: data) 49 | self.send(payload.encode()) 50 | return 51 | } 52 | 53 | self.identify() 54 | 55 | /// OP: 9 56 | case .invalidSession: 57 | self.isReconnecting = payload.d as! Bool 58 | await self.reconnect() 59 | 60 | /// OP: 7 61 | case .reconnect: 62 | self.isReconnecting = true 63 | await self.reconnect() 64 | 65 | /// Others 66 | default: 67 | break 68 | } 69 | 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Gateway/Heartbeat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Heartbeat.swift 3 | // Swiftcord 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2017 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Dispatch 11 | 12 | /// <3 13 | extension Gateway { 14 | func heartbeat(at interval: Int) { 15 | guard self.isConnected else { 16 | return 17 | } 18 | 19 | guard self.acksMissed < 3 else { 20 | Task { 21 | self.swiftcord.debug("[Swiftcord] Did not receive ACK from server, reconnecting...") 22 | await self.reconnect() 23 | } 24 | return 25 | } 26 | 27 | self.acksMissed += 1 28 | 29 | self.send(self.heartbeatPayload.encode(), presence: false) 30 | 31 | self.heartbeatQueue.asyncAfter( 32 | deadline: .now() + .milliseconds(interval) 33 | ) { [unowned self] in 34 | self.heartbeat(at: interval) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Gateway/Payload.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Payload.swift 3 | // Swiftcord 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2017 Alejandro Alonso. All rights reserved. 7 | // 8 | /// Payload Type 9 | struct Payload { 10 | 11 | // MARK: Properties 12 | /// OP Code for payload 13 | var op: Int 14 | 15 | /// Data for payload 16 | var d: Any 17 | 18 | /// Sequence number from payload 19 | let s: Int? 20 | 21 | /// Event name from payload 22 | let t: String? 23 | 24 | // MARK: Initializers 25 | /** 26 | Creates a payload from JSON String 27 | - parameter text: JSON String 28 | */ 29 | init(with text: String) { 30 | let data = text.decode() as! [String: Any] 31 | self.op = data["op"] as! Int 32 | self.d = data["d"]! 33 | self.s = data["s"] as? Int 34 | self.t = data["t"] as? String 35 | } 36 | 37 | /** 38 | Creates a payload from either an Array | Dictionary 39 | - parameter op: OP code to dispatch 40 | - parameter data: Either an Array | Dictionary to dispatch under the payload.d 41 | */ 42 | init(op: OP, data: Any) { 43 | self.op = op.rawValue 44 | self.d = data 45 | self.s = nil 46 | self.t = nil 47 | } 48 | 49 | // MARK: Functions 50 | /// Returns self as a String 51 | func encode() -> String { 52 | var payload = ["op": self.op, "d": self.d] 53 | 54 | if self.t != nil { 55 | payload["s"] = self.s! 56 | payload["t"] = self.t! 57 | } 58 | 59 | return payload.encode() 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Gateway/ShardManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShardManager.swift 3 | // Swiftcord 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2017 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | import NIOCore 10 | class ShardManager { 11 | 12 | /// The gateway url to connect to 13 | var gatewayUrl: String? 14 | 15 | /// Array of Shard class 16 | var shards = [Shard]() 17 | 18 | var eventLoopGroup: EventLoopGroup? 19 | 20 | /// Parent Swiftcord class 21 | weak var swiftcord: Swiftcord? 22 | 23 | init(eventLoopGroup: EventLoopGroup?) { 24 | self.eventLoopGroup = eventLoopGroup 25 | } 26 | 27 | /** 28 | Used to create a set amount of shards 29 | - parameter amount: Number of shards to instantiate 30 | */ 31 | func create(_ amount: Int) { 32 | guard self.swiftcord != nil else { return } 33 | guard self.shards.isEmpty else { return } 34 | 35 | for id in 0 ..< amount { 36 | let shard = Shard(self.swiftcord!, id, amount, self.gatewayUrl!, eventLoopGroup: self.eventLoopGroup) 37 | self.shards.append(shard) 38 | Task { 39 | await shard.start() 40 | } 41 | } 42 | } 43 | 44 | /// Disconnects all shards from the gateway 45 | func disconnect() { 46 | guard self.shards.count > 0 else { return } 47 | 48 | for shard in self.shards { 49 | shard.stop() 50 | } 51 | 52 | self.shards.removeAll() 53 | } 54 | 55 | /** 56 | Kills a specific shard from the gateway 57 | - parameter id: Id of the shard to kill 58 | */ 59 | func kill(_ id: Int) { 60 | guard let index = self.shards.firstIndex(where: { $0.id == id }) else { return } 61 | 62 | let shard = self.shards.remove(at: index) 63 | 64 | shard.stop() 65 | } 66 | 67 | /** 68 | Spawns a shard based off id 69 | - parameter id: Id of shard to spawn 70 | */ 71 | func spawn(_ id: Int) { 72 | guard self.swiftcord != nil else { return } 73 | guard self.shards.first(where: { $0.id == id }) == nil else { return } 74 | guard self.gatewayUrl != nil else { return } 75 | 76 | let shard = Shard( 77 | self.swiftcord!, 78 | id, 79 | self.swiftcord!.shardCount, 80 | self.gatewayUrl!, 81 | eventLoopGroup: self.eventLoopGroup 82 | ) 83 | self.shards.append(shard) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Rest/RateLimit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RateLimit.swift 3 | // Swiftcord 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2017 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Dispatch 11 | 12 | /// Rate Limit 13 | extension Swiftcord { 14 | 15 | /** 16 | Gets the "route" for an HTTP request 17 | 18 | - parameter url: URL to get route from 19 | */ 20 | func getRoute(for url: String) -> String { 21 | 22 | let regex = try! NSRegularExpression( 23 | pattern: "/([a-z-]+)/(?:[0-9]{17,})+?", 24 | options: .caseInsensitive 25 | ) 26 | 27 | let string = NSString(string: url) 28 | let matches = regex.matches( 29 | in: url, 30 | options: [], 31 | range: NSRange(location: 0, length: string.length) 32 | ) 33 | 34 | guard matches.count > 0 else { 35 | return url 36 | } 37 | 38 | var route = "" 39 | 40 | for match in matches { 41 | let miniRoute = string.substring(with: match.range) 42 | var parameters = miniRoute.components(separatedBy: "/") 43 | 44 | parameters.remove(at: 0) 45 | 46 | let parameterId = parameters[1] 47 | parameters[1] = ":id" 48 | 49 | if (parameters[0] == "channels" || parameters[0] == "guilds") 50 | && route.isEmpty { 51 | parameters[1] = parameterId 52 | } 53 | 54 | route += "/" + parameters.joined(separator: "/") 55 | } 56 | 57 | return route 58 | } 59 | 60 | /// Used to un clog the global queue full of requests that woudld've resulted in 429 because of global rate limit 61 | func globalUnlock() { 62 | self.isGloballyLocked = false 63 | for request in self.globalRequestQueue { 64 | request() 65 | } 66 | } 67 | 68 | /** 69 | Handles creating buckets, and making sure the bucket is up to date with the headers 70 | 71 | - parameter headers: The received headers from the request 72 | */ 73 | func handleRateLimitHeaders( 74 | _ limitHeader: Any?, 75 | _ intervalHeader: Any?, 76 | _ date: Double, 77 | _ route: String 78 | ) { 79 | guard let limitHeader = limitHeader, 80 | let intervalHeader = intervalHeader else { 81 | return 82 | } 83 | 84 | let limit = Int(limitHeader as! String)! 85 | let interval = Int(round(Double(intervalHeader as! String)!)) - Int(date) 86 | if self.rateLimits[route] == nil { 87 | let bucket = Bucket( 88 | name: "me.azoy.swiftcord.rest.\(route)", 89 | limit: limit, 90 | interval: interval 91 | ) 92 | bucket.take(1) 93 | 94 | self.rateLimits[route] = bucket 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Types/Activities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Activities.swift 3 | // Swiftcord 4 | // 5 | // Created by Noah Pistilli on 2021-12-16. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct Activities: Encodable { 11 | public let name: String 12 | public let type: Int 13 | public var url: String? 14 | 15 | public init(name: String, type: ActivityType, url: String? = nil) { 16 | self.name = name 17 | self.type = type.rawValue 18 | let isValidURL = validateUrl(url: url) 19 | 20 | if isValidURL { 21 | self.url = url 22 | } else { 23 | self.url = "https://www.twitch.tv/" 24 | } 25 | } 26 | 27 | private func validateUrl(url: String?) -> Bool { 28 | // If URL is nil then we can safely ignore 29 | if url == nil { 30 | return true 31 | } 32 | 33 | if !url!.contains("https://twitch.tv/") || !url!.contains("https://youtube.com/") { 34 | print("[Swiftcord] URL for activities requires either a Twitch or Youtube link. There will be no link to your stream in the RPC section.") 35 | return false 36 | } 37 | 38 | return true 39 | } 40 | } 41 | 42 | public enum ActivityType: Int, Encodable { 43 | case playing 44 | case streaming 45 | case listening 46 | case watching 47 | case custom 48 | case competing 49 | } 50 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Types/DM.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DMChannel.swift 3 | // Swiftcord 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2017 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// DM Type 10 | public struct DM: TextChannel { 11 | 12 | // MARK: Properties 13 | 14 | /// Parent class 15 | public internal(set) weak var swiftcord: Swiftcord? 16 | 17 | /// ID of DM 18 | public let id: Snowflake 19 | 20 | /// The recipient of this DM 21 | public internal(set) var recipient: User 22 | 23 | /// The last message's ID 24 | public let lastMessageId: Snowflake? 25 | 26 | /// Indicates what kind of channel this is 27 | public let type = ChannelType.dm 28 | 29 | // MARK: Initializer 30 | 31 | /** 32 | Creates a DMChannel struct 33 | 34 | - parameter swiftcord: Parent class 35 | - parameter json: JSON representable as a dictionary 36 | */ 37 | init(_ swiftcord: Swiftcord, _ json: [String: Any]) { 38 | self.swiftcord = swiftcord 39 | 40 | self.id = Snowflake(json["id"])! 41 | 42 | let recipients = json["recipients"] as! [[String: Any]] 43 | self.recipient = User(swiftcord, recipients[0]) 44 | 45 | self.lastMessageId = Snowflake(json["last_message_id"]) 46 | 47 | swiftcord.dms[self.recipient.id] = self 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Types/GroupDM.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GroupChannel.swift 3 | // Swiftcord 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2017 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// GroupDM Type 10 | public struct GroupDM: TextChannel { 11 | 12 | // MARK: Properties 13 | 14 | /// Parent class 15 | public internal(set) weak var swiftcord: Swiftcord? 16 | 17 | /// ID of DM 18 | public let id: Snowflake 19 | 20 | /// The recipient of this DM 21 | public internal(set) var recipients = [User]() 22 | 23 | /// The last message's ID 24 | public let lastMessageId: Snowflake? 25 | 26 | /// Indicates what kind of channel this is 27 | public let type = ChannelType.groupDM 28 | 29 | // MARK: Initializer 30 | 31 | /** 32 | Creates a GroupChannel struct 33 | 34 | - parameter swiftcord: Parent class 35 | - parameter json: JSON representable as a dictionary 36 | */ 37 | init(_ swiftcord: Swiftcord, _ json: [String: Any]) { 38 | self.swiftcord = swiftcord 39 | 40 | self.id = Snowflake(json["id"])! 41 | 42 | let recipients = json["recipients"] as! [[String: Any]] 43 | for recipient in recipients { 44 | self.recipients.append(User(swiftcord, recipient)) 45 | } 46 | 47 | self.lastMessageId = Snowflake(json["last_message_id"]) 48 | 49 | swiftcord.groups[self.id] = self 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Types/GuildCategory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Webhook.swift 3 | // Swiftcord 4 | // 5 | // Created by Alejandro Alonso 6 | // Copyright © 2017 Alejandro Alonso. All rights reserved. 7 | // 8 | 9 | /// Guild Channel Category Structure 10 | public class GuildCategory: GuildChannel { 11 | 12 | /// Parent class 13 | public weak var swiftcord: Swiftcord? 14 | 15 | /// Channel Category this channel belongs to 16 | public var category: GuildCategory? { 17 | guard let parentId = parentId else { 18 | return nil 19 | } 20 | 21 | return guild?.channels[parentId] as? GuildCategory 22 | } 23 | 24 | /// Collection of channels this category parents mapped by channel id 25 | public internal(set) var channels = [Snowflake: GuildChannel]() 26 | 27 | /// The id of the channel 28 | public let id: Snowflake 29 | 30 | /// Guild this channel belongs to 31 | public var guild: Guild? { 32 | return self.swiftcord?.getGuild(for: id) 33 | } 34 | 35 | /// Name of the channel 36 | public let name: String? 37 | 38 | /// Parent Category ID of this channel 39 | public let parentId: Snowflake? 40 | 41 | /// Collection of overwrites mapped by `OverwriteID` 42 | public internal(set) var permissionOverwrites = [Snowflake: Overwrite]() 43 | 44 | /// Position the channel is in guild 45 | public let position: Int? 46 | 47 | /// Indicates what type of channel this is (.guildCategory) 48 | public let type = ChannelType.guildCategory 49 | 50 | /** 51 | Creates Guild Category structure 52 | 53 | - parameter swiftcord: The parent class 54 | - parameter json: The data to transform to a webhook 55 | */ 56 | init(_ swiftcord: Swiftcord, _ json: [String: Any]) { 57 | self.swiftcord = swiftcord 58 | 59 | self.id = Snowflake(json["id"])! 60 | self.name = json["name"] as? String 61 | self.parentId = Snowflake(json["parent_id"]) 62 | 63 | if let overwrites = json["permission_overwrites"] as? [[String: Any]] { 64 | for overwrite in overwrites { 65 | let overwrite = Overwrite(overwrite) 66 | self.permissionOverwrites[overwrite.id] = overwrite 67 | } 68 | } 69 | 70 | self.position = json["position"] as? Int 71 | 72 | if let guildId = Snowflake(json["guild_id"]) { 73 | swiftcord.guilds[guildId]!.channels[self.id] = self 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Types/Icon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Icon.swift 3 | // Swiftcord 4 | // 5 | // Created by Noah Pistilli on 2022-01-18. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents the base64 encoded image needed to send to Discord for Emojis, Stickers, etc... 11 | public struct Icon { 12 | public let imageType: ImageType 13 | public let base64Image: String 14 | 15 | public init(imageType: ImageType, image: Data) { 16 | self.imageType = imageType 17 | self.base64Image = image.base64EncodedString() 18 | } 19 | 20 | func toDataString() -> String { 21 | return "data:\(self.imageType.rawValue);base64,\(self.base64Image)" 22 | } 23 | } 24 | 25 | public enum ImageType: String { 26 | case jpeg = "image/jpeg" 27 | case png = "image/png" 28 | case webp = "image/webp" 29 | case gif = "image/gif" 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Types/Interactions/ActionRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionRow.swift 3 | // Swiftcord 4 | // 5 | // Created by Noah Pistilli on 2021-12-19. 6 | // 7 | 8 | import Foundation 9 | 10 | /// ActionRow object that can hold any `Component` 11 | public struct ActionRow: Component { 12 | public let type: ComponentTypes 13 | public let components: [T] 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case type 17 | case components 18 | } 19 | 20 | public init(components: T...) { 21 | self.type = .actionRow 22 | self.components = components 23 | } 24 | 25 | func encode(from encoder: Encoder) throws { 26 | var container = encoder.container(keyedBy: CodingKeys.self) 27 | try container.encode(self.type, forKey: .type) 28 | try container.encode(self.components, forKey: .components) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Swiftcord/Types/Interactions/Button.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Button.swift 3 | // Swiftcord 4 | // 5 | // Created by Noah Pistilli on 2021-12-16. 6 | // 7 | 8 | import Foundation 9 | 10 | public class ButtonBuilder: Encodable { 11 | /// Message above the buttons. This is required if not sending an embed 12 | public var content: String? 13 | 14 | /// Embed above the buttons. 15 | public var embeds: [EmbedBuilder]? 16 | 17 | /// The buttons array 18 | public var components: [ActionRow