From e4c0ca0f4807a36a6cc0ea5f793905539afa4648 Mon Sep 17 00:00:00 2001 From: Wim Date: Sat, 12 Mar 2022 17:06:39 +0100 Subject: [PATCH] Switch to discordgo upstream again (#1759) * Switch to upstream discordgo again * Fix discord api changes --- bridge/discord/discord.go | 2 +- bridge/discord/handlers.go | 4 +- bridge/discord/handlers_test.go | 2 +- bridge/discord/helpers.go | 2 +- bridge/discord/transmitter/transmitter.go | 2 +- bridge/discord/transmitter/utils.go | 2 +- bridge/discord/webhook.go | 4 +- go.mod | 2 +- go.sum | 5 +- .../github.com/bwmarrin/discordgo/.gitignore | 5 + .../bwmarrin/discordgo/.golangci.yml | 19 + .../discordgo/.travis.yml | 1 + .../discordgo/LICENSE | 0 .../discordgo/README.md | 10 +- .../bwmarrin/discordgo/components.go | 241 ++ .../github.com/bwmarrin/discordgo/discord.go | 60 + .../bwmarrin/discordgo/endpoints.go | 210 ++ .../discordgo/event.go | 2 +- .../discordgo/eventhandlers.go | 376 ++- .../discordgo/events.go | 103 + .../bwmarrin/discordgo/interactions.go | 568 +++++ .../github.com/bwmarrin/discordgo/locales.go | 83 + .../discordgo/logging.go | 0 .../discordgo/message.go | 200 +- .../discordgo/mkdocs.yml | 0 .../discordgo/oauth2.go | 49 +- .../discordgo/ratelimit.go | 2 +- .../discordgo/restapi.go | 1244 +++++++--- .../discordgo/state.go | 291 ++- .../github.com/bwmarrin/discordgo/structs.go | 2038 +++++++++++++++++ .../discordgo/types.go | 10 - .../discordgo/user.go | 57 +- vendor/github.com/bwmarrin/discordgo/util.go | 110 + .../discordgo/voice.go | 12 +- .../github.com/bwmarrin/discordgo/webhook.go | 49 + .../discordgo/wsapi.go | 17 +- .../matterbridge/discordgo/.gitignore | 2 - .../matterbridge/discordgo/discord.go | 161 -- .../matterbridge/discordgo/endpoints.go | 153 -- .../matterbridge/discordgo/interactions.go | 54 - .../matterbridge/discordgo/structs.go | 1339 ----------- .../github.com/matterbridge/discordgo/util.go | 17 - vendor/modules.txt | 6 +- 43 files changed, 5151 insertions(+), 2363 deletions(-) create mode 100644 vendor/github.com/bwmarrin/discordgo/.gitignore create mode 100644 vendor/github.com/bwmarrin/discordgo/.golangci.yml rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/.travis.yml (96%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/LICENSE (100%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/README.md (77%) create mode 100644 vendor/github.com/bwmarrin/discordgo/components.go create mode 100644 vendor/github.com/bwmarrin/discordgo/discord.go create mode 100644 vendor/github.com/bwmarrin/discordgo/endpoints.go rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/event.go (99%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/eventhandlers.go (71%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/events.go (68%) create mode 100644 vendor/github.com/bwmarrin/discordgo/interactions.go create mode 100644 vendor/github.com/bwmarrin/discordgo/locales.go rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/logging.go (100%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/message.go (65%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/mkdocs.yml (100%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/oauth2.go (64%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/ratelimit.go (99%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/restapi.go (67%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/state.go (82%) create mode 100644 vendor/github.com/bwmarrin/discordgo/structs.go rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/types.go (79%) rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/user.go (61%) create mode 100644 vendor/github.com/bwmarrin/discordgo/util.go rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/voice.go (98%) create mode 100644 vendor/github.com/bwmarrin/discordgo/webhook.go rename vendor/github.com/{matterbridge => bwmarrin}/discordgo/wsapi.go (96%) delete mode 100644 vendor/github.com/matterbridge/discordgo/.gitignore delete mode 100644 vendor/github.com/matterbridge/discordgo/discord.go delete mode 100644 vendor/github.com/matterbridge/discordgo/endpoints.go delete mode 100644 vendor/github.com/matterbridge/discordgo/interactions.go delete mode 100644 vendor/github.com/matterbridge/discordgo/structs.go delete mode 100644 vendor/github.com/matterbridge/discordgo/util.go diff --git a/bridge/discord/discord.go b/bridge/discord/discord.go index 4e86835b..1c2e8ca4 100644 --- a/bridge/discord/discord.go +++ b/bridge/discord/discord.go @@ -10,8 +10,8 @@ import ( "github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/discord/transmitter" "github.com/42wim/matterbridge/bridge/helper" + "github.com/bwmarrin/discordgo" lru "github.com/hashicorp/golang-lru" - "github.com/matterbridge/discordgo" ) const ( diff --git a/bridge/discord/handlers.go b/bridge/discord/handlers.go index 42c5f383..d2613e1a 100644 --- a/bridge/discord/handlers.go +++ b/bridge/discord/handlers.go @@ -2,8 +2,8 @@ package bdiscord import ( "github.com/42wim/matterbridge/bridge/config" + "github.com/bwmarrin/discordgo" "github.com/davecgh/go-spew/spew" - "github.com/matterbridge/discordgo" ) func (b *Bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) { //nolint:unparam @@ -56,7 +56,7 @@ func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdat return } // only when message is actually edited - if m.Message.EditedTimestamp != "" { + if m.Message.EditedTimestamp != nil { b.Log.Debugf("Sending edit message") m.Content += b.GetString("EditSuffix") msg := &discordgo.MessageCreate{ diff --git a/bridge/discord/handlers_test.go b/bridge/discord/handlers_test.go index a809dadd..915d9b19 100644 --- a/bridge/discord/handlers_test.go +++ b/bridge/discord/handlers_test.go @@ -3,7 +3,7 @@ package bdiscord import ( "testing" - "github.com/matterbridge/discordgo" + "github.com/bwmarrin/discordgo" "github.com/stretchr/testify/assert" ) diff --git a/bridge/discord/helpers.go b/bridge/discord/helpers.go index 4e453ad7..2e18f46c 100644 --- a/bridge/discord/helpers.go +++ b/bridge/discord/helpers.go @@ -6,7 +6,7 @@ import ( "strings" "unicode" - "github.com/matterbridge/discordgo" + "github.com/bwmarrin/discordgo" ) func (b *Bdiscord) getAllowedMentions() *discordgo.MessageAllowedMentions { diff --git a/bridge/discord/transmitter/transmitter.go b/bridge/discord/transmitter/transmitter.go index f327dfb7..71407a1d 100644 --- a/bridge/discord/transmitter/transmitter.go +++ b/bridge/discord/transmitter/transmitter.go @@ -20,7 +20,7 @@ import ( "sync" "time" - "github.com/matterbridge/discordgo" + "github.com/bwmarrin/discordgo" log "github.com/sirupsen/logrus" ) diff --git a/bridge/discord/transmitter/utils.go b/bridge/discord/transmitter/utils.go index f42e81eb..042aa505 100644 --- a/bridge/discord/transmitter/utils.go +++ b/bridge/discord/transmitter/utils.go @@ -1,7 +1,7 @@ package transmitter import ( - "github.com/matterbridge/discordgo" + "github.com/bwmarrin/discordgo" ) // isDiscordPermissionError returns false for nil, and true if a Discord RESTError with code discordgo.ErrorCodeMissionPermissions diff --git a/bridge/discord/webhook.go b/bridge/discord/webhook.go index 3afb942d..c34fc945 100644 --- a/bridge/discord/webhook.go +++ b/bridge/discord/webhook.go @@ -5,7 +5,7 @@ import ( "github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/helper" - "github.com/matterbridge/discordgo" + "github.com/bwmarrin/discordgo" ) // shouldMessageUseWebhooks checks if have a channel specific webhook, if we're not using auto webhooks @@ -89,7 +89,7 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg &discordgo.WebhookParams{ Username: msg.Username, AvatarURL: msg.Avatar, - File: &file, + Files: []*discordgo.File{&file}, Content: content, AllowedMentions: b.getAllowedMentions(), }, diff --git a/go.mod b/go.mod index a4d051f2..d8c5aee2 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560 github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c github.com/SevereCloud/vksdk/v2 v2.13.1 + github.com/bwmarrin/discordgo v0.24.0 github.com/d5/tengo/v2 v2.10.0 github.com/davecgh/go-spew v1.1.1 github.com/fsnotify/fsnotify v1.5.1 @@ -24,7 +25,6 @@ require ( github.com/lrstanley/girc v0.0.0-20211023233735-147f0ff77566 github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696 - github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7 github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75 github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba diff --git a/go.sum b/go.sum index 84125ab2..787fce3c 100644 --- a/go.sum +++ b/go.sum @@ -290,6 +290,8 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/bwmarrin/discordgo v0.24.0 h1:Gw4MYxqHdvhO99A3nXnSLy97z5pmIKHZVJ1JY5ZDPqY= +github.com/bwmarrin/discordgo v0.24.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -1079,8 +1081,6 @@ github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c/go.mod h1:74gB1z2wpxxI github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696 h1:pmPKkN3RJM9wVMZidR99epzK0+gatQiqVtvP1FacZcQ= github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A= -github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7 h1:4J2YZuY8dIYrxbLMsWGqPZb/B59ygCwSBkyZHez5PSY= -github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7/go.mod h1:411nZYv0UMMrtppR5glXop1foboJiFAowy+42U+Ahvw= github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be h1:zlirT+LngOJ60G6FVzI87DljGZLUnfNzmXja61EjtYM= github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q= github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419 h1:dx8x2J3EsVwP3hBGNmVT/otz4b42p7TRQ6Cu4BK2910= @@ -1791,6 +1791,7 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/vendor/github.com/bwmarrin/discordgo/.gitignore b/vendor/github.com/bwmarrin/discordgo/.gitignore new file mode 100644 index 00000000..681a96b1 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/.gitignore @@ -0,0 +1,5 @@ +# IDE-specific metadata +.idea/ + +# Environment variables. Useful for examples. +.env diff --git a/vendor/github.com/bwmarrin/discordgo/.golangci.yml b/vendor/github.com/bwmarrin/discordgo/.golangci.yml new file mode 100644 index 00000000..dd9d2e5b --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/.golangci.yml @@ -0,0 +1,19 @@ +linters: + disable-all: true + enable: + # - staticcheck + # - unused + - golint + +linters-settings: + staticcheck: + go: "1.13" + + checks: ["all"] + + unused: + go: "1.13" + +issues: + include: + - EXC0002 diff --git a/vendor/github.com/matterbridge/discordgo/.travis.yml b/vendor/github.com/bwmarrin/discordgo/.travis.yml similarity index 96% rename from vendor/github.com/matterbridge/discordgo/.travis.yml rename to vendor/github.com/bwmarrin/discordgo/.travis.yml index b54331b1..5d9cea3e 100644 --- a/vendor/github.com/matterbridge/discordgo/.travis.yml +++ b/vendor/github.com/bwmarrin/discordgo/.travis.yml @@ -3,6 +3,7 @@ go: - 1.13.x - 1.14.x - 1.15.x + - 1.16.x env: - GO111MODULE=on install: diff --git a/vendor/github.com/matterbridge/discordgo/LICENSE b/vendor/github.com/bwmarrin/discordgo/LICENSE similarity index 100% rename from vendor/github.com/matterbridge/discordgo/LICENSE rename to vendor/github.com/bwmarrin/discordgo/LICENSE diff --git a/vendor/github.com/matterbridge/discordgo/README.md b/vendor/github.com/bwmarrin/discordgo/README.md similarity index 77% rename from vendor/github.com/matterbridge/discordgo/README.md rename to vendor/github.com/bwmarrin/discordgo/README.md index 289e1622..2bcd43b8 100644 --- a/vendor/github.com/matterbridge/discordgo/README.md +++ b/vendor/github.com/bwmarrin/discordgo/README.md @@ -1,8 +1,8 @@ # DiscordGo -[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discord.com/invite/discord-api) +[![Go Reference](https://pkg.go.dev/badge/github.com/bwmarrin/discordgo.svg)](https://pkg.go.dev/github.com/bwmarrin/discordgo) [![Go Report Card](https://goreportcard.com/badge/github.com/bwmarrin/discordgo)](https://goreportcard.com/report/github.com/bwmarrin/discordgo) [![Build Status](https://travis-ci.com/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.com/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/golang) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discord.com/invite/discord-api) - +DiscordGo logo DiscordGo is a [Go](https://golang.org/) package that provides low level bindings to the [Discord](https://discord.com/) chat client API. DiscordGo @@ -22,7 +22,7 @@ tool that wraps `ffmpeg` to create opus encoded audio appropriate for use with Discord (and DiscordGo). **For help with this package or general Go discussion, please join the [Discord -Gophers](https://discord.gg/0f1SbxBZjYq9jLBk) chat server.** +Gophers](https://discord.gg/golang) chat server.** ## Getting Started @@ -64,8 +64,8 @@ The DiscordGo code is fairly well documented at this point and is currently the only documentation available. Both GoDoc and GoWalker (below) present that information in a nice format. -- [![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) -- [![Go Walker](http://gowalker.org/api/v1/badge)](https://gowalker.org/github.com/bwmarrin/discordgo) +- [![Go Reference](https://pkg.go.dev/badge/github.com/bwmarrin/discordgo.svg)](https://pkg.go.dev/github.com/bwmarrin/discordgo) +- [![Go Walker](https://gowalker.org/api/v1/badge)](https://gowalker.org/github.com/bwmarrin/discordgo) - Hand crafted documentation coming eventually. diff --git a/vendor/github.com/bwmarrin/discordgo/components.go b/vendor/github.com/bwmarrin/discordgo/components.go new file mode 100644 index 00000000..00cbbf19 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/components.go @@ -0,0 +1,241 @@ +package discordgo + +import ( + "encoding/json" + "fmt" +) + +// ComponentType is type of component. +type ComponentType uint + +// MessageComponent types. +const ( + ActionsRowComponent ComponentType = 1 + ButtonComponent ComponentType = 2 + SelectMenuComponent ComponentType = 3 + TextInputComponent ComponentType = 4 +) + +// MessageComponent is a base interface for all message components. +type MessageComponent interface { + json.Marshaler + Type() ComponentType +} + +type unmarshalableMessageComponent struct { + MessageComponent +} + +// UnmarshalJSON is a helper function to unmarshal MessageComponent object. +func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error { + var v struct { + Type ComponentType `json:"type"` + } + err := json.Unmarshal(src, &v) + if err != nil { + return err + } + + switch v.Type { + case ActionsRowComponent: + umc.MessageComponent = &ActionsRow{} + case ButtonComponent: + umc.MessageComponent = &Button{} + case SelectMenuComponent: + umc.MessageComponent = &SelectMenu{} + case TextInputComponent: + umc.MessageComponent = &TextInput{} + default: + return fmt.Errorf("unknown component type: %d", v.Type) + } + return json.Unmarshal(src, umc.MessageComponent) +} + +// MessageComponentFromJSON is a helper function for unmarshaling message components +func MessageComponentFromJSON(b []byte) (MessageComponent, error) { + var u unmarshalableMessageComponent + err := u.UnmarshalJSON(b) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal into MessageComponent: %w", err) + } + return u.MessageComponent, nil +} + +// ActionsRow is a container for components within one row. +type ActionsRow struct { + Components []MessageComponent `json:"components"` +} + +// MarshalJSON is a method for marshaling ActionsRow to a JSON object. +func (r ActionsRow) MarshalJSON() ([]byte, error) { + type actionsRow ActionsRow + + return json.Marshal(struct { + actionsRow + Type ComponentType `json:"type"` + }{ + actionsRow: actionsRow(r), + Type: r.Type(), + }) +} + +// UnmarshalJSON is a helper function to unmarshal Actions Row. +func (r *ActionsRow) UnmarshalJSON(data []byte) error { + var v struct { + RawComponents []unmarshalableMessageComponent `json:"components"` + } + err := json.Unmarshal(data, &v) + if err != nil { + return err + } + r.Components = make([]MessageComponent, len(v.RawComponents)) + for i, v := range v.RawComponents { + r.Components[i] = v.MessageComponent + } + + return err +} + +// Type is a method to get the type of a component. +func (r ActionsRow) Type() ComponentType { + return ActionsRowComponent +} + +// ButtonStyle is style of button. +type ButtonStyle uint + +// Button styles. +const ( + // PrimaryButton is a button with blurple color. + PrimaryButton ButtonStyle = 1 + // SecondaryButton is a button with grey color. + SecondaryButton ButtonStyle = 2 + // SuccessButton is a button with green color. + SuccessButton ButtonStyle = 3 + // DangerButton is a button with red color. + DangerButton ButtonStyle = 4 + // LinkButton is a special type of button which navigates to a URL. Has grey color. + LinkButton ButtonStyle = 5 +) + +// ComponentEmoji represents button emoji, if it does have one. +type ComponentEmoji struct { + Name string `json:"name,omitempty"` + ID string `json:"id,omitempty"` + Animated bool `json:"animated,omitempty"` +} + +// Button represents button component. +type Button struct { + Label string `json:"label"` + Style ButtonStyle `json:"style"` + Disabled bool `json:"disabled"` + Emoji ComponentEmoji `json:"emoji"` + + // NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID. + URL string `json:"url,omitempty"` + CustomID string `json:"custom_id,omitempty"` +} + +// MarshalJSON is a method for marshaling Button to a JSON object. +func (b Button) MarshalJSON() ([]byte, error) { + type button Button + + if b.Style == 0 { + b.Style = PrimaryButton + } + + return json.Marshal(struct { + button + Type ComponentType `json:"type"` + }{ + button: button(b), + Type: b.Type(), + }) +} + +// Type is a method to get the type of a component. +func (Button) Type() ComponentType { + return ButtonComponent +} + +// SelectMenuOption represents an option for a select menu. +type SelectMenuOption struct { + Label string `json:"label,omitempty"` + Value string `json:"value"` + Description string `json:"description"` + Emoji ComponentEmoji `json:"emoji"` + // Determines whenever option is selected by default or not. + Default bool `json:"default"` +} + +// SelectMenu represents select menu component. +type SelectMenu struct { + CustomID string `json:"custom_id,omitempty"` + // The text which will be shown in the menu if there's no default options or all options was deselected and component was closed. + Placeholder string `json:"placeholder"` + // This value determines the minimal amount of selected items in the menu. + MinValues *int `json:"min_values,omitempty"` + // This value determines the maximal amount of selected items in the menu. + // If MaxValues or MinValues are greater than one then the user can select multiple items in the component. + MaxValues int `json:"max_values,omitempty"` + Options []SelectMenuOption `json:"options"` + Disabled bool `json:"disabled"` +} + +// Type is a method to get the type of a component. +func (SelectMenu) Type() ComponentType { + return SelectMenuComponent +} + +// MarshalJSON is a method for marshaling SelectMenu to a JSON object. +func (m SelectMenu) MarshalJSON() ([]byte, error) { + type selectMenu SelectMenu + + return json.Marshal(struct { + selectMenu + Type ComponentType `json:"type"` + }{ + selectMenu: selectMenu(m), + Type: m.Type(), + }) +} + +// TextInput represents text input component. +type TextInput struct { + CustomID string `json:"custom_id"` + Label string `json:"label"` + Style TextInputStyle `json:"style"` + Placeholder string `json:"placeholder,omitempty"` + Value string `json:"value,omitempty"` + Required bool `json:"required,omitempty"` + MinLength int `json:"min_length,omitempty"` + MaxLength int `json:"max_length,omitempty"` +} + +// Type is a method to get the type of a component. +func (TextInput) Type() ComponentType { + return TextInputComponent +} + +// MarshalJSON is a method for marshaling TextInput to a JSON object. +func (m TextInput) MarshalJSON() ([]byte, error) { + type inputText TextInput + + return json.Marshal(struct { + inputText + Type ComponentType `json:"type"` + }{ + inputText: inputText(m), + Type: m.Type(), + }) +} + +// TextInputStyle is style of text in TextInput component. +type TextInputStyle uint + +// Text styles +const ( + TextInputShort TextInputStyle = 1 + TextInputParagraph TextInputStyle = 2 +) diff --git a/vendor/github.com/bwmarrin/discordgo/discord.go b/vendor/github.com/bwmarrin/discordgo/discord.go new file mode 100644 index 00000000..74f4190a --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/discord.go @@ -0,0 +1,60 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains high level helper functions and easy entry points for the +// entire discordgo package. These functions are being developed and are very +// experimental at this point. They will most likely change so please use the +// low level functions if that's a problem. + +// Package discordgo provides Discord binding for Go +package discordgo + +import ( + "net/http" + "runtime" + "time" +) + +// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/) +const VERSION = "0.24.0" + +// New creates a new Discord session with provided token. +// If the token is for a bot, it must be prefixed with "Bot " +// e.g. "Bot ..." +// Or if it is an OAuth2 token, it must be prefixed with "Bearer " +// e.g. "Bearer ..." +func New(token string) (s *Session, err error) { + + // Create an empty Session interface. + s = &Session{ + State: NewState(), + Ratelimiter: NewRatelimiter(), + StateEnabled: true, + Compress: true, + ShouldReconnectOnError: true, + ShardID: 0, + ShardCount: 1, + MaxRestRetries: 3, + Client: &http.Client{Timeout: (20 * time.Second)}, + UserAgent: "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")", + sequence: new(int64), + LastHeartbeatAck: time.Now().UTC(), + } + + // Initilize the Identify Package with defaults + // These can be modified prior to calling Open() + s.Identify.Compress = true + s.Identify.LargeThreshold = 250 + s.Identify.GuildSubscriptions = true + s.Identify.Properties.OS = runtime.GOOS + s.Identify.Properties.Browser = "DiscordGo v" + VERSION + s.Identify.Intents = IntentsAllWithoutPrivileged + s.Identify.Token = token + s.Token = token + + return +} diff --git a/vendor/github.com/bwmarrin/discordgo/endpoints.go b/vendor/github.com/bwmarrin/discordgo/endpoints.go new file mode 100644 index 00000000..d39a175b --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/endpoints.go @@ -0,0 +1,210 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains variables for all known Discord end points. All functions +// throughout the Discordgo package use these variables for all connections +// to Discord. These are all exported and you may modify them if needed. + +package discordgo + +import "strconv" + +// APIVersion is the Discord API version used for the REST and Websocket API. +var APIVersion = "9" + +// Known Discord API Endpoints. +var ( + EndpointStatus = "https://status.discord.com/api/v2/" + EndpointSm = EndpointStatus + "scheduled-maintenances/" + EndpointSmActive = EndpointSm + "active.json" + EndpointSmUpcoming = EndpointSm + "upcoming.json" + + EndpointDiscord = "https://discord.com/" + EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/" + EndpointGuilds = EndpointAPI + "guilds/" + EndpointChannels = EndpointAPI + "channels/" + EndpointUsers = EndpointAPI + "users/" + EndpointGateway = EndpointAPI + "gateway" + EndpointGatewayBot = EndpointGateway + "/bot" + EndpointWebhooks = EndpointAPI + "webhooks/" + EndpointStickers = EndpointAPI + "stickers/" + + EndpointCDN = "https://cdn.discordapp.com/" + EndpointCDNAttachments = EndpointCDN + "attachments/" + EndpointCDNAvatars = EndpointCDN + "avatars/" + EndpointCDNIcons = EndpointCDN + "icons/" + EndpointCDNSplashes = EndpointCDN + "splashes/" + EndpointCDNChannelIcons = EndpointCDN + "channel-icons/" + EndpointCDNBanners = EndpointCDN + "banners/" + EndpointCDNGuilds = EndpointCDN + "guilds/" + + EndpointVoice = EndpointAPI + "/voice/" + EndpointVoiceRegions = EndpointVoice + "regions" + + // TODO: EndpointUserGuildMember + + EndpointUser = func(uID string) string { return EndpointUsers + uID } + EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" } + EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" } + EndpointDefaultUserAvatar = func(uDiscriminator string) string { + uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator) + return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png" + } + EndpointUserBanner = func(uID, cID string) string { + return EndpointCDNBanners + uID + "/" + cID + ".png" + } + EndpointUserBannerAnimated = func(uID, cID string) string { + return EndpointCDNBanners + uID + "/" + cID + ".gif" + } + + EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" } + EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID } + EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" } + EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" } + + EndpointGuild = func(gID string) string { return EndpointGuilds + gID } + EndpointGuildThreads = func(gID string) string { return EndpointGuild(gID) + "/threads" } + EndpointGuildActiveThreads = func(gID string) string { return EndpointGuildThreads(gID) + "/active" } + EndpointGuildPreview = func(gID string) string { return EndpointGuilds + gID + "/preview" } + EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" } + EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" } + EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID } + EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID } + EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" } + EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID } + EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" } + EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID } + EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" } + EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID } + EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" } + EndpointGuildWidget = func(gID string) string { return EndpointGuilds + gID + "/widget" } + EndpointGuildEmbed = EndpointGuildWidget + EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" } + EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" } + EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" } + EndpointGuildSplash = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" } + EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" } + EndpointGuildAuditLogs = func(gID string) string { return EndpointGuilds + gID + "/audit-logs" } + EndpointGuildEmojis = func(gID string) string { return EndpointGuilds + gID + "/emojis" } + EndpointGuildEmoji = func(gID, eID string) string { return EndpointGuilds + gID + "/emojis/" + eID } + EndpointGuildBanner = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".png" } + EndpointGuildStickers = func(gID string) string { return EndpointGuilds + gID + "/stickers" } + EndpointGuildSticker = func(gID, sID string) string { return EndpointGuilds + gID + "/stickers/" + sID } + EndpointGuildScheduledEvents = func(gID string) string { return EndpointGuilds + gID + "/scheduled-events" } + EndpointGuildScheduledEvent = func(gID, eID string) string { return EndpointGuilds + gID + "/scheduled-events/" + eID } + EndpointGuildScheduledEventUsers = func(gID, eID string) string { return EndpointGuildScheduledEvent(gID, eID) + "/users" } + EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "/templates/" + tID } + EndpointGuildTemplates = func(gID string) string { return EndpointGuilds + gID + "/templates" } + EndpointGuildTemplateSync = func(gID, tID string) string { return EndpointGuilds + gID + "/templates/" + tID } + EndpointGuildMemberAvatar = func(gId, uID, aID string) string { + return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".png" + } + EndpointGuildMemberAvatarAnimated = func(gId, uID, aID string) string { + return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".gif" + } + + EndpointChannel = func(cID string) string { return EndpointChannels + cID } + EndpointChannelThreads = func(cID string) string { return EndpointChannel(cID) + "/threads" } + EndpointChannelActiveThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/active" } + EndpointChannelPublicArchivedThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/archived/public" } + EndpointChannelPrivateArchivedThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/archived/private" } + EndpointChannelJoinedPrivateArchivedThreads = func(cID string) string { return EndpointChannel(cID) + "/users/@me/threads/archived/private" } + EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" } + EndpointChannelPermission = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID } + EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" } + EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" } + EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" } + EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID } + EndpointChannelMessageThread = func(cID, mID string) string { return EndpointChannelMessage(cID, mID) + "/threads" } + EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" } + EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" } + EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID } + EndpointChannelMessageCrosspost = func(cID, mID string) string { return EndpointChannel(cID) + "/messages/" + mID + "/crosspost" } + EndpointChannelFollow = func(cID string) string { return EndpointChannel(cID) + "/followers" } + EndpointThreadMembers = func(tID string) string { return EndpointChannel(tID) + "/thread-members" } + EndpointThreadMember = func(tID, mID string) string { return EndpointThreadMembers(tID) + "/" + mID } + + EndpointGroupIcon = func(cID, hash string) string { return EndpointCDNChannelIcons + cID + "/" + hash + ".png" } + + EndpointSticker = func(sID string) string { return EndpointStickers + sID } + EndpointNitroStickersPacks = EndpointAPI + "/sticker-packs" + + EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" } + EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID } + EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token } + EndpointWebhookMessage = func(wID, token, messageID string) string { + return EndpointWebhookToken(wID, token) + "/messages/" + messageID + } + + EndpointMessageReactionsAll = func(cID, mID string) string { + return EndpointChannelMessage(cID, mID) + "/reactions" + } + EndpointMessageReactions = func(cID, mID, eID string) string { + return EndpointChannelMessage(cID, mID) + "/reactions/" + eID + } + EndpointMessageReaction = func(cID, mID, eID, uID string) string { + return EndpointMessageReactions(cID, mID, eID) + "/" + uID + } + + EndpointApplicationGlobalCommands = func(aID string) string { + return EndpointApplication(aID) + "/commands" + } + EndpointApplicationGlobalCommand = func(aID, cID string) string { + return EndpointApplicationGlobalCommands(aID) + "/" + cID + } + + EndpointApplicationGuildCommands = func(aID, gID string) string { + return EndpointApplication(aID) + "/guilds/" + gID + "/commands" + } + EndpointApplicationGuildCommand = func(aID, gID, cID string) string { + return EndpointApplicationGuildCommands(aID, gID) + "/" + cID + } + EndpointApplicationCommandPermissions = func(aID, gID, cID string) string { + return EndpointApplicationGuildCommand(aID, gID, cID) + "/permissions" + } + EndpointApplicationCommandsGuildPermissions = func(aID, gID string) string { + return EndpointApplicationGuildCommands(aID, gID) + "/permissions" + } + EndpointInteraction = func(aID, iToken string) string { + return EndpointAPI + "interactions/" + aID + "/" + iToken + } + EndpointInteractionResponse = func(iID, iToken string) string { + return EndpointInteraction(iID, iToken) + "/callback" + } + EndpointInteractionResponseActions = func(aID, iToken string) string { + return EndpointWebhookMessage(aID, iToken, "@original") + } + EndpointFollowupMessage = func(aID, iToken string) string { + return EndpointWebhookToken(aID, iToken) + } + EndpointFollowupMessageActions = func(aID, iToken, mID string) string { + return EndpointWebhookMessage(aID, iToken, mID) + } + + EndpointGuildCreate = EndpointAPI + "guilds" + + EndpointInvite = func(iID string) string { return EndpointAPI + "invites/" + iID } + + EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" } + EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" } + + EndpointApplications = EndpointAPI + "applications" + EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } + + EndpointOAuth2 = EndpointAPI + "oauth2/" + EndpointOAuth2Applications = EndpointOAuth2 + "applications" + EndpointOAuth2Application = func(aID string) string { return EndpointOAuth2Applications + "/" + aID } + EndpointOAuth2ApplicationsBot = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/bot" } + EndpointOAuth2ApplicationAssets = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/assets" } + + // TODO: Deprecated, remove in the next release + EndpointOauth2 = EndpointOAuth2 + EndpointOauth2Applications = EndpointOAuth2Applications + EndpointOauth2Application = EndpointOAuth2Application + EndpointOauth2ApplicationsBot = EndpointOAuth2ApplicationsBot + EndpointOauth2ApplicationAssets = EndpointOAuth2ApplicationAssets +) diff --git a/vendor/github.com/matterbridge/discordgo/event.go b/vendor/github.com/bwmarrin/discordgo/event.go similarity index 99% rename from vendor/github.com/matterbridge/discordgo/event.go rename to vendor/github.com/bwmarrin/discordgo/event.go index 67c5f7dd..84dbdc7f 100644 --- a/vendor/github.com/matterbridge/discordgo/event.go +++ b/vendor/github.com/bwmarrin/discordgo/event.go @@ -157,7 +157,7 @@ func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance onceHandlers := s.onceHandlers[t] for i := range onceHandlers { if onceHandlers[i] == ehi { - s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...) + s.onceHandlers[t] = append(onceHandlers[:i], onceHandlers[i+1:]...) } } } diff --git a/vendor/github.com/matterbridge/discordgo/eventhandlers.go b/vendor/github.com/bwmarrin/discordgo/eventhandlers.go similarity index 71% rename from vendor/github.com/matterbridge/discordgo/eventhandlers.go rename to vendor/github.com/bwmarrin/discordgo/eventhandlers.go index d2b9a98b..18d6248a 100644 --- a/vendor/github.com/matterbridge/discordgo/eventhandlers.go +++ b/vendor/github.com/bwmarrin/discordgo/eventhandlers.go @@ -7,50 +7,62 @@ package discordgo // Event type values are used to match the events returned by Discord. // EventTypes surrounded by __ are synthetic and are internal to DiscordGo. const ( - channelCreateEventType = "CHANNEL_CREATE" - channelDeleteEventType = "CHANNEL_DELETE" - channelPinsUpdateEventType = "CHANNEL_PINS_UPDATE" - channelUpdateEventType = "CHANNEL_UPDATE" - connectEventType = "__CONNECT__" - disconnectEventType = "__DISCONNECT__" - eventEventType = "__EVENT__" - guildBanAddEventType = "GUILD_BAN_ADD" - guildBanRemoveEventType = "GUILD_BAN_REMOVE" - guildCreateEventType = "GUILD_CREATE" - guildDeleteEventType = "GUILD_DELETE" - guildEmojisUpdateEventType = "GUILD_EMOJIS_UPDATE" - guildIntegrationsUpdateEventType = "GUILD_INTEGRATIONS_UPDATE" - guildMemberAddEventType = "GUILD_MEMBER_ADD" - guildMemberRemoveEventType = "GUILD_MEMBER_REMOVE" - guildMemberUpdateEventType = "GUILD_MEMBER_UPDATE" - guildMembersChunkEventType = "GUILD_MEMBERS_CHUNK" - guildRoleCreateEventType = "GUILD_ROLE_CREATE" - guildRoleDeleteEventType = "GUILD_ROLE_DELETE" - guildRoleUpdateEventType = "GUILD_ROLE_UPDATE" - guildUpdateEventType = "GUILD_UPDATE" - messageAckEventType = "MESSAGE_ACK" - messageCreateEventType = "MESSAGE_CREATE" - messageDeleteEventType = "MESSAGE_DELETE" - messageDeleteBulkEventType = "MESSAGE_DELETE_BULK" - messageReactionAddEventType = "MESSAGE_REACTION_ADD" - messageReactionRemoveEventType = "MESSAGE_REACTION_REMOVE" - messageReactionRemoveAllEventType = "MESSAGE_REACTION_REMOVE_ALL" - messageUpdateEventType = "MESSAGE_UPDATE" - presenceUpdateEventType = "PRESENCE_UPDATE" - presencesReplaceEventType = "PRESENCES_REPLACE" - rateLimitEventType = "__RATE_LIMIT__" - readyEventType = "READY" - relationshipAddEventType = "RELATIONSHIP_ADD" - relationshipRemoveEventType = "RELATIONSHIP_REMOVE" - resumedEventType = "RESUMED" - typingStartEventType = "TYPING_START" - userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE" - userNoteUpdateEventType = "USER_NOTE_UPDATE" - userSettingsUpdateEventType = "USER_SETTINGS_UPDATE" - userUpdateEventType = "USER_UPDATE" - voiceServerUpdateEventType = "VOICE_SERVER_UPDATE" - voiceStateUpdateEventType = "VOICE_STATE_UPDATE" - webhooksUpdateEventType = "WEBHOOKS_UPDATE" + channelCreateEventType = "CHANNEL_CREATE" + channelDeleteEventType = "CHANNEL_DELETE" + channelPinsUpdateEventType = "CHANNEL_PINS_UPDATE" + channelUpdateEventType = "CHANNEL_UPDATE" + connectEventType = "__CONNECT__" + disconnectEventType = "__DISCONNECT__" + eventEventType = "__EVENT__" + guildBanAddEventType = "GUILD_BAN_ADD" + guildBanRemoveEventType = "GUILD_BAN_REMOVE" + guildCreateEventType = "GUILD_CREATE" + guildDeleteEventType = "GUILD_DELETE" + guildEmojisUpdateEventType = "GUILD_EMOJIS_UPDATE" + guildIntegrationsUpdateEventType = "GUILD_INTEGRATIONS_UPDATE" + guildMemberAddEventType = "GUILD_MEMBER_ADD" + guildMemberRemoveEventType = "GUILD_MEMBER_REMOVE" + guildMemberUpdateEventType = "GUILD_MEMBER_UPDATE" + guildMembersChunkEventType = "GUILD_MEMBERS_CHUNK" + guildRoleCreateEventType = "GUILD_ROLE_CREATE" + guildRoleDeleteEventType = "GUILD_ROLE_DELETE" + guildRoleUpdateEventType = "GUILD_ROLE_UPDATE" + guildUpdateEventType = "GUILD_UPDATE" + guildScheduledEventCreateEventType = "GUILD_SCHEDULED_EVENT_CREATE" + guildScheduledEventUpdateEventType = "GUILD_SCHEDULED_EVENT_UPDATE" + guildScheduledEventDeleteEventType = "GUILD_SCHEDULED_EVENT_DELETE" + interactionCreateEventType = "INTERACTION_CREATE" + inviteCreateEventType = "INVITE_CREATE" + inviteDeleteEventType = "INVITE_DELETE" + messageAckEventType = "MESSAGE_ACK" + messageCreateEventType = "MESSAGE_CREATE" + messageDeleteEventType = "MESSAGE_DELETE" + messageDeleteBulkEventType = "MESSAGE_DELETE_BULK" + messageReactionAddEventType = "MESSAGE_REACTION_ADD" + messageReactionRemoveEventType = "MESSAGE_REACTION_REMOVE" + messageReactionRemoveAllEventType = "MESSAGE_REACTION_REMOVE_ALL" + messageUpdateEventType = "MESSAGE_UPDATE" + presenceUpdateEventType = "PRESENCE_UPDATE" + presencesReplaceEventType = "PRESENCES_REPLACE" + rateLimitEventType = "__RATE_LIMIT__" + readyEventType = "READY" + relationshipAddEventType = "RELATIONSHIP_ADD" + relationshipRemoveEventType = "RELATIONSHIP_REMOVE" + resumedEventType = "RESUMED" + threadCreateEventType = "THREAD_CREATE" + threadDeleteEventType = "THREAD_DELETE" + threadListSyncEventType = "THREAD_LIST_SYNC" + threadMemberUpdateEventType = "THREAD_MEMBER_UPDATE" + threadMembersUpdateEventType = "THREAD_MEMBERS_UPDATE" + threadUpdateEventType = "THREAD_UPDATE" + typingStartEventType = "TYPING_START" + userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE" + userNoteUpdateEventType = "USER_NOTE_UPDATE" + userSettingsUpdateEventType = "USER_SETTINGS_UPDATE" + userUpdateEventType = "USER_UPDATE" + voiceServerUpdateEventType = "VOICE_SERVER_UPDATE" + voiceStateUpdateEventType = "VOICE_STATE_UPDATE" + webhooksUpdateEventType = "WEBHOOKS_UPDATE" ) // channelCreateEventHandler is an event handler for ChannelCreate events. @@ -298,6 +310,66 @@ func (eh guildIntegrationsUpdateEventHandler) Handle(s *Session, i interface{}) } } +// guildScheduledEventCreateEventHandler is an event handler for GuildScheduledEventCreate events. +type guildScheduledEventCreateEventHandler func(*Session, *GuildScheduledEventCreate) + +// Type returns the event type for GuildScheduledEventCreate events. +func (eh guildScheduledEventCreateEventHandler) Type() string { + return guildScheduledEventCreateEventType +} + +// New returns a new instance of GuildScheduledEventCreate. +func (eh guildScheduledEventCreateEventHandler) New() interface{} { + return &GuildScheduledEventCreate{} +} + +// Handle is the handler for GuildScheduledEventCreate events. +func (eh guildScheduledEventCreateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*GuildScheduledEventCreate); ok { + eh(s, t) + } +} + +// guildScheduledEventUpdateEventHandler is an event handler for GuildScheduledEventUpdate events. +type guildScheduledEventUpdateEventHandler func(*Session, *GuildScheduledEventUpdate) + +// Type returns the event type for GuildScheduledEventUpdate events. +func (eh guildScheduledEventUpdateEventHandler) Type() string { + return guildScheduledEventUpdateEventType +} + +// New returns a new instance of GuildScheduledEventUpdate. +func (eh guildScheduledEventUpdateEventHandler) New() interface{} { + return &GuildScheduledEventUpdate{} +} + +// Handle is the handler for GuildScheduledEventUpdate events. +func (eh guildScheduledEventUpdateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*GuildScheduledEventUpdate); ok { + eh(s, t) + } +} + +// guildScheduledEventDeleteEventHandler is an event handler for GuildScheduledEventDelete events. +type guildScheduledEventDeleteEventHandler func(*Session, *GuildScheduledEventDelete) + +// Type returns the event type for GuildScheduledEventDelete events. +func (eh guildScheduledEventDeleteEventHandler) Type() string { + return guildScheduledEventDeleteEventType +} + +// New returns a new instance of GuildScheduledEventDelete. +func (eh guildScheduledEventDeleteEventHandler) New() interface{} { + return &GuildScheduledEventDelete{} +} + +// Handle is the handler for GuildScheduledEventDelete events. +func (eh guildScheduledEventDeleteEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*GuildScheduledEventDelete); ok { + eh(s, t) + } +} + // guildMemberAddEventHandler is an event handler for GuildMemberAdd events. type guildMemberAddEventHandler func(*Session, *GuildMemberAdd) @@ -458,6 +530,66 @@ func (eh guildUpdateEventHandler) Handle(s *Session, i interface{}) { } } +// interactionCreateEventHandler is an event handler for InteractionCreate events. +type interactionCreateEventHandler func(*Session, *InteractionCreate) + +// Type returns the event type for InteractionCreate events. +func (eh interactionCreateEventHandler) Type() string { + return interactionCreateEventType +} + +// New returns a new instance of InteractionCreate. +func (eh interactionCreateEventHandler) New() interface{} { + return &InteractionCreate{} +} + +// Handle is the handler for InteractionCreate events. +func (eh interactionCreateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*InteractionCreate); ok { + eh(s, t) + } +} + +// inviteCreateEventHandler is an event handler for InviteCreate events. +type inviteCreateEventHandler func(*Session, *InviteCreate) + +// Type returns the event type for InviteCreate events. +func (eh inviteCreateEventHandler) Type() string { + return inviteCreateEventType +} + +// New returns a new instance of InviteCreate. +func (eh inviteCreateEventHandler) New() interface{} { + return &InviteCreate{} +} + +// Handle is the handler for InviteCreate events. +func (eh inviteCreateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*InviteCreate); ok { + eh(s, t) + } +} + +// inviteDeleteEventHandler is an event handler for InviteDelete events. +type inviteDeleteEventHandler func(*Session, *InviteDelete) + +// Type returns the event type for InviteDelete events. +func (eh inviteDeleteEventHandler) Type() string { + return inviteDeleteEventType +} + +// New returns a new instance of InviteDelete. +func (eh inviteDeleteEventHandler) New() interface{} { + return &InviteDelete{} +} + +// Handle is the handler for InviteDelete events. +func (eh inviteDeleteEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*InviteDelete); ok { + eh(s, t) + } +} + // messageAckEventHandler is an event handler for MessageAck events. type messageAckEventHandler func(*Session, *MessageAck) @@ -753,6 +885,126 @@ func (eh resumedEventHandler) Handle(s *Session, i interface{}) { } } +// threadCreateEventHandler is an event handler for ThreadCreate events. +type threadCreateEventHandler func(*Session, *ThreadCreate) + +// Type returns the event type for ThreadCreate events. +func (eh threadCreateEventHandler) Type() string { + return threadCreateEventType +} + +// New returns a new instance of ThreadCreate. +func (eh threadCreateEventHandler) New() interface{} { + return &ThreadCreate{} +} + +// Handle is the handler for ThreadCreate events. +func (eh threadCreateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*ThreadCreate); ok { + eh(s, t) + } +} + +// threadDeleteEventHandler is an event handler for ThreadDelete events. +type threadDeleteEventHandler func(*Session, *ThreadDelete) + +// Type returns the event type for ThreadDelete events. +func (eh threadDeleteEventHandler) Type() string { + return threadDeleteEventType +} + +// New returns a new instance of ThreadDelete. +func (eh threadDeleteEventHandler) New() interface{} { + return &ThreadDelete{} +} + +// Handle is the handler for ThreadDelete events. +func (eh threadDeleteEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*ThreadDelete); ok { + eh(s, t) + } +} + +// threadListSyncEventHandler is an event handler for ThreadListSync events. +type threadListSyncEventHandler func(*Session, *ThreadListSync) + +// Type returns the event type for ThreadListSync events. +func (eh threadListSyncEventHandler) Type() string { + return threadListSyncEventType +} + +// New returns a new instance of ThreadListSync. +func (eh threadListSyncEventHandler) New() interface{} { + return &ThreadListSync{} +} + +// Handle is the handler for ThreadListSync events. +func (eh threadListSyncEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*ThreadListSync); ok { + eh(s, t) + } +} + +// threadMemberUpdateEventHandler is an event handler for ThreadMemberUpdate events. +type threadMemberUpdateEventHandler func(*Session, *ThreadMemberUpdate) + +// Type returns the event type for ThreadMemberUpdate events. +func (eh threadMemberUpdateEventHandler) Type() string { + return threadMemberUpdateEventType +} + +// New returns a new instance of ThreadMemberUpdate. +func (eh threadMemberUpdateEventHandler) New() interface{} { + return &ThreadMemberUpdate{} +} + +// Handle is the handler for ThreadMemberUpdate events. +func (eh threadMemberUpdateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*ThreadMemberUpdate); ok { + eh(s, t) + } +} + +// threadMembersUpdateEventHandler is an event handler for ThreadMembersUpdate events. +type threadMembersUpdateEventHandler func(*Session, *ThreadMembersUpdate) + +// Type returns the event type for ThreadMembersUpdate events. +func (eh threadMembersUpdateEventHandler) Type() string { + return threadMembersUpdateEventType +} + +// New returns a new instance of ThreadMembersUpdate. +func (eh threadMembersUpdateEventHandler) New() interface{} { + return &ThreadMembersUpdate{} +} + +// Handle is the handler for ThreadMembersUpdate events. +func (eh threadMembersUpdateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*ThreadMembersUpdate); ok { + eh(s, t) + } +} + +// threadUpdateEventHandler is an event handler for ThreadUpdate events. +type threadUpdateEventHandler func(*Session, *ThreadUpdate) + +// Type returns the event type for ThreadUpdate events. +func (eh threadUpdateEventHandler) Type() string { + return threadUpdateEventType +} + +// New returns a new instance of ThreadUpdate. +func (eh threadUpdateEventHandler) New() interface{} { + return &ThreadUpdate{} +} + +// Handle is the handler for ThreadUpdate events. +func (eh threadUpdateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*ThreadUpdate); ok { + eh(s, t) + } +} + // typingStartEventHandler is an event handler for TypingStart events. type typingStartEventHandler func(*Session, *TypingStart) @@ -943,6 +1195,12 @@ func handlerForInterface(handler interface{}) EventHandler { return guildEmojisUpdateEventHandler(v) case func(*Session, *GuildIntegrationsUpdate): return guildIntegrationsUpdateEventHandler(v) + case func(*Session, *GuildScheduledEventCreate): + return guildScheduledEventCreateEventHandler(v) + case func(*Session, *GuildScheduledEventUpdate): + return guildScheduledEventUpdateEventHandler(v) + case func(*Session, *GuildScheduledEventDelete): + return guildScheduledEventDeleteEventHandler(v) case func(*Session, *GuildMemberAdd): return guildMemberAddEventHandler(v) case func(*Session, *GuildMemberRemove): @@ -959,6 +1217,12 @@ func handlerForInterface(handler interface{}) EventHandler { return guildRoleUpdateEventHandler(v) case func(*Session, *GuildUpdate): return guildUpdateEventHandler(v) + case func(*Session, *InteractionCreate): + return interactionCreateEventHandler(v) + case func(*Session, *InviteCreate): + return inviteCreateEventHandler(v) + case func(*Session, *InviteDelete): + return inviteDeleteEventHandler(v) case func(*Session, *MessageAck): return messageAckEventHandler(v) case func(*Session, *MessageCreate): @@ -989,6 +1253,18 @@ func handlerForInterface(handler interface{}) EventHandler { return relationshipRemoveEventHandler(v) case func(*Session, *Resumed): return resumedEventHandler(v) + case func(*Session, *ThreadCreate): + return threadCreateEventHandler(v) + case func(*Session, *ThreadDelete): + return threadDeleteEventHandler(v) + case func(*Session, *ThreadListSync): + return threadListSyncEventHandler(v) + case func(*Session, *ThreadMemberUpdate): + return threadMemberUpdateEventHandler(v) + case func(*Session, *ThreadMembersUpdate): + return threadMembersUpdateEventHandler(v) + case func(*Session, *ThreadUpdate): + return threadUpdateEventHandler(v) case func(*Session, *TypingStart): return typingStartEventHandler(v) case func(*Session, *UserGuildSettingsUpdate): @@ -1021,6 +1297,9 @@ func init() { registerInterfaceProvider(guildDeleteEventHandler(nil)) registerInterfaceProvider(guildEmojisUpdateEventHandler(nil)) registerInterfaceProvider(guildIntegrationsUpdateEventHandler(nil)) + registerInterfaceProvider(guildScheduledEventCreateEventHandler(nil)) + registerInterfaceProvider(guildScheduledEventUpdateEventHandler(nil)) + registerInterfaceProvider(guildScheduledEventDeleteEventHandler(nil)) registerInterfaceProvider(guildMemberAddEventHandler(nil)) registerInterfaceProvider(guildMemberRemoveEventHandler(nil)) registerInterfaceProvider(guildMemberUpdateEventHandler(nil)) @@ -1029,6 +1308,9 @@ func init() { registerInterfaceProvider(guildRoleDeleteEventHandler(nil)) registerInterfaceProvider(guildRoleUpdateEventHandler(nil)) registerInterfaceProvider(guildUpdateEventHandler(nil)) + registerInterfaceProvider(interactionCreateEventHandler(nil)) + registerInterfaceProvider(inviteCreateEventHandler(nil)) + registerInterfaceProvider(inviteDeleteEventHandler(nil)) registerInterfaceProvider(messageAckEventHandler(nil)) registerInterfaceProvider(messageCreateEventHandler(nil)) registerInterfaceProvider(messageDeleteEventHandler(nil)) @@ -1043,6 +1325,12 @@ func init() { registerInterfaceProvider(relationshipAddEventHandler(nil)) registerInterfaceProvider(relationshipRemoveEventHandler(nil)) registerInterfaceProvider(resumedEventHandler(nil)) + registerInterfaceProvider(threadCreateEventHandler(nil)) + registerInterfaceProvider(threadDeleteEventHandler(nil)) + registerInterfaceProvider(threadListSyncEventHandler(nil)) + registerInterfaceProvider(threadMemberUpdateEventHandler(nil)) + registerInterfaceProvider(threadMembersUpdateEventHandler(nil)) + registerInterfaceProvider(threadUpdateEventHandler(nil)) registerInterfaceProvider(typingStartEventHandler(nil)) registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil)) registerInterfaceProvider(userNoteUpdateEventHandler(nil)) diff --git a/vendor/github.com/matterbridge/discordgo/events.go b/vendor/github.com/bwmarrin/discordgo/events.go similarity index 68% rename from vendor/github.com/matterbridge/discordgo/events.go rename to vendor/github.com/bwmarrin/discordgo/events.go index 7488dcc7..cc5d7116 100644 --- a/vendor/github.com/matterbridge/discordgo/events.go +++ b/vendor/github.com/bwmarrin/discordgo/events.go @@ -73,6 +73,53 @@ type ChannelPinsUpdate struct { GuildID string `json:"guild_id,omitempty"` } +// ThreadCreate is the data for a ThreadCreate event. +type ThreadCreate struct { + *Channel + NewlyCreated bool `json:"newly_created"` +} + +// ThreadUpdate is the data for a ThreadUpdate event. +type ThreadUpdate struct { + *Channel + BeforeUpdate *Channel `json:"-"` +} + +// ThreadDelete is the data for a ThreadDelete event. +type ThreadDelete struct { + *Channel +} + +// ThreadListSync is the data for a ThreadListSync event. +type ThreadListSync struct { + // The id of the guild + GuildID string `json:"guild_id"` + // The parent channel ids whose threads are being synced. + // If omitted, then threads were synced for the entire guild. + // This array may contain channel_ids that have no active threads as well, so you know to clear that data. + ChannelIDs []string `json:"channel_ids"` + // All active threads in the given channels that the current user can access + Threads []*Channel `json:"threads"` + // All thread member objects from the synced threads for the current user, + // indicating which threads the current user has been added to + Members []*ThreadMember `json:"members"` +} + +// ThreadMemberUpdate is the data for a ThreadMemberUpdate event. +type ThreadMemberUpdate struct { + *ThreadMember + GuildID string `json:"guild_id"` +} + +// ThreadMembersUpdate is the data for a ThreadMembersUpdate event. +type ThreadMembersUpdate struct { + ID string `json:"id"` + GuildID string `json:"guild_id"` + MemberCount int `json:"member_count"` + AddedMembers []AddedThreadMember `json:"added_members"` + RemovedMembers []string `json:"removed_member_ids"` +} + // GuildCreate is the data for a GuildCreate event. type GuildCreate struct { *Guild @@ -86,6 +133,7 @@ type GuildUpdate struct { // GuildDelete is the data for a GuildDelete event. type GuildDelete struct { *Guild + BeforeDelete *Guild `json:"-"` } // GuildBanAdd is the data for a GuildBanAdd event. @@ -151,6 +199,21 @@ type GuildIntegrationsUpdate struct { GuildID string `json:"guild_id"` } +// GuildScheduledEventCreate is the data for a GuildScheduledEventCreate event. +type GuildScheduledEventCreate struct { + *GuildScheduledEvent +} + +// GuildScheduledEventUpdate is the data for a GuildScheduledEventUpdate event. +type GuildScheduledEventUpdate struct { + *GuildScheduledEvent +} + +// GuildScheduledEventDelete is the data for a GuildScheduledEventDelete event. +type GuildScheduledEventDelete struct { + *GuildScheduledEvent +} + // MessageAck is the data for a MessageAck event. type MessageAck struct { MessageID string `json:"message_id"` @@ -162,6 +225,11 @@ type MessageCreate struct { *Message } +// UnmarshalJSON is a helper function to unmarshal MessageCreate object. +func (m *MessageCreate) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &m.Message) +} + // MessageUpdate is the data for a MessageUpdate event. type MessageUpdate struct { *Message @@ -169,15 +237,26 @@ type MessageUpdate struct { BeforeUpdate *Message `json:"-"` } +// UnmarshalJSON is a helper function to unmarshal MessageUpdate object. +func (m *MessageUpdate) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &m.Message) +} + // MessageDelete is the data for a MessageDelete event. type MessageDelete struct { *Message BeforeDelete *Message `json:"-"` } +// UnmarshalJSON is a helper function to unmarshal MessageDelete object. +func (m *MessageDelete) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &m.Message) +} + // MessageReactionAdd is the data for a MessageReactionAdd event. type MessageReactionAdd struct { *MessageReaction + Member *Member `json:"member,omitempty"` } // MessageReactionRemove is the data for a MessageReactionRemove event. @@ -267,3 +346,27 @@ type WebhooksUpdate struct { GuildID string `json:"guild_id"` ChannelID string `json:"channel_id"` } + +// InteractionCreate is the data for a InteractionCreate event +type InteractionCreate struct { + *Interaction +} + +// UnmarshalJSON is a helper function to unmarshal Interaction object. +func (i *InteractionCreate) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &i.Interaction) +} + +// InviteCreate is the data for a InviteCreate event +type InviteCreate struct { + *Invite + ChannelID string `json:"channel_id"` + GuildID string `json:"guild_id"` +} + +// InviteDelete is the data for a InviteDelete event +type InviteDelete struct { + ChannelID string `json:"channel_id"` + GuildID string `json:"guild_id"` + Code string `json:"code"` +} diff --git a/vendor/github.com/bwmarrin/discordgo/interactions.go b/vendor/github.com/bwmarrin/discordgo/interactions.go new file mode 100644 index 00000000..0e5ae3c4 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/interactions.go @@ -0,0 +1,568 @@ +package discordgo + +import ( + "bytes" + "crypto/ed25519" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "time" +) + +// InteractionDeadline is the time allowed to respond to an interaction. +const InteractionDeadline = time.Second * 3 + +// ApplicationCommandType represents the type of application command. +type ApplicationCommandType uint8 + +// Application command types +const ( + // ChatApplicationCommand is default command type. They are slash commands (i.e. called directly from the chat). + ChatApplicationCommand ApplicationCommandType = 1 + // UserApplicationCommand adds command to user context menu. + UserApplicationCommand ApplicationCommandType = 2 + // MessageApplicationCommand adds command to message context menu. + MessageApplicationCommand ApplicationCommandType = 3 +) + +// ApplicationCommand represents an application's slash command. +type ApplicationCommand struct { + ID string `json:"id,omitempty"` + ApplicationID string `json:"application_id,omitempty"` + Version string `json:"version,omitempty"` + Type ApplicationCommandType `json:"type,omitempty"` + Name string `json:"name"` + DefaultPermission *bool `json:"default_permission,omitempty"` + + // NOTE: Chat commands only. Otherwise it mustn't be set. + + Description string `json:"description,omitempty"` + Options []*ApplicationCommandOption `json:"options"` +} + +// ApplicationCommandOptionType indicates the type of a slash command's option. +type ApplicationCommandOptionType uint8 + +// Application command option types. +const ( + ApplicationCommandOptionSubCommand ApplicationCommandOptionType = 1 + ApplicationCommandOptionSubCommandGroup ApplicationCommandOptionType = 2 + ApplicationCommandOptionString ApplicationCommandOptionType = 3 + ApplicationCommandOptionInteger ApplicationCommandOptionType = 4 + ApplicationCommandOptionBoolean ApplicationCommandOptionType = 5 + ApplicationCommandOptionUser ApplicationCommandOptionType = 6 + ApplicationCommandOptionChannel ApplicationCommandOptionType = 7 + ApplicationCommandOptionRole ApplicationCommandOptionType = 8 + ApplicationCommandOptionMentionable ApplicationCommandOptionType = 9 + ApplicationCommandOptionNumber ApplicationCommandOptionType = 10 + ApplicationCommandOptionAttachment ApplicationCommandOptionType = 11 +) + +func (t ApplicationCommandOptionType) String() string { + switch t { + case ApplicationCommandOptionSubCommand: + return "SubCommand" + case ApplicationCommandOptionSubCommandGroup: + return "SubCommandGroup" + case ApplicationCommandOptionString: + return "String" + case ApplicationCommandOptionInteger: + return "Integer" + case ApplicationCommandOptionBoolean: + return "Boolean" + case ApplicationCommandOptionUser: + return "User" + case ApplicationCommandOptionChannel: + return "Channel" + case ApplicationCommandOptionRole: + return "Role" + case ApplicationCommandOptionMentionable: + return "Mentionable" + case ApplicationCommandOptionNumber: + return "Number" + case ApplicationCommandOptionAttachment: + return "Attachment" + } + return fmt.Sprintf("ApplicationCommandOptionType(%d)", t) +} + +// ApplicationCommandOption represents an option/subcommand/subcommands group. +type ApplicationCommandOption struct { + Type ApplicationCommandOptionType `json:"type"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + // NOTE: This feature was on the API, but at some point developers decided to remove it. + // So I commented it, until it will be officially on the docs. + // Default bool `json:"default"` + + ChannelTypes []ChannelType `json:"channel_types"` + Required bool `json:"required"` + Options []*ApplicationCommandOption `json:"options"` + + // NOTE: mutually exclusive with Choices. + Autocomplete bool `json:"autocomplete"` + Choices []*ApplicationCommandOptionChoice `json:"choices"` + // Minimal value of number/integer option. + MinValue *float64 `json:"min_value,omitempty"` + // Maximum value of number/integer option. + MaxValue float64 `json:"max_value,omitempty"` +} + +// ApplicationCommandOptionChoice represents a slash command option choice. +type ApplicationCommandOptionChoice struct { + Name string `json:"name"` + Value interface{} `json:"value"` +} + +// ApplicationCommandPermissions represents a single user or role permission for a command. +type ApplicationCommandPermissions struct { + ID string `json:"id"` + Type ApplicationCommandPermissionType `json:"type"` + Permission bool `json:"permission"` +} + +// ApplicationCommandPermissionsList represents a list of ApplicationCommandPermissions, needed for serializing to JSON. +type ApplicationCommandPermissionsList struct { + Permissions []*ApplicationCommandPermissions `json:"permissions"` +} + +// GuildApplicationCommandPermissions represents all permissions for a single guild command. +type GuildApplicationCommandPermissions struct { + ID string `json:"id"` + ApplicationID string `json:"application_id"` + GuildID string `json:"guild_id"` + Permissions []*ApplicationCommandPermissions `json:"permissions"` +} + +// ApplicationCommandPermissionType indicates whether a permission is user or role based. +type ApplicationCommandPermissionType uint8 + +// Application command permission types. +const ( + ApplicationCommandPermissionTypeRole ApplicationCommandPermissionType = 1 + ApplicationCommandPermissionTypeUser ApplicationCommandPermissionType = 2 +) + +// InteractionType indicates the type of an interaction event. +type InteractionType uint8 + +// Interaction types +const ( + InteractionPing InteractionType = 1 + InteractionApplicationCommand InteractionType = 2 + InteractionMessageComponent InteractionType = 3 + InteractionApplicationCommandAutocomplete InteractionType = 4 + InteractionModalSubmit InteractionType = 5 +) + +func (t InteractionType) String() string { + switch t { + case InteractionPing: + return "Ping" + case InteractionApplicationCommand: + return "ApplicationCommand" + case InteractionMessageComponent: + return "MessageComponent" + case InteractionModalSubmit: + return "ModalSubmit" + } + return fmt.Sprintf("InteractionType(%d)", t) +} + +// Interaction represents data of an interaction. +type Interaction struct { + ID string `json:"id"` + Type InteractionType `json:"type"` + Data InteractionData `json:"data"` + GuildID string `json:"guild_id"` + ChannelID string `json:"channel_id"` + + // The message on which interaction was used. + // NOTE: this field is only filled when a button click triggered the interaction. Otherwise it will be nil. + Message *Message `json:"message"` + + // The member who invoked this interaction. + // NOTE: this field is only filled when the slash command was invoked in a guild; + // if it was invoked in a DM, the `User` field will be filled instead. + // Make sure to check for `nil` before using this field. + Member *Member `json:"member"` + // The user who invoked this interaction. + // NOTE: this field is only filled when the slash command was invoked in a DM; + // if it was invoked in a guild, the `Member` field will be filled instead. + // Make sure to check for `nil` before using this field. + User *User `json:"user"` + + // The user's discord client locale. + Locale Locale `json:"locale"` + // The guild's locale. This defaults to EnglishUS + // NOTE: this field is only filled when the interaction was invoked in a guild. + GuildLocale *Locale `json:"guild_locale"` + + Token string `json:"token"` + Version int `json:"version"` +} + +type interaction Interaction + +type rawInteraction struct { + interaction + Data json.RawMessage `json:"data"` +} + +// UnmarshalJSON is a method for unmarshalling JSON object to Interaction. +func (i *Interaction) UnmarshalJSON(raw []byte) error { + var tmp rawInteraction + err := json.Unmarshal(raw, &tmp) + if err != nil { + return err + } + + *i = Interaction(tmp.interaction) + + switch tmp.Type { + case InteractionApplicationCommand, InteractionApplicationCommandAutocomplete: + v := ApplicationCommandInteractionData{} + err = json.Unmarshal(tmp.Data, &v) + if err != nil { + return err + } + i.Data = v + case InteractionMessageComponent: + v := MessageComponentInteractionData{} + err = json.Unmarshal(tmp.Data, &v) + if err != nil { + return err + } + i.Data = v + case InteractionModalSubmit: + v := ModalSubmitInteractionData{} + err = json.Unmarshal(tmp.Data, &v) + if err != nil { + return err + } + i.Data = v + } + return nil +} + +// MessageComponentData is helper function to assert the inner InteractionData to MessageComponentInteractionData. +// Make sure to check that the Type of the interaction is InteractionMessageComponent before calling. +func (i Interaction) MessageComponentData() (data MessageComponentInteractionData) { + if i.Type != InteractionMessageComponent { + panic("MessageComponentData called on interaction of type " + i.Type.String()) + } + return i.Data.(MessageComponentInteractionData) +} + +// ApplicationCommandData is helper function to assert the inner InteractionData to ApplicationCommandInteractionData. +// Make sure to check that the Type of the interaction is InteractionApplicationCommand before calling. +func (i Interaction) ApplicationCommandData() (data ApplicationCommandInteractionData) { + if i.Type != InteractionApplicationCommand && i.Type != InteractionApplicationCommandAutocomplete { + panic("ApplicationCommandData called on interaction of type " + i.Type.String()) + } + return i.Data.(ApplicationCommandInteractionData) +} + +// ModalSubmitData is helper function to assert the inner InteractionData to ModalSubmitInteractionData. +// Make sure to check that the Type of the interaction is InteractionModalSubmit before calling. +func (i Interaction) ModalSubmitData() (data ModalSubmitInteractionData) { + if i.Type != InteractionModalSubmit { + panic("ModalSubmitData called on interaction of type " + i.Type.String()) + } + return i.Data.(ModalSubmitInteractionData) +} + +// InteractionData is a common interface for all types of interaction data. +type InteractionData interface { + Type() InteractionType +} + +// ApplicationCommandInteractionData contains the data of application command interaction. +type ApplicationCommandInteractionData struct { + ID string `json:"id"` + Name string `json:"name"` + Resolved *ApplicationCommandInteractionDataResolved `json:"resolved"` + + // Slash command options + Options []*ApplicationCommandInteractionDataOption `json:"options"` + // Target (user/message) id on which context menu command was called. + // The details are stored in Resolved according to command type. + TargetID string `json:"target_id"` +} + +// ApplicationCommandInteractionDataResolved contains resolved data of command execution. +// Partial Member objects are missing user, deaf and mute fields. +// Partial Channel objects only have id, name, type and permissions fields. +type ApplicationCommandInteractionDataResolved struct { + Users map[string]*User `json:"users"` + Members map[string]*Member `json:"members"` + Roles map[string]*Role `json:"roles"` + Channels map[string]*Channel `json:"channels"` + Messages map[string]*Message `json:"messages"` + Attachments map[string]*MessageAttachment `json:"attachments"` +} + +// Type returns the type of interaction data. +func (ApplicationCommandInteractionData) Type() InteractionType { + return InteractionApplicationCommand +} + +// MessageComponentInteractionData contains the data of message component interaction. +type MessageComponentInteractionData struct { + CustomID string `json:"custom_id"` + ComponentType ComponentType `json:"component_type"` + + // NOTE: Only filled when ComponentType is SelectMenuComponent (3). Otherwise is nil. + Values []string `json:"values"` +} + +// Type returns the type of interaction data. +func (MessageComponentInteractionData) Type() InteractionType { + return InteractionMessageComponent +} + +// ModalSubmitInteractionData contains the data of modal submit interaction. +type ModalSubmitInteractionData struct { + CustomID string `json:"custom_id"` + Components []MessageComponent `json:"-"` +} + +// Type returns the type of interaction data. +func (ModalSubmitInteractionData) Type() InteractionType { + return InteractionModalSubmit +} + +// UnmarshalJSON is a helper function to correctly unmarshal Components. +func (d *ModalSubmitInteractionData) UnmarshalJSON(data []byte) error { + type modalSubmitInteractionData ModalSubmitInteractionData + var v struct { + modalSubmitInteractionData + RawComponents []unmarshalableMessageComponent `json:"components"` + } + err := json.Unmarshal(data, &v) + if err != nil { + return err + } + *d = ModalSubmitInteractionData(v.modalSubmitInteractionData) + d.Components = make([]MessageComponent, len(v.RawComponents)) + for i, v := range v.RawComponents { + d.Components[i] = v.MessageComponent + } + return err +} + +// ApplicationCommandInteractionDataOption represents an option of a slash command. +type ApplicationCommandInteractionDataOption struct { + Name string `json:"name"` + Type ApplicationCommandOptionType `json:"type"` + // NOTE: Contains the value specified by Type. + Value interface{} `json:"value,omitempty"` + Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"` + + // NOTE: autocomplete interaction only. + Focused bool `json:"focused,omitempty"` +} + +// IntValue is a utility function for casting option value to integer +func (o ApplicationCommandInteractionDataOption) IntValue() int64 { + if o.Type != ApplicationCommandOptionInteger { + panic("IntValue called on data option of type " + o.Type.String()) + } + return int64(o.Value.(float64)) +} + +// UintValue is a utility function for casting option value to unsigned integer +func (o ApplicationCommandInteractionDataOption) UintValue() uint64 { + if o.Type != ApplicationCommandOptionInteger { + panic("UintValue called on data option of type " + o.Type.String()) + } + return uint64(o.Value.(float64)) +} + +// FloatValue is a utility function for casting option value to float +func (o ApplicationCommandInteractionDataOption) FloatValue() float64 { + if o.Type != ApplicationCommandOptionNumber { + panic("FloatValue called on data option of type " + o.Type.String()) + } + return o.Value.(float64) +} + +// StringValue is a utility function for casting option value to string +func (o ApplicationCommandInteractionDataOption) StringValue() string { + if o.Type != ApplicationCommandOptionString { + panic("StringValue called on data option of type " + o.Type.String()) + } + return o.Value.(string) +} + +// BoolValue is a utility function for casting option value to bool +func (o ApplicationCommandInteractionDataOption) BoolValue() bool { + if o.Type != ApplicationCommandOptionBoolean { + panic("BoolValue called on data option of type " + o.Type.String()) + } + return o.Value.(bool) +} + +// ChannelValue is a utility function for casting option value to channel object. +// s : Session object, if not nil, function additionally fetches all channel's data +func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) *Channel { + if o.Type != ApplicationCommandOptionChannel { + panic("ChannelValue called on data option of type " + o.Type.String()) + } + chanID := o.Value.(string) + + if s == nil { + return &Channel{ID: chanID} + } + + ch, err := s.State.Channel(chanID) + if err != nil { + ch, err = s.Channel(chanID) + if err != nil { + return &Channel{ID: chanID} + } + } + + return ch +} + +// RoleValue is a utility function for casting option value to role object. +// s : Session object, if not nil, function additionally fetches all role's data +func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID string) *Role { + if o.Type != ApplicationCommandOptionRole && o.Type != ApplicationCommandOptionMentionable { + panic("RoleValue called on data option of type " + o.Type.String()) + } + roleID := o.Value.(string) + + if s == nil || gID == "" { + return &Role{ID: roleID} + } + + r, err := s.State.Role(roleID, gID) + if err != nil { + roles, err := s.GuildRoles(gID) + if err == nil { + for _, r = range roles { + if r.ID == roleID { + return r + } + } + } + return &Role{ID: roleID} + } + + return r +} + +// UserValue is a utility function for casting option value to user object. +// s : Session object, if not nil, function additionally fetches all user's data +func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) *User { + if o.Type != ApplicationCommandOptionUser && o.Type != ApplicationCommandOptionMentionable { + panic("UserValue called on data option of type " + o.Type.String()) + } + userID := o.Value.(string) + + if s == nil { + return &User{ID: userID} + } + + u, err := s.User(userID) + if err != nil { + return &User{ID: userID} + } + + return u +} + +// InteractionResponseType is type of interaction response. +type InteractionResponseType uint8 + +// Interaction response types. +const ( + // InteractionResponsePong is for ACK ping event. + InteractionResponsePong InteractionResponseType = 1 + // InteractionResponseChannelMessageWithSource is for responding with a message, showing the user's input. + InteractionResponseChannelMessageWithSource InteractionResponseType = 4 + // InteractionResponseDeferredChannelMessageWithSource acknowledges that the event was received, and that a follow-up will come later. + InteractionResponseDeferredChannelMessageWithSource InteractionResponseType = 5 + // InteractionResponseDeferredMessageUpdate acknowledges that the message component interaction event was received, and message will be updated later. + InteractionResponseDeferredMessageUpdate InteractionResponseType = 6 + // InteractionResponseUpdateMessage is for updating the message to which message component was attached. + InteractionResponseUpdateMessage InteractionResponseType = 7 + // InteractionApplicationCommandAutocompleteResult shows autocompletion results. Autocomplete interaction only. + InteractionApplicationCommandAutocompleteResult InteractionResponseType = 8 + // InteractionResponseModal is for responding to an interaction with a modal window. + InteractionResponseModal InteractionResponseType = 9 +) + +// InteractionResponse represents a response for an interaction event. +type InteractionResponse struct { + Type InteractionResponseType `json:"type,omitempty"` + Data *InteractionResponseData `json:"data,omitempty"` +} + +// InteractionResponseData is response data for an interaction. +type InteractionResponseData struct { + TTS bool `json:"tts"` + Content string `json:"content"` + Components []MessageComponent `json:"components"` + Embeds []*MessageEmbed `json:"embeds,omitempty"` + AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` + Flags uint64 `json:"flags,omitempty"` + Files []*File `json:"-"` + + // NOTE: autocomplete interaction only. + Choices []*ApplicationCommandOptionChoice `json:"choices,omitempty"` + + // NOTE: modal interaction only. + + CustomID string `json:"custom_id,omitempty"` + Title string `json:"title,omitempty"` +} + +// VerifyInteraction implements message verification of the discord interactions api +// signing algorithm, as documented here: +// https://discord.com/developers/docs/interactions/receiving-and-responding#security-and-authorization +func VerifyInteraction(r *http.Request, key ed25519.PublicKey) bool { + var msg bytes.Buffer + + signature := r.Header.Get("X-Signature-Ed25519") + if signature == "" { + return false + } + + sig, err := hex.DecodeString(signature) + if err != nil { + return false + } + + if len(sig) != ed25519.SignatureSize { + return false + } + + timestamp := r.Header.Get("X-Signature-Timestamp") + if timestamp == "" { + return false + } + + msg.WriteString(timestamp) + + defer r.Body.Close() + var body bytes.Buffer + + // at the end of the function, copy the original body back into the request + defer func() { + r.Body = ioutil.NopCloser(&body) + }() + + // copy body into buffers + _, err = io.Copy(&msg, io.TeeReader(r.Body, &body)) + if err != nil { + return false + } + + return ed25519.Verify(key, msg.Bytes(), sig) +} diff --git a/vendor/github.com/bwmarrin/discordgo/locales.go b/vendor/github.com/bwmarrin/discordgo/locales.go new file mode 100644 index 00000000..a6a1c521 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/locales.go @@ -0,0 +1,83 @@ +package discordgo + +// Locale represents the accepted languages for Discord. +// https://discord.com/developers/docs/reference#locales +type Locale string + +// String returns the human-readable string of the locale +func (l Locale) String() string { + if name, ok := Locales[l]; ok { + return name + } + return Unknown.String() +} + +// All defined locales in Discord +const ( + EnglishUS Locale = "en-US" + EnglishGB Locale = "en-GB" + Bulgarian Locale = "bg" + ChineseCN Locale = "zh-CN" + ChineseTW Locale = "zh-TW" + Croatian Locale = "hr" + Czech Locale = "cs" + Danish Locale = "da" + Dutch Locale = "nl" + Finnish Locale = "fi" + French Locale = "fr" + German Locale = "de" + Greek Locale = "el" + Hindi Locale = "hi" + Hungarian Locale = "hu" + Italian Locale = "it" + Japanese Locale = "ja" + Korean Locale = "ko" + Lithuanian Locale = "lt" + Norwegian Locale = "no" + Polish Locale = "pl" + PortugueseBR Locale = "pt-BR" + Romanian Locale = "ro" + Russian Locale = "ru" + SpanishES Locale = "es-ES" + Swedish Locale = "sv-SE" + Thai Locale = "th" + Turkish Locale = "tr" + Ukrainian Locale = "uk" + Vietnamese Locale = "vi" + Unknown Locale = "" +) + +// Locales is a map of all the languages codes to their names. +var Locales = map[Locale]string{ + EnglishUS: "English (United States)", + EnglishGB: "English (Great Britain)", + Bulgarian: "Bulgarian", + ChineseCN: "Chinese (China)", + ChineseTW: "Chinese (Taiwan)", + Croatian: "Croatian", + Czech: "Czech", + Danish: "Danish", + Dutch: "Dutch", + Finnish: "Finnish", + French: "French", + German: "German", + Greek: "Greek", + Hindi: "Hindi", + Hungarian: "Hungarian", + Italian: "Italian", + Japanese: "Japanese", + Korean: "Korean", + Lithuanian: "Lithuanian", + Norwegian: "Norwegian", + Polish: "Polish", + PortugueseBR: "Portuguese (Brazil)", + Romanian: "Romanian", + Russian: "Russian", + SpanishES: "Spanish (Spain)", + Swedish: "Swedish", + Thai: "Thai", + Turkish: "Turkish", + Ukrainian: "Ukrainian", + Vietnamese: "Vietnamese", + Unknown: "unknown", +} diff --git a/vendor/github.com/matterbridge/discordgo/logging.go b/vendor/github.com/bwmarrin/discordgo/logging.go similarity index 100% rename from vendor/github.com/matterbridge/discordgo/logging.go rename to vendor/github.com/bwmarrin/discordgo/logging.go diff --git a/vendor/github.com/matterbridge/discordgo/message.go b/vendor/github.com/bwmarrin/discordgo/message.go similarity index 65% rename from vendor/github.com/matterbridge/discordgo/message.go rename to vendor/github.com/bwmarrin/discordgo/message.go index 61cd0d9c..eb2f4962 100644 --- a/vendor/github.com/matterbridge/discordgo/message.go +++ b/vendor/github.com/bwmarrin/discordgo/message.go @@ -10,9 +10,11 @@ package discordgo import ( + "encoding/json" "io" "regexp" "strings" + "time" ) // MessageType is the type of Message @@ -21,23 +23,26 @@ type MessageType int // Block contains the valid known MessageType values const ( - MessageTypeDefault MessageType = iota - MessageTypeRecipientAdd - MessageTypeRecipientRemove - MessageTypeCall - MessageTypeChannelNameChange - MessageTypeChannelIconChange - MessageTypeChannelPinnedMessage - MessageTypeGuildMemberJoin - MessageTypeUserPremiumGuildSubscription - MessageTypeUserPremiumGuildSubscriptionTierOne - MessageTypeUserPremiumGuildSubscriptionTierTwo - MessageTypeUserPremiumGuildSubscriptionTierThree - MessageTypeChannelFollowAdd - MessageTypeGuildDiscoveryDisqualified = iota + 1 - MessageTypeGuildDiscoveryRequalified - MessageTypeReply = iota + 4 - MessageTypeApplicationCommand + MessageTypeDefault MessageType = 0 + MessageTypeRecipientAdd MessageType = 1 + MessageTypeRecipientRemove MessageType = 2 + MessageTypeCall MessageType = 3 + MessageTypeChannelNameChange MessageType = 4 + MessageTypeChannelIconChange MessageType = 5 + MessageTypeChannelPinnedMessage MessageType = 6 + MessageTypeGuildMemberJoin MessageType = 7 + MessageTypeUserPremiumGuildSubscription MessageType = 8 + MessageTypeUserPremiumGuildSubscriptionTierOne MessageType = 9 + MessageTypeUserPremiumGuildSubscriptionTierTwo MessageType = 10 + MessageTypeUserPremiumGuildSubscriptionTierThree MessageType = 11 + MessageTypeChannelFollowAdd MessageType = 12 + MessageTypeGuildDiscoveryDisqualified MessageType = 14 + MessageTypeGuildDiscoveryRequalified MessageType = 15 + MessageTypeThreadCreated MessageType = 18 + MessageTypeReply MessageType = 19 + MessageTypeChatInputCommand MessageType = 20 + MessageTypeThreadStarterMessage MessageType = 21 + MessageTypeContextMenuCommand MessageType = 23 ) // A Message stores all data related to a specific Discord message. @@ -58,11 +63,11 @@ type Message struct { // CAUTION: this field may be removed in a // future API version; it is safer to calculate // the creation time via the ID. - Timestamp Timestamp `json:"timestamp"` + Timestamp time.Time `json:"timestamp"` // The time at which the last edit of the message // occurred, if it has been edited. - EditedTimestamp Timestamp `json:"edited_timestamp"` + EditedTimestamp *time.Time `json:"edited_timestamp"` // The roles mentioned in the message. MentionRoles []string `json:"mention_roles"` @@ -80,8 +85,10 @@ type Message struct { // A list of attachments present in the message. Attachments []*MessageAttachment `json:"attachments"` - // A list of embeds present in the message. Multiple - // embeds can currently only be sent by webhooks. + // A list of components attached to the message. + Components []MessageComponent `json:"-"` + + // A list of embeds present in the message. Embeds []*MessageEmbed `json:"embeds"` // A list of users mentioned in the message. @@ -116,13 +123,70 @@ type Message struct { // Is sent with Rich Presence-related chat embeds Application *MessageApplication `json:"application"` - // MessageReference contains reference data sent with crossposted messages + // MessageReference contains reference data sent with crossposted or reply messages. + // This does not contain the reference *to* this message; this is for when *this* message references another. + // To generate a reference to this message, use (*Message).Reference(). MessageReference *MessageReference `json:"message_reference"` + // The message associated with the message_reference + // NOTE: This field is only returned for messages with a type of 19 (REPLY) or 21 (THREAD_STARTER_MESSAGE). + // If the message is a reply but the referenced_message field is not present, + // the backend did not attempt to fetch the message that was being replied to, so its state is unknown. + // If the field exists but is null, the referenced message was deleted. + ReferencedMessage *Message `json:"referenced_message"` + + // Is sent when the message is a response to an Interaction, without an existing message. + // This means responses to message component interactions do not include this property, + // instead including a MessageReference, as components exist on preexisting messages. + Interaction *MessageInteraction `json:"interaction"` + // The flags of the message, which describe extra features of a message. // This is a combination of bit masks; the presence of a certain permission can // be checked by performing a bitwise AND between this int and the flag. Flags MessageFlags `json:"flags"` + + // The thread that was started from this message, includes thread member object + Thread *Channel `json:"thread,omitempty"` + + // An array of Sticker objects, if any were sent. + StickerItems []*Sticker `json:"sticker_items"` +} + +// UnmarshalJSON is a helper function to unmarshal the Message. +func (m *Message) UnmarshalJSON(data []byte) error { + type message Message + var v struct { + message + RawComponents []unmarshalableMessageComponent `json:"components"` + } + err := json.Unmarshal(data, &v) + if err != nil { + return err + } + *m = Message(v.message) + m.Components = make([]MessageComponent, len(v.RawComponents)) + for i, v := range v.RawComponents { + m.Components[i] = v.MessageComponent + } + return err +} + +// GetCustomEmojis pulls out all the custom (Non-unicode) emojis from a message and returns a Slice of the Emoji struct. +func (m *Message) GetCustomEmojis() []*Emoji { + var toReturn []*Emoji + emojis := EmojiRegex.FindAllString(m.Content, -1) + if len(emojis) < 1 { + return toReturn + } + for _, em := range emojis { + parts := strings.Split(em, ":") + toReturn = append(toReturn, &Emoji{ + ID: parts[2][:len(parts[2])-1], + Name: parts[1], + Animated: strings.HasPrefix(em, " 0xFFFFFF { @@ -1057,11 +935,11 @@ func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist b } data := struct { - Name string `json:"name"` // The role's name (overwrites existing) - Color int `json:"color"` // The color the role should have (as a decimal, not hex) - Hoist bool `json:"hoist"` // Whether to display the role's users separately - Permissions int `json:"permissions"` // The overall permissions number of the role (overwrites existing) - Mentionable bool `json:"mentionable"` // Whether this role is mentionable + Name string `json:"name"` // The role's name (overwrites existing) + Color int `json:"color"` // The color the role should have (as a decimal, not hex) + Hoist bool `json:"hoist"` // Whether to display the role's users separately + Permissions int64 `json:"permissions,string"` // The overall permissions number of the role (overwrites existing) + Mentionable bool `json:"mentionable"` // Whether this role is mentionable }{name, color, hoist, perm, mention} body, err := s.RequestWithBucketID("PATCH", EndpointGuildRole(guildID, roleID), data, EndpointGuildRole(guildID, "")) @@ -1224,15 +1102,6 @@ func (s *Session) GuildIntegrationDelete(guildID, integrationID string) (err err return } -// GuildIntegrationSync syncs an integration. -// guildID : The ID of a Guild. -// integrationID : The ID of an integration. -func (s *Session) GuildIntegrationSync(guildID, integrationID string) (err error) { - - _, err = s.RequestWithBucketID("POST", EndpointGuildIntegrationSync(guildID, integrationID), nil, EndpointGuildIntegration(guildID, "")) - return -} - // GuildIcon returns an image.Image of a guild icon. // guildID : The ID of a Guild. func (s *Session) GuildIcon(guildID string) (img image.Image, err error) { @@ -1401,6 +1270,111 @@ func (s *Session) GuildEmojiDelete(guildID, emojiID string) (err error) { return } +// GuildTemplate returns a GuildTemplate for the given code +// templateCode: The Code of a GuildTemplate +func (s *Session) GuildTemplate(templateCode string) (st *GuildTemplate, err error) { + + body, err := s.RequestWithBucketID("GET", EndpointGuildTemplate(templateCode), nil, EndpointGuildTemplate(templateCode)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildCreateWithTemplate creates a guild based on a GuildTemplate +// templateCode: The Code of a GuildTemplate +// name: The name of the guild (2-100) characters +// icon: base64 encoded 128x128 image for the guild icon +func (s *Session) GuildCreateWithTemplate(templateCode, name, icon string) (st *Guild, err error) { + + data := struct { + Name string `json:"name"` + Icon string `json:"icon"` + }{name, icon} + + body, err := s.RequestWithBucketID("POST", EndpointGuildTemplate(templateCode), data, EndpointGuildTemplate(templateCode)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildTemplates returns all of GuildTemplates +// guildID: The ID of the guild +func (s *Session) GuildTemplates(guildID string) (st []*GuildTemplate, err error) { + + body, err := s.RequestWithBucketID("GET", EndpointGuildTemplates(guildID), nil, EndpointGuildTemplates(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildTemplateCreate creates a template for the guild +// guildID: The ID of the guild +// name: The name of the template (1-100 characters) +// description: The description for the template (0-120 characters) +func (s *Session) GuildTemplateCreate(guildID, name, description string) (st *GuildTemplate) { + + data := struct { + Name string `json:"name"` + Description string `json:"description"` + }{name, description} + + body, err := s.RequestWithBucketID("POST", EndpointGuildTemplates(guildID), data, EndpointGuildTemplates(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildTemplateSync syncs the template to the guild's current state +// guildID: The ID of the guild +// templateCode: The code of the template +func (s *Session) GuildTemplateSync(guildID, templateCode string) (err error) { + + _, err = s.RequestWithBucketID("PUT", EndpointGuildTemplateSync(guildID, templateCode), nil, EndpointGuildTemplateSync(guildID, "")) + return +} + +// GuildTemplateEdit modifies the template's metadata +// guildID: The ID of the guild +// templateCode: The code of the template +// name: The name of the template (1-100 characters) +// description: The description for the template (0-120 characters) +func (s *Session) GuildTemplateEdit(guildID, templateCode, name, description string) (st *GuildTemplate, err error) { + + data := struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + }{name, description} + + body, err := s.RequestWithBucketID("PATCH", EndpointGuildTemplateSync(guildID, templateCode), data, EndpointGuildTemplateSync(guildID, "")) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildTemplateDelete deletes the template +// guildID: The ID of the guild +// templateCode: The code of the template +func (s *Session) GuildTemplateDelete(guildID, templateCode string) (err error) { + + _, err = s.RequestWithBucketID("DELETE", EndpointGuildTemplateSync(guildID, templateCode), nil, EndpointGuildTemplateSync(guildID, "")) + return +} + // ------------------------------------------------------------------------------------------------ // Functions specific to Discord Channels // ------------------------------------------------------------------------------------------------ @@ -1512,21 +1486,6 @@ func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err return } -// ChannelMessageAck acknowledges and marks the given message as read -// channeld : The ID of a Channel -// messageID : the ID of a Message -// lastToken : token returned by last ack -func (s *Session) ChannelMessageAck(channelID, messageID, lastToken string) (st *Ack, err error) { - - body, err := s.RequestWithBucketID("POST", EndpointChannelMessageAck(channelID, messageID), &Ack{Token: lastToken}, EndpointChannelMessageAck(channelID, "")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - // ChannelMessageSend sends a message to the given channel. // channelID : The ID of a Channel. // content : The message to send. @@ -1542,10 +1501,21 @@ var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") // channelID : The ID of a Channel. // data : The message struct to send. func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend) (st *Message, err error) { - if data.Embed != nil && data.Embed.Type == "" { - data.Embed.Type = "rich" + // TODO: Remove this when compatibility is not required. + if data.Embed != nil { + if data.Embeds == nil { + data.Embeds = []*MessageEmbed{data.Embed} + } else { + err = fmt.Errorf("cannot specify both Embed and Embeds") + return + } } + for _, embed := range data.Embeds { + if embed.Type == "" { + embed.Type = "rich" + } + } endpoint := EndpointChannelMessages(channelID) // TODO: Remove this when compatibility is not required. @@ -1561,55 +1531,12 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend) var response []byte if len(files) > 0 { - body := &bytes.Buffer{} - bodywriter := multipart.NewWriter(body) - - var payload []byte - payload, err = json.Marshal(data) - if err != nil { - return + contentType, body, encodeErr := MultipartBodyWithJSON(data, files) + if encodeErr != nil { + return st, encodeErr } - var p io.Writer - - h := make(textproto.MIMEHeader) - h.Set("Content-Disposition", `form-data; name="payload_json"`) - h.Set("Content-Type", "application/json") - - p, err = bodywriter.CreatePart(h) - if err != nil { - return - } - - if _, err = p.Write(payload); err != nil { - return - } - - for i, file := range files { - h := make(textproto.MIMEHeader) - h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file%d"; filename="%s"`, i, quoteEscaper.Replace(file.Name))) - contentType := file.ContentType - if contentType == "" { - contentType = "application/octet-stream" - } - h.Set("Content-Type", contentType) - - p, err = bodywriter.CreatePart(h) - if err != nil { - return - } - - if _, err = io.Copy(p, file.Reader); err != nil { - return - } - } - - err = bodywriter.Close() - if err != nil { - return - } - - response, err = s.request("POST", endpoint, bodywriter.FormDataContentType(), body.Bytes(), endpoint, 0) + response, err = s.request("POST", endpoint, contentType, body, endpoint, 0) } else { response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint) } @@ -1635,8 +1562,15 @@ func (s *Session) ChannelMessageSendTTS(channelID string, content string) (*Mess // channelID : The ID of a Channel. // embed : The embed data to send. func (s *Session) ChannelMessageSendEmbed(channelID string, embed *MessageEmbed) (*Message, error) { + return s.ChannelMessageSendEmbeds(channelID, []*MessageEmbed{embed}) +} + +// ChannelMessageSendEmbeds sends a message to the given channel with multiple embedded data. +// channelID : The ID of a Channel. +// embeds : The embeds data to send. +func (s *Session) ChannelMessageSendEmbeds(channelID string, embeds []*MessageEmbed) (*Message, error) { return s.ChannelMessageSendComplex(channelID, &MessageSend{ - Embed: embed, + Embeds: embeds, }) } @@ -1645,6 +1579,9 @@ func (s *Session) ChannelMessageSendEmbed(channelID string, embed *MessageEmbed) // content : The message to send. // reference : The message reference to send. func (s *Session) ChannelMessageSendReply(channelID string, content string, reference *MessageReference) (*Message, error) { + if reference == nil { + return nil, fmt.Errorf("reply attempted with nil message reference") + } return s.ChannelMessageSendComplex(channelID, &MessageSend{ Content: content, Reference: reference, @@ -1663,10 +1600,21 @@ func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (*Mes // ChannelMessageEditComplex edits an existing message, replacing it entirely with // the given MessageEdit struct func (s *Session) ChannelMessageEditComplex(m *MessageEdit) (st *Message, err error) { - if m.Embed != nil && m.Embed.Type == "" { - m.Embed.Type = "rich" + // TODO: Remove this when compatibility is not required. + if m.Embed != nil { + if m.Embeds == nil { + m.Embeds = []*MessageEmbed{m.Embed} + } else { + err = fmt.Errorf("cannot specify both Embed and Embeds") + return + } } + for _, embed := range m.Embeds { + if embed.Type == "" { + embed.Type = "rich" + } + } response, err := s.RequestWithBucketID("PATCH", EndpointChannelMessage(m.Channel, m.ID), m, EndpointChannelMessage(m.Channel, "")) if err != nil { return @@ -1681,7 +1629,15 @@ func (s *Session) ChannelMessageEditComplex(m *MessageEdit) (st *Message, err er // messageID : The ID of a Message // embed : The embed data to send func (s *Session) ChannelMessageEditEmbed(channelID, messageID string, embed *MessageEmbed) (*Message, error) { - return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetEmbed(embed)) + return s.ChannelMessageEditEmbeds(channelID, messageID, []*MessageEmbed{embed}) +} + +// ChannelMessageEditEmbeds edits an existing message with multiple embedded data. +// channelID : The ID of a Channel +// messageID : The ID of a Message +// embeds : The embeds data to send +func (s *Session) ChannelMessageEditEmbeds(channelID, messageID string, embeds []*MessageEmbed) (*Message, error) { + return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetEmbeds(embeds)) } // ChannelMessageDelete deletes a message from the Channel. @@ -1807,13 +1763,13 @@ func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, e // ChannelPermissionSet creates a Permission Override for the given channel. // NOTE: This func name may changed. Using Set instead of Create because // you can both create a new override or update an override with this function. -func (s *Session) ChannelPermissionSet(channelID, targetID string, targetType PermissionOverwriteType, allow, deny int) (err error) { +func (s *Session) ChannelPermissionSet(channelID, targetID string, targetType PermissionOverwriteType, allow, deny int64) (err error) { data := struct { ID string `json:"id"` Type PermissionOverwriteType `json:"type"` - Allow int `json:"allow"` - Deny int `json:"deny"` + Allow int64 `json:"allow,string"` + Deny int64 `json:"deny,string"` }{targetID, targetType, allow, deny} _, err = s.RequestWithBucketID("PUT", EndpointChannelPermission(channelID, targetID), data, EndpointChannelPermission(channelID, "")) @@ -1937,18 +1893,6 @@ func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) { return } -// VoiceICE returns the voice server ICE information -func (s *Session) VoiceICE() (st *VoiceICE, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointVoiceIce, nil, EndpointVoiceIce) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - // ------------------------------------------------------------------------------------------------ // Functions specific to Discord Websockets // ------------------------------------------------------------------------------------------------ @@ -2151,79 +2095,33 @@ func (s *Session) WebhookDeleteWithToken(webhookID, token string) (st *Webhook, return } -// WebhookExecute executes a webhook. -// webhookID: The ID of a webhook. -// token : The auth token for the webhook -// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) -// -// If `wait` is `false`, the returned *Message is always empty, because server -// does not provide the response data. -func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *WebhookParams) (st *Message, err error) { +func (s *Session) webhookExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams) (st *Message, err error) { uri := EndpointWebhookToken(webhookID, token) + v := url.Values{} if wait { - uri += "?wait=true" + v.Set("wait", "true") + } + + if threadID != "" { + v.Set("thread_id", threadID) + } + if len(v) != 0 { + uri += "?" + v.Encode() } var response []byte - if data.File != nil { - body := &bytes.Buffer{} - bodywriter := multipart.NewWriter(body) - - var payload []byte - payload, err = json.Marshal(data) - if err != nil { - return + if len(data.Files) > 0 { + contentType, body, encodeErr := MultipartBodyWithJSON(data, data.Files) + if encodeErr != nil { + return st, encodeErr } - var p io.Writer - - h := make(textproto.MIMEHeader) - h.Set("Content-Disposition", `form-data; name="payload_json"`) - h.Set("Content-Type", "application/json") - - p, err = bodywriter.CreatePart(h) - if err != nil { - return - } - - if _, err = p.Write(payload); err != nil { - return - } - - { - file := data.File - h := make(textproto.MIMEHeader) - h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, quoteEscaper.Replace(file.Name))) - contentType := file.ContentType - if contentType == "" { - contentType = "application/octet-stream" - } - h.Set("Content-Type", contentType) - - p, err = bodywriter.CreatePart(h) - if err != nil { - return - } - - if _, err = io.Copy(p, file.Reader); err != nil { - return - } - } - - err = bodywriter.Close() - if err != nil { - return - } - - response, err = s.request("POST", uri, bodywriter.FormDataContentType(), body.Bytes(), EndpointWebhookToken("", ""), 0) + response, err = s.request("POST", uri, contentType, body, uri, 0) } else { - response, err = s.RequestWithBucketID("POST", uri, data, EndpointWebhookToken("", "")) + response, err = s.RequestWithBucketID("POST", uri, data, uri) } - if err != nil { - return - } - if !wait { + if !wait || err != nil { return } @@ -2231,6 +2129,81 @@ func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *Webho return } +// WebhookExecute executes a webhook. +// webhookID: The ID of a webhook. +// token : The auth token for the webhook +// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) +func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *WebhookParams) (st *Message, err error) { + return s.webhookExecute(webhookID, token, wait, "", data) +} + +// WebhookThreadExecute executes a webhook in a thread. +// webhookID: The ID of a webhook. +// token : The auth token for the webhook +// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) +// threadID : Sends a message to the specified thread within a webhook's channel. The thread will automatically be unarchived. +func (s *Session) WebhookThreadExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams) (st *Message, err error) { + return s.webhookExecute(webhookID, token, wait, threadID, data) +} + +// WebhookMessage gets a webhook message. +// webhookID : The ID of a webhook +// token : The auth token for the webhook +// messageID : The ID of message to get +func (s *Session) WebhookMessage(webhookID, token, messageID string) (message *Message, err error) { + uri := EndpointWebhookMessage(webhookID, token, messageID) + + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointWebhookToken("", "")) + if err != nil { + return + } + + err = json.Unmarshal(body, &message) + + return +} + +// WebhookMessageEdit edits a webhook message and returns a new one. +// webhookID : The ID of a webhook +// token : The auth token for the webhook +// messageID : The ID of message to edit +func (s *Session) WebhookMessageEdit(webhookID, token, messageID string, data *WebhookEdit) (st *Message, err error) { + uri := EndpointWebhookMessage(webhookID, token, messageID) + + var response []byte + if len(data.Files) > 0 { + contentType, body, err := MultipartBodyWithJSON(data, data.Files) + if err != nil { + return nil, err + } + + response, err = s.request("PATCH", uri, contentType, body, uri, 0) + if err != nil { + return nil, err + } + } else { + response, err = s.RequestWithBucketID("PATCH", uri, data, EndpointWebhookToken("", "")) + + if err != nil { + return nil, err + } + } + + err = unmarshal(response, &st) + return +} + +// WebhookMessageDelete deletes a webhook message. +// webhookID : The ID of a webhook +// token : The auth token for the webhook +// messageID : The ID of a message to edit +func (s *Session) WebhookMessageDelete(webhookID, token, messageID string) (err error) { + uri := EndpointWebhookMessage(webhookID, token, messageID) + + _, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointWebhookToken("", "")) + return +} + // MessageReactionAdd creates an emoji reaction to a message. // channelID : The channel ID. // messageID : The message ID. @@ -2268,6 +2241,19 @@ func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error { return err } +// MessageReactionsRemoveEmoji deletes all reactions of a certain emoji from a message +// channelID : The channel ID +// messageID : The message ID +// emojiID : The emoji ID +func (s *Session) MessageReactionsRemoveEmoji(channelID, messageID, emojiID string) error { + + // emoji such as #⃣ need to have # escaped + emojiID = strings.Replace(emojiID, "#", "%23", -1) + _, err := s.RequestWithBucketID("DELETE", EndpointMessageReactions(channelID, messageID, emojiID), nil, EndpointMessageReactions(channelID, messageID, emojiID)) + + return err +} + // MessageReactions gets all the users reactions for a specific emoji. // channelID : The channel ID. // messageID : The message ID. @@ -2307,81 +2293,579 @@ func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit i } // ------------------------------------------------------------------------------------------------ -// Functions specific to user notes +// Functions specific to threads // ------------------------------------------------------------------------------------------------ -// UserNoteSet sets the note for a specific user. -func (s *Session) UserNoteSet(userID string, message string) (err error) { - data := struct { - Note string `json:"note"` - }{message} - - _, err = s.RequestWithBucketID("PUT", EndpointUserNotes(userID), data, EndpointUserNotes("")) - return -} - -// ------------------------------------------------------------------------------------------------ -// Functions specific to Discord Relationships (Friends list) -// ------------------------------------------------------------------------------------------------ - -// RelationshipsGet returns an array of all the relationships of the user. -func (s *Session) RelationshipsGet() (r []*Relationship, err error) { - body, err := s.RequestWithBucketID("GET", EndpointRelationships(), nil, EndpointRelationships()) +// MessageThreadStartComplex creates a new thread from an existing message. +// channelID : Channel to create thread in +// messageID : Message to start thread from +// data : Parameters of the thread +func (s *Session) MessageThreadStartComplex(channelID, messageID string, data *ThreadStart) (ch *Channel, err error) { + endpoint := EndpointChannelMessageThread(channelID, messageID) + var body []byte + body, err = s.RequestWithBucketID("POST", endpoint, data, endpoint) if err != nil { return } - err = unmarshal(body, &r) + err = unmarshal(body, &ch) return } -// relationshipCreate creates a new relationship. (I.e. send or accept a friend request, block a user.) -// relationshipType : 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req -func (s *Session) relationshipCreate(userID string, relationshipType int) (err error) { - data := struct { - Type int `json:"type"` - }{relationshipType} - - _, err = s.RequestWithBucketID("PUT", EndpointRelationship(userID), data, EndpointRelationships()) - return +// MessageThreadStart creates a new thread from an existing message. +// channelID : Channel to create thread in +// messageID : Message to start thread from +// name : Name of the thread +// archiveDuration : Auto archive duration (in minutes) +func (s *Session) MessageThreadStart(channelID, messageID string, name string, archiveDuration int) (ch *Channel, err error) { + return s.MessageThreadStartComplex(channelID, messageID, &ThreadStart{ + Name: name, + AutoArchiveDuration: archiveDuration, + }) } -// RelationshipFriendRequestSend sends a friend request to a user. -// userID: ID of the user. -func (s *Session) RelationshipFriendRequestSend(userID string) (err error) { - err = s.relationshipCreate(userID, 4) - return -} - -// RelationshipFriendRequestAccept accepts a friend request from a user. -// userID: ID of the user. -func (s *Session) RelationshipFriendRequestAccept(userID string) (err error) { - err = s.relationshipCreate(userID, 1) - return -} - -// RelationshipUserBlock blocks a user. -// userID: ID of the user. -func (s *Session) RelationshipUserBlock(userID string) (err error) { - err = s.relationshipCreate(userID, 2) - return -} - -// RelationshipDelete removes the relationship with a user. -// userID: ID of the user. -func (s *Session) RelationshipDelete(userID string) (err error) { - _, err = s.RequestWithBucketID("DELETE", EndpointRelationship(userID), nil, EndpointRelationships()) - return -} - -// RelationshipsMutualGet returns an array of all the users both @me and the given user is friends with. -// userID: ID of the user. -func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) { - body, err := s.RequestWithBucketID("GET", EndpointRelationshipsMutual(userID), nil, EndpointRelationshipsMutual(userID)) +// ThreadStartComplex creates a new thread. +// channelID : Channel to create thread in +// data : Parameters of the thread +func (s *Session) ThreadStartComplex(channelID string, data *ThreadStart) (ch *Channel, err error) { + endpoint := EndpointChannelThreads(channelID) + var body []byte + body, err = s.RequestWithBucketID("POST", endpoint, data, endpoint) if err != nil { return } - err = unmarshal(body, &mf) + err = unmarshal(body, &ch) + return +} + +// ThreadStart creates a new thread. +// channelID : Channel to create thread in +// name : Name of the thread +// archiveDuration : Auto archive duration (in minutes) +func (s *Session) ThreadStart(channelID, name string, typ ChannelType, archiveDuration int) (ch *Channel, err error) { + return s.ThreadStartComplex(channelID, &ThreadStart{ + Name: name, + Type: typ, + AutoArchiveDuration: archiveDuration, + }) +} + +// ThreadJoin adds current user to a thread +func (s *Session) ThreadJoin(id string) error { + endpoint := EndpointThreadMember(id, "@me") + _, err := s.RequestWithBucketID("PUT", endpoint, nil, endpoint) + return err +} + +// ThreadLeave removes current user to a thread +func (s *Session) ThreadLeave(id string) error { + endpoint := EndpointThreadMember(id, "@me") + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + return err +} + +// ThreadMemberAdd adds another member to a thread +func (s *Session) ThreadMemberAdd(threadID, memberID string) error { + endpoint := EndpointThreadMember(threadID, memberID) + _, err := s.RequestWithBucketID("PUT", endpoint, nil, endpoint) + return err +} + +// ThreadMemberRemove removes another member from a thread +func (s *Session) ThreadMemberRemove(threadID, memberID string) error { + endpoint := EndpointThreadMember(threadID, memberID) + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + return err +} + +// ThreadMember returns thread member object for the specified member of a thread +func (s *Session) ThreadMember(threadID, memberID string) (member *ThreadMember, err error) { + endpoint := EndpointThreadMember(threadID, memberID) + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + + if err != nil { + return + } + + err = unmarshal(body, &member) + return +} + +// ThreadMembers returns all members of specified thread. +func (s *Session) ThreadMembers(threadID string) (members []*ThreadMember, err error) { + var body []byte + body, err = s.RequestWithBucketID("GET", EndpointThreadMembers(threadID), nil, EndpointThreadMembers(threadID)) + + if err != nil { + return + } + + err = unmarshal(body, &members) + return +} + +// ThreadsActive returns all active threads for specified channel. +func (s *Session) ThreadsActive(channelID string) (threads *ThreadsList, err error) { + var body []byte + body, err = s.RequestWithBucketID("GET", EndpointChannelActiveThreads(channelID), nil, EndpointChannelActiveThreads(channelID)) + if err != nil { + return + } + + err = unmarshal(body, &threads) + return +} + +// GuildThreadsActive returns all active threads for specified guild. +func (s *Session) GuildThreadsActive(guildID string) (threads *ThreadsList, err error) { + var body []byte + body, err = s.RequestWithBucketID("GET", EndpointGuildActiveThreads(guildID), nil, EndpointGuildActiveThreads(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &threads) + return +} + +// ThreadsArchived returns archived threads for specified channel. +// before : If specified returns only threads before the timestamp +// limit : Optional maximum amount of threads to return. +func (s *Session) ThreadsArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) { + endpoint := EndpointChannelPublicArchivedThreads(channelID) + v := url.Values{} + if before != nil { + v.Set("before", before.Format(time.RFC3339)) + } + + if limit > 0 { + v.Set("limit", strconv.Itoa(limit)) + } + + if len(v) > 0 { + endpoint += "?" + v.Encode() + } + + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &threads) + return +} + +// ThreadsPrivateArchived returns archived private threads for specified channel. +// before : If specified returns only threads before the timestamp +// limit : Optional maximum amount of threads to return. +func (s *Session) ThreadsPrivateArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) { + endpoint := EndpointChannelPrivateArchivedThreads(channelID) + v := url.Values{} + if before != nil { + v.Set("before", before.Format(time.RFC3339)) + } + + if limit > 0 { + v.Set("limit", strconv.Itoa(limit)) + } + + if len(v) > 0 { + endpoint += "?" + v.Encode() + } + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &threads) + return +} + +// ThreadsPrivateJoinedArchived returns archived joined private threads for specified channel. +// before : If specified returns only threads before the timestamp +// limit : Optional maximum amount of threads to return. +func (s *Session) ThreadsPrivateJoinedArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) { + endpoint := EndpointChannelJoinedPrivateArchivedThreads(channelID) + v := url.Values{} + if before != nil { + v.Set("before", before.Format(time.RFC3339)) + } + + if limit > 0 { + v.Set("limit", strconv.Itoa(limit)) + } + + if len(v) > 0 { + endpoint += "?" + v.Encode() + } + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &threads) + return +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to application (slash) commands +// ------------------------------------------------------------------------------------------------ + +// ApplicationCommandCreate creates a global application command and returns it. +// appID : The application ID. +// guildID : Guild ID to create guild-specific application command. If empty - creates global application command. +// cmd : New application command data. +func (s *Session) ApplicationCommandCreate(appID string, guildID string, cmd *ApplicationCommand) (ccmd *ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommands(appID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommands(appID, guildID) + } + + body, err := s.RequestWithBucketID("POST", endpoint, *cmd, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &ccmd) + + return +} + +// ApplicationCommandEdit edits application command and returns new command data. +// appID : The application ID. +// cmdID : Application command ID to edit. +// guildID : Guild ID to edit guild-specific application command. If empty - edits global application command. +// cmd : Updated application command data. +func (s *Session) ApplicationCommandEdit(appID, guildID, cmdID string, cmd *ApplicationCommand) (updated *ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) + } + + body, err := s.RequestWithBucketID("PATCH", endpoint, *cmd, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &updated) + + return +} + +// ApplicationCommandBulkOverwrite Creates commands overwriting existing commands. Returns a list of commands. +// appID : The application ID. +// commands : The commands to create. +func (s *Session) ApplicationCommandBulkOverwrite(appID string, guildID string, commands []*ApplicationCommand) (createdCommands []*ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommands(appID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommands(appID, guildID) + } + + body, err := s.RequestWithBucketID("PUT", endpoint, commands, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &createdCommands) + + return +} + +// ApplicationCommandDelete deletes application command by ID. +// appID : The application ID. +// cmdID : Application command ID to delete. +// guildID : Guild ID to delete guild-specific application command. If empty - deletes global application command. +func (s *Session) ApplicationCommandDelete(appID, guildID, cmdID string) error { + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) + } + + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + + return err +} + +// ApplicationCommand retrieves an application command by given ID. +// appID : The application ID. +// cmdID : Application command ID. +// guildID : Guild ID to retrieve guild-specific application command. If empty - retrieves global application command. +func (s *Session) ApplicationCommand(appID, guildID, cmdID string) (cmd *ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommand(appID, cmdID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommand(appID, guildID, cmdID) + } + + body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &cmd) + + return +} + +// ApplicationCommands retrieves all commands in application. +// appID : The application ID. +// guildID : Guild ID to retrieve all guild-specific application commands. If empty - retrieves global application commands. +func (s *Session) ApplicationCommands(appID, guildID string) (cmd []*ApplicationCommand, err error) { + endpoint := EndpointApplicationGlobalCommands(appID) + if guildID != "" { + endpoint = EndpointApplicationGuildCommands(appID, guildID) + } + + body, err := s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &cmd) + + return +} + +// GuildApplicationCommandsPermissions returns permissions for application commands in a guild. +// appID : The application ID +// guildID : Guild ID to retrieve application commands permissions for. +func (s *Session) GuildApplicationCommandsPermissions(appID, guildID string) (permissions []*GuildApplicationCommandPermissions, err error) { + endpoint := EndpointApplicationCommandsGuildPermissions(appID, guildID) + + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &permissions) + return +} + +// ApplicationCommandPermissions returns all permissions of an application command +// appID : The Application ID +// guildID : The guild ID containing the application command +// cmdID : The command ID to retrieve the permissions of +func (s *Session) ApplicationCommandPermissions(appID, guildID, cmdID string) (permissions *GuildApplicationCommandPermissions, err error) { + endpoint := EndpointApplicationCommandPermissions(appID, guildID, cmdID) + + var body []byte + body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint) + if err != nil { + return + } + + err = unmarshal(body, &permissions) + return +} + +// ApplicationCommandPermissionsEdit edits the permissions of an application command +// appID : The Application ID +// guildID : The guild ID containing the application command +// cmdID : The command ID to edit the permissions of +// permissions : An object containing a list of permissions for the application command +func (s *Session) ApplicationCommandPermissionsEdit(appID, guildID, cmdID string, permissions *ApplicationCommandPermissionsList) (err error) { + endpoint := EndpointApplicationCommandPermissions(appID, guildID, cmdID) + + _, err = s.RequestWithBucketID("PUT", endpoint, permissions, endpoint) + return +} + +// ApplicationCommandPermissionsBatchEdit edits the permissions of a batch of commands +// appID : The Application ID +// guildID : The guild ID to batch edit commands of +// permissions : A list of permissions paired with a command ID, guild ID, and application ID per application command +func (s *Session) ApplicationCommandPermissionsBatchEdit(appID, guildID string, permissions []*GuildApplicationCommandPermissions) (err error) { + endpoint := EndpointApplicationCommandsGuildPermissions(appID, guildID) + + _, err = s.RequestWithBucketID("PUT", endpoint, permissions, endpoint) + return +} + +// InteractionRespond creates the response to an interaction. +// appID : The application ID. +// interaction : Interaction instance. +// resp : Response message data. +func (s *Session) InteractionRespond(interaction *Interaction, resp *InteractionResponse) (err error) { + endpoint := EndpointInteractionResponse(interaction.ID, interaction.Token) + + if resp.Data != nil && len(resp.Data.Files) > 0 { + contentType, body, err := MultipartBodyWithJSON(resp, resp.Data.Files) + if err != nil { + return err + } + + _, err = s.request("POST", endpoint, contentType, body, endpoint, 0) + } else { + _, err = s.RequestWithBucketID("POST", endpoint, *resp, endpoint) + } + return err +} + +// InteractionResponse gets the response to an interaction. +// appID : The application ID. +// interaction : Interaction instance. +func (s *Session) InteractionResponse(appID string, interaction *Interaction) (*Message, error) { + return s.WebhookMessage(appID, interaction.Token, "@original") +} + +// InteractionResponseEdit edits the response to an interaction. +// appID : The application ID. +// interaction : Interaction instance. +// newresp : Updated response message data. +func (s *Session) InteractionResponseEdit(appID string, interaction *Interaction, newresp *WebhookEdit) (*Message, error) { + return s.WebhookMessageEdit(appID, interaction.Token, "@original", newresp) +} + +// InteractionResponseDelete deletes the response to an interaction. +// appID : The application ID. +// interaction : Interaction instance. +func (s *Session) InteractionResponseDelete(appID string, interaction *Interaction) error { + endpoint := EndpointInteractionResponseActions(appID, interaction.Token) + + _, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint) + + return err +} + +// FollowupMessageCreate creates the followup message for an interaction. +// appID : The application ID. +// interaction : Interaction instance. +// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise) +// data : Data of the message to send. +func (s *Session) FollowupMessageCreate(appID string, interaction *Interaction, wait bool, data *WebhookParams) (*Message, error) { + return s.WebhookExecute(appID, interaction.Token, wait, data) +} + +// FollowupMessageEdit edits a followup message of an interaction. +// appID : The application ID. +// interaction : Interaction instance. +// messageID : The followup message ID. +// data : Data to update the message +func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, messageID string, data *WebhookEdit) (*Message, error) { + return s.WebhookMessageEdit(appID, interaction.Token, messageID, data) +} + +// FollowupMessageDelete deletes a followup message of an interaction. +// appID : The application ID. +// interaction : Interaction instance. +// messageID : The followup message ID. +func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, messageID string) error { + return s.WebhookMessageDelete(appID, interaction.Token, messageID) +} + +// ------------------------------------------------------------------------------------------------ +// Functions specific to guilds scheduled events +// ------------------------------------------------------------------------------------------------ + +// GuildScheduledEvents returns an array of GuildScheduledEvent for a guild +// guildID : The ID of a Guild +// userCount : Whether to include the user count in the response +func (s *Session) GuildScheduledEvents(guildID string, userCount bool) (st []*GuildScheduledEvent, err error) { + uri := EndpointGuildScheduledEvents(guildID) + if userCount { + uri += "?with_user_count=true" + } + + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEvents(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildScheduledEvent returns a specific GuildScheduledEvent in a guild +// guildID : The ID of a Guild +// eventID : The ID of the event +// userCount : Whether to include the user count in the response +func (s *Session) GuildScheduledEvent(guildID, eventID string, userCount bool) (st *GuildScheduledEvent, err error) { + uri := EndpointGuildScheduledEvent(guildID, eventID) + if userCount { + uri += "?with_user_count=true" + } + + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEvent(guildID, eventID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildScheduledEventCreate creates a GuildScheduledEvent for a guild and returns it +// guildID : The ID of a Guild +// eventID : The ID of the event +func (s *Session) GuildScheduledEventCreate(guildID string, event *GuildScheduledEventParams) (st *GuildScheduledEvent, err error) { + body, err := s.RequestWithBucketID("POST", EndpointGuildScheduledEvents(guildID), event, EndpointGuildScheduledEvents(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildScheduledEventEdit updates a specific event for a guild and returns it. +// guildID : The ID of a Guild +// eventID : The ID of the event +func (s *Session) GuildScheduledEventEdit(guildID, eventID string, event *GuildScheduledEventParams) (st *GuildScheduledEvent, err error) { + body, err := s.RequestWithBucketID("PATCH", EndpointGuildScheduledEvent(guildID, eventID), event, EndpointGuildScheduledEvent(guildID, eventID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildScheduledEventDelete deletes a specific GuildScheduledEvent in a guild +// guildID : The ID of a Guild +// eventID : The ID of the event +func (s *Session) GuildScheduledEventDelete(guildID, eventID string) (err error) { + _, err = s.RequestWithBucketID("DELETE", EndpointGuildScheduledEvent(guildID, eventID), nil, EndpointGuildScheduledEvent(guildID, eventID)) + return +} + +// GuildScheduledEventUsers returns an array of GuildScheduledEventUser for a particular event in a guild +// guildID : The ID of a Guild +// eventID : The ID of the event +// limit : The maximum number of users to return (Max 100) +// withMember : Whether to include the member object in the response +// beforeID : If is not empty all returned users entries will be before the given ID +// afterID : If is not empty all returned users entries will be after the given ID +func (s *Session) GuildScheduledEventUsers(guildID, eventID string, limit int, withMember bool, beforeID, afterID string) (st []*GuildScheduledEventUser, err error) { + uri := EndpointGuildScheduledEventUsers(guildID, eventID) + + queryParams := url.Values{} + if withMember { + queryParams.Set("with_member", "true") + } + if limit > 0 { + queryParams.Set("limit", strconv.Itoa(limit)) + } + if beforeID != "" { + queryParams.Set("before", beforeID) + } + if afterID != "" { + queryParams.Set("after", afterID) + } + + if len(queryParams) > 0 { + uri += "?" + queryParams.Encode() + } + + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEventUsers(guildID, eventID)) + if err != nil { + return + } + + err = unmarshal(body, &st) return } diff --git a/vendor/github.com/matterbridge/discordgo/state.go b/vendor/github.com/bwmarrin/discordgo/state.go similarity index 82% rename from vendor/github.com/matterbridge/discordgo/state.go rename to vendor/github.com/bwmarrin/discordgo/state.go index 2eeabd80..e75be895 100644 --- a/vendor/github.com/matterbridge/discordgo/state.go +++ b/vendor/github.com/bwmarrin/discordgo/state.go @@ -38,13 +38,15 @@ type State struct { Ready // MaxMessageCount represents how many messages per channel the state will store. - MaxMessageCount int - TrackChannels bool - TrackEmojis bool - TrackMembers bool - TrackRoles bool - TrackVoice bool - TrackPresences bool + MaxMessageCount int + TrackChannels bool + TrackThreads bool + TrackEmojis bool + TrackMembers bool + TrackThreadMembers bool + TrackRoles bool + TrackVoice bool + TrackPresences bool guildMap map[string]*Guild channelMap map[string]*Channel @@ -58,15 +60,17 @@ func NewState() *State { PrivateChannels: []*Channel{}, Guilds: []*Guild{}, }, - TrackChannels: true, - TrackEmojis: true, - TrackMembers: true, - TrackRoles: true, - TrackVoice: true, - TrackPresences: true, - guildMap: make(map[string]*Guild), - channelMap: make(map[string]*Channel), - memberMap: make(map[string]map[string]*Member), + TrackChannels: true, + TrackThreads: true, + TrackEmojis: true, + TrackMembers: true, + TrackThreadMembers: true, + TrackRoles: true, + TrackVoice: true, + TrackPresences: true, + guildMap: make(map[string]*Guild), + channelMap: make(map[string]*Channel), + memberMap: make(map[string]map[string]*Member), } } @@ -93,6 +97,11 @@ func (s *State) GuildAdd(guild *Guild) error { s.channelMap[c.ID] = c } + // Add all the threads to the state in case of thread sync list. + for _, t := range guild.Threads { + s.channelMap[t.ID] = t + } + // If this guild contains a new member slice, we must regenerate the member map so the pointers stay valid if guild.Members != nil { s.createMemberMap(guild) @@ -122,6 +131,9 @@ func (s *State) GuildAdd(guild *Guild) error { if guild.Channels == nil { guild.Channels = g.Channels } + if guild.Threads == nil { + guild.Threads = g.Threads + } if guild.VoiceStates == nil { guild.VoiceStates = g.VoiceStates } @@ -180,21 +192,12 @@ func (s *State) Guild(guildID string) (*Guild, error) { return nil, ErrStateNotFound } -// PresenceAdd adds a presence to the current world state, or -// updates it if it already exists. -func (s *State) PresenceAdd(guildID string, presence *Presence) error { - if s == nil { - return ErrNilState +func (s *State) presenceAdd(guildID string, presence *Presence) error { + guild, ok := s.guildMap[guildID] + if !ok { + return ErrStateNotFound } - guild, err := s.Guild(guildID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - for i, p := range guild.Presences { if p.User.ID == presence.User.ID { //guild.Presences[i] = presence @@ -233,6 +236,19 @@ func (s *State) PresenceAdd(guildID string, presence *Presence) error { return nil } +// PresenceAdd adds a presence to the current world state, or +// updates it if it already exists. +func (s *State) PresenceAdd(guildID string, presence *Presence) error { + if s == nil { + return ErrNilState + } + + s.Lock() + defer s.Unlock() + + return s.presenceAdd(guildID, presence) +} + // PresenceRemove removes a presence from the current world state. func (s *State) PresenceRemove(guildID string, presence *Presence) error { if s == nil { @@ -279,21 +295,12 @@ func (s *State) Presence(guildID, userID string) (*Presence, error) { // TODO: Consider moving Guild state update methods onto *Guild. -// MemberAdd adds a member to the current world state, or -// updates it if it already exists. -func (s *State) MemberAdd(member *Member) error { - if s == nil { - return ErrNilState +func (s *State) memberAdd(member *Member) error { + guild, ok := s.guildMap[member.GuildID] + if !ok { + return ErrStateNotFound } - guild, err := s.Guild(member.GuildID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - members, ok := s.memberMap[member.GuildID] if !ok { return ErrStateNotFound @@ -306,15 +313,27 @@ func (s *State) MemberAdd(member *Member) error { } else { // We are about to replace `m` in the state with `member`, but first we need to // make sure we preserve any fields that the `member` doesn't contain from `m`. - if member.JoinedAt == "" { + if member.JoinedAt.IsZero() { member.JoinedAt = m.JoinedAt } *m = *member } - return nil } +// MemberAdd adds a member to the current world state, or +// updates it if it already exists. +func (s *State) MemberAdd(member *Member) error { + if s == nil { + return ErrNilState + } + + s.Lock() + defer s.Unlock() + + return s.memberAdd(member) +} + // MemberRemove removes a member from current world state. func (s *State) MemberRemove(member *Member) error { if s == nil { @@ -465,6 +484,9 @@ func (s *State) ChannelAdd(channel *Channel) error { if channel.PermissionOverwrites == nil { channel.PermissionOverwrites = c.PermissionOverwrites } + if channel.ThreadMetadata == nil { + channel.ThreadMetadata = c.ThreadMetadata + } *c = *channel return nil @@ -472,12 +494,18 @@ func (s *State) ChannelAdd(channel *Channel) error { if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM { s.PrivateChannels = append(s.PrivateChannels, channel) - } else { - guild, ok := s.guildMap[channel.GuildID] - if !ok { - return ErrStateNotFound - } + s.channelMap[channel.ID] = channel + return nil + } + guild, ok := s.guildMap[channel.GuildID] + if !ok { + return ErrStateNotFound + } + + if channel.IsThread() { + guild.Threads = append(guild.Threads, channel) + } else { guild.Channels = append(guild.Channels, channel) } @@ -507,15 +535,26 @@ func (s *State) ChannelRemove(channel *Channel) error { break } } - } else { - guild, err := s.Guild(channel.GuildID) - if err != nil { - return err + delete(s.channelMap, channel.ID) + return nil + } + + guild, err := s.Guild(channel.GuildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + if channel.IsThread() { + for i, t := range guild.Threads { + if t.ID == channel.ID { + guild.Threads = append(guild.Threads[:i], guild.Threads[i+1:]...) + break + } } - - s.Lock() - defer s.Unlock() - + } else { for i, c := range guild.Channels { if c.ID == channel.ID { guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...) @@ -529,6 +568,99 @@ func (s *State) ChannelRemove(channel *Channel) error { return nil } +// ThreadListSync syncs guild threads with provided ones. +func (s *State) ThreadListSync(tls *ThreadListSync) error { + guild, err := s.Guild(tls.GuildID) + if err != nil { + return err + } + + s.Lock() + defer s.Unlock() + + // This algorithm filters out archived or + // threads which are children of channels in channelIDs + // and then it adds all synced threads to guild threads and cache + index := 0 +outer: + for _, t := range guild.Threads { + if !t.ThreadMetadata.Archived && tls.ChannelIDs != nil { + for _, v := range tls.ChannelIDs { + if t.ParentID == v { + delete(s.channelMap, t.ID) + continue outer + } + } + guild.Threads[index] = t + index++ + } else { + delete(s.channelMap, t.ID) + } + } + guild.Threads = guild.Threads[:index] + for _, t := range tls.Threads { + s.channelMap[t.ID] = t + guild.Threads = append(guild.Threads, t) + } + + for _, m := range tls.Members { + if c, ok := s.channelMap[m.ID]; ok { + c.Member = m + } + } + + return nil +} + +// ThreadMembersUpdate updates thread members list +func (s *State) ThreadMembersUpdate(tmu *ThreadMembersUpdate) error { + thread, err := s.Channel(tmu.ID) + if err != nil { + return err + } + s.Lock() + defer s.Unlock() + + for idx, member := range thread.Members { + for _, removedMember := range tmu.RemovedMembers { + if member.ID == removedMember { + thread.Members = append(thread.Members[:idx], thread.Members[idx+1:]...) + break + } + } + } + + for _, addedMember := range tmu.AddedMembers { + thread.Members = append(thread.Members, addedMember.ThreadMember) + if addedMember.Member != nil { + err = s.memberAdd(addedMember.Member) + if err != nil { + return err + } + } + if addedMember.Presence != nil { + err = s.presenceAdd(tmu.GuildID, addedMember.Presence) + if err != nil { + return err + } + } + } + thread.MemberCount = tmu.MemberCount + + return nil +} + +// ThreadMemberUpdate sets or updates member data for the current user. +func (s *State) ThreadMemberUpdate(mu *ThreadMemberUpdate) error { + thread, err := s.Channel(mu.ID) + if err != nil { + return err + } + + thread.Member = mu.ThreadMember + return nil +} + // GuildChannel gets a channel by ID from a guild. // This method is Deprecated, use Channel(channelID) func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) { @@ -637,7 +769,7 @@ func (s *State) MessageAdd(message *Message) error { if message.Content != "" { m.Content = message.Content } - if message.EditedTimestamp != "" { + if message.EditedTimestamp != nil { m.EditedTimestamp = message.EditedTimestamp } if message.Mentions != nil { @@ -649,12 +781,15 @@ func (s *State) MessageAdd(message *Message) error { if message.Attachments != nil { m.Attachments = message.Attachments } - if message.Timestamp != "" { + if !message.Timestamp.IsZero() { m.Timestamp = message.Timestamp } if message.Author != nil { m.Author = message.Author } + if message.Components != nil { + m.Components = message.Components + } return nil } @@ -665,6 +800,7 @@ func (s *State) MessageAdd(message *Message) error { if len(c.Messages) > s.MaxMessageCount { c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:] } + return nil } @@ -690,6 +826,7 @@ func (s *State) messageRemoveByID(channelID, messageID string) error { for i, m := range c.Messages { if m.ID == messageID { c.Messages = append(c.Messages[:i], c.Messages[i+1:]...) + return nil } } @@ -833,6 +970,13 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) { case *GuildUpdate: err = s.GuildAdd(t.Guild) case *GuildDelete: + var old *Guild + old, err = s.Guild(t.ID) + if err == nil { + oldCopy := *old + t.BeforeDelete = &oldCopy + } + err = s.GuildRemove(t.Guild) case *GuildMemberAdd: // Updates the MemberCount of the guild. @@ -903,6 +1047,35 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) { if s.TrackChannels { err = s.ChannelRemove(t.Channel) } + case *ThreadCreate: + if s.TrackThreads { + err = s.ChannelAdd(t.Channel) + } + case *ThreadUpdate: + if s.TrackThreads { + old, err := s.Channel(t.ID) + if err == nil { + oldCopy := *old + t.BeforeUpdate = &oldCopy + } + err = s.ChannelAdd(t.Channel) + } + case *ThreadDelete: + if s.TrackThreads { + err = s.ChannelRemove(t.Channel) + } + case *ThreadMemberUpdate: + if s.TrackThreads { + err = s.ThreadMemberUpdate(t) + } + case *ThreadMembersUpdate: + if s.TrackThreadMembers { + err = s.ThreadMembersUpdate(t) + } + case *ThreadListSync: + if s.TrackThreads { + err = s.ThreadListSync(t) + } case *MessageCreate: if s.MaxMessageCount != 0 { err = s.MessageAdd(t.Message) diff --git a/vendor/github.com/bwmarrin/discordgo/structs.go b/vendor/github.com/bwmarrin/discordgo/structs.go new file mode 100644 index 00000000..3a92c9fd --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/structs.go @@ -0,0 +1,2038 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains all structures for the discordgo package. These +// may be moved about later into separate files but I find it easier to have +// them all located together. + +package discordgo + +import ( + "encoding/json" + "fmt" + "math" + "net/http" + "regexp" + "strings" + "sync" + "time" + + "github.com/gorilla/websocket" +) + +// A Session represents a connection to the Discord API. +type Session struct { + sync.RWMutex + + // General configurable settings. + + // Authentication token for this session + // TODO: Remove Below, Deprecated, Use Identify struct + Token string + + MFA bool + + // Debug for printing JSON request/responses + Debug bool // Deprecated, will be removed. + LogLevel int + + // Should the session reconnect the websocket on errors. + ShouldReconnectOnError bool + + // Identify is sent during initial handshake with the discord gateway. + // https://discord.com/developers/docs/topics/gateway#identify + Identify Identify + + // TODO: Remove Below, Deprecated, Use Identify struct + // Should the session request compressed websocket data. + Compress bool + + // Sharding + ShardID int + ShardCount int + + // Should state tracking be enabled. + // State tracking is the best way for getting the the users + // active guilds and the members of the guilds. + StateEnabled bool + + // Whether or not to call event handlers synchronously. + // e.g false = launch event handlers in their own goroutines. + SyncEvents bool + + // Exposed but should not be modified by User. + + // Whether the Data Websocket is ready + DataReady bool // NOTE: Maye be deprecated soon + + // Max number of REST API retries + MaxRestRetries int + + // Status stores the currect status of the websocket connection + // this is being tested, may stay, may go away. + status int32 + + // Whether the Voice Websocket is ready + VoiceReady bool // NOTE: Deprecated. + + // Whether the UDP Connection is ready + UDPReady bool // NOTE: Deprecated + + // Stores a mapping of guild id's to VoiceConnections + VoiceConnections map[string]*VoiceConnection + + // Managed state object, updated internally with events when + // StateEnabled is true. + State *State + + // The http client used for REST requests + Client *http.Client + + // The user agent used for REST APIs + UserAgent string + + // Stores the last HeartbeatAck that was received (in UTC) + LastHeartbeatAck time.Time + + // Stores the last Heartbeat sent (in UTC) + LastHeartbeatSent time.Time + + // used to deal with rate limits + Ratelimiter *RateLimiter + + // Event handlers + handlersMu sync.RWMutex + handlers map[string][]*eventHandlerInstance + onceHandlers map[string][]*eventHandlerInstance + + // The websocket connection. + wsConn *websocket.Conn + + // When nil, the session is not listening. + listening chan interface{} + + // sequence tracks the current gateway api websocket sequence number + sequence *int64 + + // stores sessions current Discord Gateway + gateway string + + // stores session ID of current Gateway connection + sessionID string + + // used to make sure gateway websocket writes do not happen concurrently + wsMutex sync.Mutex +} + +// Application stores values for a Discord Application +type Application struct { + ID string `json:"id,omitempty"` + Name string `json:"name"` + Icon string `json:"icon,omitempty"` + Description string `json:"description,omitempty"` + RPCOrigins []string `json:"rpc_origins,omitempty"` + BotPublic bool `json:"bot_public,omitempty"` + BotRequireCodeGrant bool `json:"bot_require_code_grant,omitempty"` + TermsOfServiceURL string `json:"terms_of_service_url"` + PrivacyProxyURL string `json:"privacy_policy_url"` + Owner *User `json:"owner"` + Summary string `json:"summary"` + VerifyKey string `json:"verify_key"` + Team *Team `json:"team"` + GuildID string `json:"guild_id"` + PrimarySKUID string `json:"primary_sku_id"` + Slug string `json:"slug"` + CoverImage string `json:"cover_image"` + Flags int `json:"flags,omitempty"` +} + +// UserConnection is a Connection returned from the UserConnections endpoint +type UserConnection struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Revoked bool `json:"revoked"` + Integrations []*Integration `json:"integrations"` +} + +// Integration stores integration information +type Integration struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Enabled bool `json:"enabled"` + Syncing bool `json:"syncing"` + RoleID string `json:"role_id"` + EnableEmoticons bool `json:"enable_emoticons"` + ExpireBehavior ExpireBehavior `json:"expire_behavior"` + ExpireGracePeriod int `json:"expire_grace_period"` + User *User `json:"user"` + Account IntegrationAccount `json:"account"` + SyncedAt time.Time `json:"synced_at"` +} + +// ExpireBehavior of Integration +// https://discord.com/developers/docs/resources/guild#integration-object-integration-expire-behaviors +type ExpireBehavior int + +// Block of valid ExpireBehaviors +const ( + ExpireBehaviorRemoveRole ExpireBehavior = 0 + ExpireBehaviorKick ExpireBehavior = 1 +) + +// IntegrationAccount is integration account information +// sent by the UserConnections endpoint +type IntegrationAccount struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// A VoiceRegion stores data for a specific voice region server. +type VoiceRegion struct { + ID string `json:"id"` + Name string `json:"name"` + Hostname string `json:"sample_hostname"` + Port int `json:"sample_port"` +} + +// A VoiceICE stores data for voice ICE servers. +type VoiceICE struct { + TTL string `json:"ttl"` + Servers []*ICEServer `json:"servers"` +} + +// A ICEServer stores data for a specific voice ICE server. +type ICEServer struct { + URL string `json:"url"` + Username string `json:"username"` + Credential string `json:"credential"` +} + +// InviteTargetType indicates the type of target of an invite +// https://discord.com/developers/docs/resources/invite#invite-object-invite-target-types +type InviteTargetType uint8 + +// Invite target types +const ( + InviteTargetStream InviteTargetType = 1 + InviteTargetEmbeddedAppliction InviteTargetType = 2 +) + +// A Invite stores all data related to a specific Discord Guild or Channel invite. +type Invite struct { + Guild *Guild `json:"guild"` + Channel *Channel `json:"channel"` + Inviter *User `json:"inviter"` + Code string `json:"code"` + CreatedAt time.Time `json:"created_at"` + MaxAge int `json:"max_age"` + Uses int `json:"uses"` + MaxUses int `json:"max_uses"` + Revoked bool `json:"revoked"` + Temporary bool `json:"temporary"` + Unique bool `json:"unique"` + TargetUser *User `json:"target_user"` + TargetType InviteTargetType `json:"target_type"` + TargetApplication *Application `json:"target_application"` + + // will only be filled when using InviteWithCounts + ApproximatePresenceCount int `json:"approximate_presence_count"` + ApproximateMemberCount int `json:"approximate_member_count"` +} + +// ChannelType is the type of a Channel +type ChannelType int + +// Block contains known ChannelType values +const ( + ChannelTypeGuildText ChannelType = 0 + ChannelTypeDM ChannelType = 1 + ChannelTypeGuildVoice ChannelType = 2 + ChannelTypeGroupDM ChannelType = 3 + ChannelTypeGuildCategory ChannelType = 4 + ChannelTypeGuildNews ChannelType = 5 + ChannelTypeGuildStore ChannelType = 6 + ChannelTypeGuildNewsThread ChannelType = 10 + ChannelTypeGuildPublicThread ChannelType = 11 + ChannelTypeGuildPrivateThread ChannelType = 12 +) + +// A Channel holds all data related to an individual Discord channel. +type Channel struct { + // The ID of the channel. + ID string `json:"id"` + + // The ID of the guild to which the channel belongs, if it is in a guild. + // Else, this ID is empty (e.g. DM channels). + GuildID string `json:"guild_id"` + + // The name of the channel. + Name string `json:"name"` + + // The topic of the channel. + Topic string `json:"topic"` + + // The type of the channel. + Type ChannelType `json:"type"` + + // The ID of the last message sent in the channel. This is not + // guaranteed to be an ID of a valid message. + LastMessageID string `json:"last_message_id"` + + // The timestamp of the last pinned message in the channel. + // nil if the channel has no pinned messages. + LastPinTimestamp *time.Time `json:"last_pin_timestamp"` + + // An approximate count of messages in a thread, stops counting at 50 + MessageCount int `json:"message_count"` + // An approximate count of users in a thread, stops counting at 50 + MemberCount int `json:"member_count"` + + // Whether the channel is marked as NSFW. + NSFW bool `json:"nsfw"` + + // Icon of the group DM channel. + Icon string `json:"icon"` + + // The position of the channel, used for sorting in client. + Position int `json:"position"` + + // The bitrate of the channel, if it is a voice channel. + Bitrate int `json:"bitrate"` + + // The recipients of the channel. This is only populated in DM channels. + Recipients []*User `json:"recipients"` + + // The messages in the channel. This is only present in state-cached channels, + // and State.MaxMessageCount must be non-zero. + Messages []*Message `json:"-"` + + // A list of permission overwrites present for the channel. + PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"` + + // The user limit of the voice channel. + UserLimit int `json:"user_limit"` + + // The ID of the parent channel, if the channel is under a category. For threads - id of the channel thread was created in. + ParentID string `json:"parent_id"` + + // Amount of seconds a user has to wait before sending another message or creating another thread (0-21600) + // bots, as well as users with the permission manage_messages or manage_channel, are unaffected + RateLimitPerUser int `json:"rate_limit_per_user"` + + // ID of the creator of the group DM or thread + OwnerID string `json:"owner_id"` + + // ApplicationID of the DM creator Zeroed if guild channel or not a bot user + ApplicationID string `json:"application_id"` + + // Thread-specific fields not needed by other channels + ThreadMetadata *ThreadMetadata `json:"thread_metadata,omitempty"` + // Thread member object for the current user, if they have joined the thread, only included on certain API endpoints + Member *ThreadMember `json:"thread_member"` + + // All thread members. State channels only. + Members []*ThreadMember `json:"-"` +} + +// Mention returns a string which mentions the channel +func (c *Channel) Mention() string { + return fmt.Sprintf("<#%s>", c.ID) +} + +// IsThread is a helper function to determine if channel is a thread or not +func (c *Channel) IsThread() bool { + return c.Type == ChannelTypeGuildPublicThread || c.Type == ChannelTypeGuildPrivateThread || c.Type == ChannelTypeGuildNewsThread +} + +// A ChannelEdit holds Channel Field data for a channel edit. +type ChannelEdit struct { + Name string `json:"name,omitempty"` + Topic string `json:"topic,omitempty"` + NSFW bool `json:"nsfw,omitempty"` + Position int `json:"position"` + Bitrate int `json:"bitrate,omitempty"` + UserLimit int `json:"user_limit,omitempty"` + PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"` + ParentID string `json:"parent_id,omitempty"` + RateLimitPerUser int `json:"rate_limit_per_user,omitempty"` + + // NOTE: threads only + + Archived bool `json:"archived,omitempty"` + AutoArchiveDuration int `json:"auto_archive_duration,omitempty"` + Locked bool `json:"locked,bool"` + Invitable bool `json:"invitable,omitempty"` +} + +// A ChannelFollow holds data returned after following a news channel +type ChannelFollow struct { + ChannelID string `json:"channel_id"` + WebhookID string `json:"webhook_id"` +} + +// PermissionOverwriteType represents the type of resource on which +// a permission overwrite acts. +type PermissionOverwriteType int + +// The possible permission overwrite types. +const ( + PermissionOverwriteTypeRole PermissionOverwriteType = 0 + PermissionOverwriteTypeMember PermissionOverwriteType = 1 +) + +// A PermissionOverwrite holds permission overwrite data for a Channel +type PermissionOverwrite struct { + ID string `json:"id"` + Type PermissionOverwriteType `json:"type"` + Deny int64 `json:"deny,string"` + Allow int64 `json:"allow,string"` +} + +// ThreadStart stores all parameters you can use with MessageThreadStartComplex or ThreadStartComplex +type ThreadStart struct { + Name string `json:"name"` + AutoArchiveDuration int `json:"auto_archive_duration,omitempty"` + Type ChannelType `json:"type,omitempty"` + Invitable bool `json:"invitable"` + RateLimitPerUser int `json:"rate_limit_per_user,omitempty"` +} + +// ThreadMetadata contains a number of thread-specific channel fields that are not needed by other channel types. +type ThreadMetadata struct { + // Whether the thread is archived + Archived bool `json:"archived"` + // Duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 + AutoArchiveDuration int `json:"auto_archive_duration"` + // Timestamp when the thread's archive status was last changed, used for calculating recent activity + ArchiveTimestamp time.Time `json:"archive_timestamp"` + // Whether the thread is locked; when a thread is locked, only users with MANAGE_THREADS can unarchive it + Locked bool `json:"locked"` + // Whether non-moderators can add other non-moderators to a thread; only available on private threads + Invitable bool `json:"invitable"` +} + +// ThreadMember is used to indicate whether a user has joined a thread or not. +// NOTE: ID and UserID are empty (omitted) on the member sent within each thread in the GUILD_CREATE event. +type ThreadMember struct { + // The id of the thread + ID string `json:"id,omitempty"` + // The id of the user + UserID string `json:"user_id,omitempty"` + // The time the current user last joined the thread + JoinTimestamp time.Time `json:"join_timestamp"` + // Any user-thread settings, currently only used for notifications + Flags int +} + +// ThreadsList represents a list of threads alongisde with thread member objects for the current user. +type ThreadsList struct { + Threads []*Channel `json:"threads"` + Members []*ThreadMember `json:"members"` + HasMore bool `json:"has_more"` +} + +// AddedThreadMember holds information about the user who was added to the thread +type AddedThreadMember struct { + *ThreadMember + Member *Member `json:"member"` + Presence *Presence `json:"presence"` +} + +// Emoji struct holds data related to Emoji's +type Emoji struct { + ID string `json:"id"` + Name string `json:"name"` + Roles []string `json:"roles"` + User *User `json:"user"` + RequireColons bool `json:"require_colons"` + Managed bool `json:"managed"` + Animated bool `json:"animated"` + Available bool `json:"available"` +} + +// EmojiRegex is the regex used to find and identify emojis in messages +var ( + EmojiRegex = regexp.MustCompile(`<(a|):[A-z0-9_~]+:[0-9]{18}>`) +) + +// MessageFormat returns a correctly formatted Emoji for use in Message content and embeds +func (e *Emoji) MessageFormat() string { + if e.ID != "" && e.Name != "" { + if e.Animated { + return "" + } + + return "<:" + e.APIName() + ">" + } + + return e.APIName() +} + +// APIName returns an correctly formatted API name for use in the MessageReactions endpoints. +func (e *Emoji) APIName() string { + if e.ID != "" && e.Name != "" { + return e.Name + ":" + e.ID + } + if e.Name != "" { + return e.Name + } + return e.ID +} + +// StickerFormat is the file format of the Sticker. +type StickerFormat int + +// Defines all known Sticker types. +const ( + StickerFormatTypePNG StickerFormat = 1 + StickerFormatTypeAPNG StickerFormat = 2 + StickerFormatTypeLottie StickerFormat = 3 +) + +// StickerType is the type of sticker. +type StickerType int + +// Defines Sticker types. +const ( + StickerTypeStandard StickerType = 1 + StickerTypeGuild StickerType = 2 +) + +// Sticker represents a sticker object that can be sent in a Message. +type Sticker struct { + ID string `json:"id"` + PackID string `json:"pack_id"` + Name string `json:"name"` + Description string `json:"description"` + Tags string `json:"tags"` + Type StickerType `json:"type"` + FormatType StickerFormat `json:"format_type"` + Available bool `json:"available"` + GuildID string `json:"guild_id"` + User *User `json:"user"` + SortValue int `json:"sort_value"` +} + +// StickerPack represents a pack of standard stickers. +type StickerPack struct { + ID string `json:"id"` + Stickers []*Sticker `json:"stickers"` + Name string `json:"name"` + SKUID string `json:"sku_id"` + CoverStickerID string `json:"cover_sticker_id"` + Description string `json:"description"` + BannerAssetID string `json:"banner_asset_id"` +} + +// VerificationLevel type definition +type VerificationLevel int + +// Constants for VerificationLevel levels from 0 to 4 inclusive +const ( + VerificationLevelNone VerificationLevel = 0 + VerificationLevelLow VerificationLevel = 1 + VerificationLevelMedium VerificationLevel = 2 + VerificationLevelHigh VerificationLevel = 3 + VerificationLevelVeryHigh VerificationLevel = 4 +) + +// ExplicitContentFilterLevel type definition +type ExplicitContentFilterLevel int + +// Constants for ExplicitContentFilterLevel levels from 0 to 2 inclusive +const ( + ExplicitContentFilterDisabled ExplicitContentFilterLevel = 0 + ExplicitContentFilterMembersWithoutRoles ExplicitContentFilterLevel = 1 + ExplicitContentFilterAllMembers ExplicitContentFilterLevel = 2 +) + +// MfaLevel type definition +type MfaLevel int + +// Constants for MfaLevel levels from 0 to 1 inclusive +const ( + MfaLevelNone MfaLevel = 0 + MfaLevelElevated MfaLevel = 1 +) + +// PremiumTier type definition +type PremiumTier int + +// Constants for PremiumTier levels from 0 to 3 inclusive +const ( + PremiumTierNone PremiumTier = 0 + PremiumTier1 PremiumTier = 1 + PremiumTier2 PremiumTier = 2 + PremiumTier3 PremiumTier = 3 +) + +// A Guild holds all data related to a specific Discord Guild. Guilds are also +// sometimes referred to as Servers in the Discord client. +type Guild struct { + // The ID of the guild. + ID string `json:"id"` + + // The name of the guild. (2–100 characters) + Name string `json:"name"` + + // The hash of the guild's icon. Use Session.GuildIcon + // to retrieve the icon itself. + Icon string `json:"icon"` + + // The voice region of the guild. + Region string `json:"region"` + + // The ID of the AFK voice channel. + AfkChannelID string `json:"afk_channel_id"` + + // The user ID of the owner of the guild. + OwnerID string `json:"owner_id"` + + // If we are the owner of the guild + Owner bool `json:"owner"` + + // The time at which the current user joined the guild. + // This field is only present in GUILD_CREATE events and websocket + // update events, and thus is only present in state-cached guilds. + JoinedAt time.Time `json:"joined_at"` + + // The hash of the guild's discovery splash. + DiscoverySplash string `json:"discovery_splash"` + + // The hash of the guild's splash. + Splash string `json:"splash"` + + // The timeout, in seconds, before a user is considered AFK in voice. + AfkTimeout int `json:"afk_timeout"` + + // The number of members in the guild. + // This field is only present in GUILD_CREATE events and websocket + // update events, and thus is only present in state-cached guilds. + MemberCount int `json:"member_count"` + + // The verification level required for the guild. + VerificationLevel VerificationLevel `json:"verification_level"` + + // Whether the guild is considered large. This is + // determined by a member threshold in the identify packet, + // and is currently hard-coded at 250 members in the library. + Large bool `json:"large"` + + // The default message notification setting for the guild. + DefaultMessageNotifications MessageNotifications `json:"default_message_notifications"` + + // A list of roles in the guild. + Roles []*Role `json:"roles"` + + // A list of the custom emojis present in the guild. + Emojis []*Emoji `json:"emojis"` + + // A list of the custom stickers present in the guild. + Stickers []*Sticker `json:"stickers"` + + // A list of the members in the guild. + // This field is only present in GUILD_CREATE events and websocket + // update events, and thus is only present in state-cached guilds. + Members []*Member `json:"members"` + + // A list of partial presence objects for members in the guild. + // This field is only present in GUILD_CREATE events and websocket + // update events, and thus is only present in state-cached guilds. + Presences []*Presence `json:"presences"` + + // The maximum number of presences for the guild (the default value, currently 25000, is in effect when null is returned) + MaxPresences int `json:"max_presences"` + + // The maximum number of members for the guild + MaxMembers int `json:"max_members"` + + // A list of channels in the guild. + // This field is only present in GUILD_CREATE events and websocket + // update events, and thus is only present in state-cached guilds. + Channels []*Channel `json:"channels"` + + // A list of all active threads in the guild that current user has permission to view + // This field is only present in GUILD_CREATE events and websocket + // update events and thus is only present in state-cached guilds. + Threads []*Channel `json:"threads"` + + // A list of voice states for the guild. + // This field is only present in GUILD_CREATE events and websocket + // update events, and thus is only present in state-cached guilds. + VoiceStates []*VoiceState `json:"voice_states"` + + // Whether this guild is currently unavailable (most likely due to outage). + // This field is only present in GUILD_CREATE events and websocket + // update events, and thus is only present in state-cached guilds. + Unavailable bool `json:"unavailable"` + + // The explicit content filter level + ExplicitContentFilter ExplicitContentFilterLevel `json:"explicit_content_filter"` + + // The list of enabled guild features + Features []string `json:"features"` + + // Required MFA level for the guild + MfaLevel MfaLevel `json:"mfa_level"` + + // The application id of the guild if bot created. + ApplicationID string `json:"application_id"` + + // Whether or not the Server Widget is enabled + WidgetEnabled bool `json:"widget_enabled"` + + // The Channel ID for the Server Widget + WidgetChannelID string `json:"widget_channel_id"` + + // The Channel ID to which system messages are sent (eg join and leave messages) + SystemChannelID string `json:"system_channel_id"` + + // The System channel flags + SystemChannelFlags SystemChannelFlag `json:"system_channel_flags"` + + // The ID of the rules channel ID, used for rules. + RulesChannelID string `json:"rules_channel_id"` + + // the vanity url code for the guild + VanityURLCode string `json:"vanity_url_code"` + + // the description for the guild + Description string `json:"description"` + + // The hash of the guild's banner + Banner string `json:"banner"` + + // The premium tier of the guild + PremiumTier PremiumTier `json:"premium_tier"` + + // The total number of users currently boosting this server + PremiumSubscriptionCount int `json:"premium_subscription_count"` + + // The preferred locale of a guild with the "PUBLIC" feature; used in server discovery and notices from Discord; defaults to "en-US" + PreferredLocale string `json:"preferred_locale"` + + // The id of the channel where admins and moderators of guilds with the "PUBLIC" feature receive notices from Discord + PublicUpdatesChannelID string `json:"public_updates_channel_id"` + + // The maximum amount of users in a video channel + MaxVideoChannelUsers int `json:"max_video_channel_users"` + + // Approximate number of members in this guild, returned from the GET /guild/ endpoint when with_counts is true + ApproximateMemberCount int `json:"approximate_member_count"` + + // Approximate number of non-offline members in this guild, returned from the GET /guild/ endpoint when with_counts is true + ApproximatePresenceCount int `json:"approximate_presence_count"` + + // Permissions of our user + Permissions int64 `json:"permissions,string"` +} + +// A GuildPreview holds data related to a specific public Discord Guild, even if the user is not in the guild. +type GuildPreview struct { + // The ID of the guild. + ID string `json:"id"` + + // The name of the guild. (2–100 characters) + Name string `json:"name"` + + // The hash of the guild's icon. Use Session.GuildIcon + // to retrieve the icon itself. + Icon string `json:"icon"` + + // The hash of the guild's splash. + Splash string `json:"splash"` + + // The hash of the guild's discovery splash. + DiscoverySplash string `json:"discovery_splash"` + + // A list of the custom emojis present in the guild. + Emojis []*Emoji `json:"emojis"` + + // The list of enabled guild features + Features []string `json:"features"` + + // Approximate number of members in this guild, returned from the GET /guild/ endpoint when with_counts is true + ApproximateMemberCount int `json:"approximate_member_count"` + + // Approximate number of non-offline members in this guild, returned from the GET /guild/ endpoint when with_counts is true + ApproximatePresenceCount int `json:"approximate_presence_count"` + + // the description for the guild + Description string `json:"description"` +} + +// GuildScheduledEvent is a representation of a scheduled event in a guild. Only for retrieval of the data. +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event +type GuildScheduledEvent struct { + // The ID of the scheduled event + ID string `json:"id"` + // The guild id which the scheduled event belongs to + GuildID string `json:"guild_id"` + // The channel id in which the scheduled event will be hosted, or null if scheduled entity type is EXTERNAL + ChannelID string `json:"channel_id"` + // The id of the user that created the scheduled event + CreatorID string `json:"creator_id"` + // The name of the scheduled event (1-100 characters) + Name string `json:"name"` + // The description of the scheduled event (1-1000 characters) + Description string `json:"description"` + // The time the scheduled event will start + ScheduledStartTime time.Time `json:"scheduled_start_time"` + // The time the scheduled event will end, required only when entity_type is EXTERNAL + ScheduledEndTime *time.Time `json:"scheduled_end_time"` + // The privacy level of the scheduled event + PrivacyLevel GuildScheduledEventPrivacyLevel `json:"privacy_level"` + // The status of the scheduled event + Status GuildScheduledEventStatus `json:"status"` + // Type of the entity where event would be hosted + // See field requirements + // https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-field-requirements-by-entity-type + EntityType GuildScheduledEventEntityType `json:"entity_type"` + // The id of an entity associated with a guild scheduled event + EntityID string `json:"entity_id"` + // Additional metadata for the guild scheduled event + EntityMetadata GuildScheduledEventEntityMetadata `json:"entity_metadata"` + // The user that created the scheduled event + Creator *User `json:"creator"` + // The number of users subscribed to the scheduled event + UserCount int `json:"user_count"` + // The cover image hash of the scheduled event + // see https://discord.com/developers/docs/reference#image-formatting for more + // information about image formatting + Image string `json:"image"` +} + +// GuildScheduledEventParams are the parameters allowed for creating or updating a scheduled event +// https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event +type GuildScheduledEventParams struct { + // The channel id in which the scheduled event will be hosted, or null if scheduled entity type is EXTERNAL + ChannelID string `json:"channel_id,omitempty"` + // The name of the scheduled event (1-100 characters) + Name string `json:"name,omitempty"` + // The description of the scheduled event (1-1000 characters) + Description string `json:"description,omitempty"` + // The time the scheduled event will start + ScheduledStartTime *time.Time `json:"scheduled_start_time,omitempty"` + // The time the scheduled event will end, required only when entity_type is EXTERNAL + ScheduledEndTime *time.Time `json:"scheduled_end_time,omitempty"` + // The privacy level of the scheduled event + PrivacyLevel GuildScheduledEventPrivacyLevel `json:"privacy_level,omitempty"` + // The status of the scheduled event + Status GuildScheduledEventStatus `json:"status,omitempty"` + // Type of the entity where event would be hosted + // See field requirements + // https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-field-requirements-by-entity-type + EntityType GuildScheduledEventEntityType `json:"entity_type,omitempty"` + // Additional metadata for the guild scheduled event + EntityMetadata *GuildScheduledEventEntityMetadata `json:"entity_metadata,omitempty"` + // The cover image hash of the scheduled event + // see https://discord.com/developers/docs/reference#image-formatting for more + // information about image formatting + Image string `json:"image,omitempty"` +} + +// MarshalJSON is a helper function to marshal GuildScheduledEventParams +func (p GuildScheduledEventParams) MarshalJSON() ([]byte, error) { + type guildScheduledEventParams GuildScheduledEventParams + + if p.EntityType == GuildScheduledEventEntityTypeExternal && p.ChannelID == "" { + return json.Marshal(struct { + guildScheduledEventParams + ChannelID json.RawMessage `json:"channel_id"` + }{ + guildScheduledEventParams: guildScheduledEventParams(p), + ChannelID: json.RawMessage("null"), + }) + } + + return json.Marshal(guildScheduledEventParams(p)) +} + +// GuildScheduledEventEntityMetadata holds additional metadata for guild scheduled event. +type GuildScheduledEventEntityMetadata struct { + // location of the event (1-100 characters) + // required for events with 'entity_type': EXTERNAL + Location string `json:"location"` +} + +// GuildScheduledEventPrivacyLevel is the privacy level of a scheduled event. +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level +type GuildScheduledEventPrivacyLevel int + +const ( + // GuildScheduledEventPrivacyLevelGuildOnly makes the scheduled + // event is only accessible to guild members + GuildScheduledEventPrivacyLevelGuildOnly GuildScheduledEventPrivacyLevel = 2 +) + +// GuildScheduledEventStatus is the status of a scheduled event +// Valid Guild Scheduled Event Status Transitions : +// SCHEDULED --> ACTIVE --> COMPLETED +// SCHEDULED --> CANCELED +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status +type GuildScheduledEventStatus int + +const ( + // GuildScheduledEventStatusScheduled represents the current event is in scheduled state + GuildScheduledEventStatusScheduled = 1 + // GuildScheduledEventStatusActive represents the current event is in active state + GuildScheduledEventStatusActive = 2 + // GuildScheduledEventStatusCompleted represents the current event is in completed state + GuildScheduledEventStatusCompleted = 3 + // GuildScheduledEventStatusCanceled represents the current event is in canceled state + GuildScheduledEventStatusCanceled = 4 +) + +// GuildScheduledEventEntityType is the type of entity associated with a guild scheduled event. +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types +type GuildScheduledEventEntityType int + +const ( + // GuildScheduledEventEntityTypeStageInstance represents a stage channel + GuildScheduledEventEntityTypeStageInstance = 1 + // GuildScheduledEventEntityTypeVoice represents a voice channel + GuildScheduledEventEntityTypeVoice = 2 + // GuildScheduledEventEntityTypeExternal represents an external event + GuildScheduledEventEntityTypeExternal = 3 +) + +// GuildScheduledEventUser is a user subscribed to a scheduled event. +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-user-object +type GuildScheduledEventUser struct { + GuildScheduledEventID string `json:"guild_scheduled_event_id"` + User *User `json:"user"` + Member *Member `json:"member"` +} + +// A GuildTemplate represents +type GuildTemplate struct { + // The unique code for the guild template + Code string `json:"code"` + + // The name of the template + Name string `json:"name"` + + // The description for the template + Description string `json:"description"` + + // The number of times this template has been used + UsageCount string `json:"usage_count"` + + // The ID of the user who created the template + CreatorID string `json:"creator_id"` + + // The user who created the template + Creator *User `json:"creator"` + + // The timestamp of when the template was created + CreatedAt time.Time `json:"created_at"` + + // The timestamp of when the template was last synced + UpdatedAt time.Time `json:"updated_at"` + + // The ID of the guild the template was based on + SourceGuildID string `json:"source_guild_id"` + + // The guild 'snapshot' this template contains + SerializedSourceGuild *Guild `json:"serialized_source_guild"` + + // Whether the template has unsynced changes + IsDirty bool `json:"is_dirty"` +} + +// MessageNotifications is the notification level for a guild +// https://discord.com/developers/docs/resources/guild#guild-object-default-message-notification-level +type MessageNotifications int + +// Block containing known MessageNotifications values +const ( + MessageNotificationsAllMessages MessageNotifications = 0 + MessageNotificationsOnlyMentions MessageNotifications = 1 +) + +// SystemChannelFlag is the type of flags in the system channel (see SystemChannelFlag* consts) +// https://discord.com/developers/docs/resources/guild#guild-object-system-channel-flags +type SystemChannelFlag int + +// Block containing known SystemChannelFlag values +const ( + SystemChannelFlagsSuppressJoin SystemChannelFlag = 1 << 0 + SystemChannelFlagsSuppressPremium SystemChannelFlag = 1 << 1 +) + +// IconURL returns a URL to the guild's icon. +func (g *Guild) IconURL() string { + if g.Icon == "" { + return "" + } + + if strings.HasPrefix(g.Icon, "a_") { + return EndpointGuildIconAnimated(g.ID, g.Icon) + } + + return EndpointGuildIcon(g.ID, g.Icon) +} + +// BannerURL returns a URL to the guild's banner. +func (g *Guild) BannerURL() string { + if g.Banner == "" { + return "" + } + return EndpointGuildBanner(g.ID, g.Banner) +} + +// A UserGuild holds a brief version of a Guild +type UserGuild struct { + ID string `json:"id"` + Name string `json:"name"` + Icon string `json:"icon"` + Owner bool `json:"owner"` + Permissions int64 `json:"permissions,string"` +} + +// A GuildParams stores all the data needed to update discord guild settings +type GuildParams struct { + Name string `json:"name,omitempty"` + Region string `json:"region,omitempty"` + VerificationLevel *VerificationLevel `json:"verification_level,omitempty"` + DefaultMessageNotifications int `json:"default_message_notifications,omitempty"` // TODO: Separate type? + AfkChannelID string `json:"afk_channel_id,omitempty"` + AfkTimeout int `json:"afk_timeout,omitempty"` + Icon string `json:"icon,omitempty"` + OwnerID string `json:"owner_id,omitempty"` + Splash string `json:"splash,omitempty"` + Banner string `json:"banner,omitempty"` +} + +// A Role stores information about Discord guild member roles. +type Role struct { + // The ID of the role. + ID string `json:"id"` + + // The name of the role. + Name string `json:"name"` + + // Whether this role is managed by an integration, and + // thus cannot be manually added to, or taken from, members. + Managed bool `json:"managed"` + + // Whether this role is mentionable. + Mentionable bool `json:"mentionable"` + + // Whether this role is hoisted (shows up separately in member list). + Hoist bool `json:"hoist"` + + // The hex color of this role. + Color int `json:"color"` + + // The position of this role in the guild's role hierarchy. + Position int `json:"position"` + + // The permissions of the role on the guild (doesn't include channel overrides). + // This is a combination of bit masks; the presence of a certain permission can + // be checked by performing a bitwise AND between this int and the permission. + Permissions int64 `json:"permissions,string"` +} + +// Mention returns a string which mentions the role +func (r *Role) Mention() string { + return fmt.Sprintf("<@&%s>", r.ID) +} + +// Roles are a collection of Role +type Roles []*Role + +func (r Roles) Len() int { + return len(r) +} + +func (r Roles) Less(i, j int) bool { + return r[i].Position > r[j].Position +} + +func (r Roles) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +// A VoiceState stores the voice states of Guilds +type VoiceState struct { + UserID string `json:"user_id"` + SessionID string `json:"session_id"` + ChannelID string `json:"channel_id"` + GuildID string `json:"guild_id"` + Suppress bool `json:"suppress"` + SelfMute bool `json:"self_mute"` + SelfDeaf bool `json:"self_deaf"` + Mute bool `json:"mute"` + Deaf bool `json:"deaf"` +} + +// A Presence stores the online, offline, or idle and game status of Guild members. +type Presence struct { + User *User `json:"user"` + Status Status `json:"status"` + Activities []*Activity `json:"activities"` + Since *int `json:"since"` +} + +// A TimeStamps struct contains start and end times used in the rich presence "playing .." Game +type TimeStamps struct { + EndTimestamp int64 `json:"end,omitempty"` + StartTimestamp int64 `json:"start,omitempty"` +} + +// UnmarshalJSON unmarshals JSON into TimeStamps struct +func (t *TimeStamps) UnmarshalJSON(b []byte) error { + temp := struct { + End float64 `json:"end,omitempty"` + Start float64 `json:"start,omitempty"` + }{} + err := json.Unmarshal(b, &temp) + if err != nil { + return err + } + t.EndTimestamp = int64(temp.End) + t.StartTimestamp = int64(temp.Start) + return nil +} + +// An Assets struct contains assets and labels used in the rich presence "playing .." Game +type Assets struct { + LargeImageID string `json:"large_image,omitempty"` + SmallImageID string `json:"small_image,omitempty"` + LargeText string `json:"large_text,omitempty"` + SmallText string `json:"small_text,omitempty"` +} + +// A Member stores user information for Guild members. A guild +// member represents a certain user's presence in a guild. +type Member struct { + // The guild ID on which the member exists. + GuildID string `json:"guild_id"` + + // The time at which the member joined the guild. + JoinedAt time.Time `json:"joined_at"` + + // The nickname of the member, if they have one. + Nick string `json:"nick"` + + // Whether the member is deafened at a guild level. + Deaf bool `json:"deaf"` + + // Whether the member is muted at a guild level. + Mute bool `json:"mute"` + + // The hash of the avatar for the guild member, if any. + Avatar string `json:"avatar"` + + // The underlying user on which the member is based. + User *User `json:"user"` + + // A list of IDs of the roles which are possessed by the member. + Roles []string `json:"roles"` + + // When the user used their Nitro boost on the server + PremiumSince *time.Time `json:"premium_since"` + + // Is true while the member hasn't accepted the membership screen. + Pending bool `json:"pending"` + + // Total permissions of the member in the channel, including overrides, returned when in the interaction object. + Permissions int64 `json:"permissions,string"` + + // The time at which the member's timeout will expire. + // Time in the past or nil if the user is not timed out. + CommunicationDisabledUntil *time.Time `json:"communication_disabled_until"` +} + +// Mention creates a member mention +func (m *Member) Mention() string { + return "<@!" + m.User.ID + ">" +} + +// AvatarURL returns the URL of the member's avatar +// size: The size of the user's avatar as a power of two +// if size is an empty string, no size parameter will +// be added to the URL. +func (m *Member) AvatarURL(size string) string { + if m.Avatar == "" { + return m.User.AvatarURL(size) + } + // The default/empty avatar case should be handled by the above condition + return avatarURL(m.Avatar, "", EndpointGuildMemberAvatar(m.GuildID, m.User.ID, m.Avatar), + EndpointGuildMemberAvatarAnimated(m.GuildID, m.User.ID, m.Avatar), size) + +} + +// A Settings stores data for a specific users Discord client settings. +type Settings struct { + RenderEmbeds bool `json:"render_embeds"` + InlineEmbedMedia bool `json:"inline_embed_media"` + InlineAttachmentMedia bool `json:"inline_attachment_media"` + EnableTTSCommand bool `json:"enable_tts_command"` + MessageDisplayCompact bool `json:"message_display_compact"` + ShowCurrentGame bool `json:"show_current_game"` + ConvertEmoticons bool `json:"convert_emoticons"` + Locale string `json:"locale"` + Theme string `json:"theme"` + GuildPositions []string `json:"guild_positions"` + RestrictedGuilds []string `json:"restricted_guilds"` + FriendSourceFlags *FriendSourceFlags `json:"friend_source_flags"` + Status Status `json:"status"` + DetectPlatformAccounts bool `json:"detect_platform_accounts"` + DeveloperMode bool `json:"developer_mode"` +} + +// Status type definition +type Status string + +// Constants for Status with the different current available status +const ( + StatusOnline Status = "online" + StatusIdle Status = "idle" + StatusDoNotDisturb Status = "dnd" + StatusInvisible Status = "invisible" + StatusOffline Status = "offline" +) + +// FriendSourceFlags stores ... TODO :) +type FriendSourceFlags struct { + All bool `json:"all"` + MutualGuilds bool `json:"mutual_guilds"` + MutualFriends bool `json:"mutual_friends"` +} + +// A Relationship between the logged in user and Relationship.User +type Relationship struct { + User *User `json:"user"` + Type int `json:"type"` // 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req + ID string `json:"id"` +} + +// A TooManyRequests struct holds information received from Discord +// when receiving a HTTP 429 response. +type TooManyRequests struct { + Bucket string `json:"bucket"` + Message string `json:"message"` + RetryAfter time.Duration `json:"retry_after"` +} + +// UnmarshalJSON helps support translation of a milliseconds-based float +// into a time.Duration on TooManyRequests. +func (t *TooManyRequests) UnmarshalJSON(b []byte) error { + u := struct { + Bucket string `json:"bucket"` + Message string `json:"message"` + RetryAfter float64 `json:"retry_after"` + }{} + err := json.Unmarshal(b, &u) + if err != nil { + return err + } + + t.Bucket = u.Bucket + t.Message = u.Message + whole, frac := math.Modf(u.RetryAfter) + t.RetryAfter = time.Duration(whole)*time.Second + time.Duration(frac*1000)*time.Millisecond + return nil +} + +// A ReadState stores data on the read state of channels. +type ReadState struct { + MentionCount int `json:"mention_count"` + LastMessageID string `json:"last_message_id"` + ID string `json:"id"` +} + +// An Ack is used to ack messages +type Ack struct { + Token string `json:"token"` +} + +// A GuildRole stores data for guild roles. +type GuildRole struct { + Role *Role `json:"role"` + GuildID string `json:"guild_id"` +} + +// A GuildBan stores data for a guild ban. +type GuildBan struct { + Reason string `json:"reason"` + User *User `json:"user"` +} + +// A GuildEmbed stores data for a guild embed. +type GuildEmbed struct { + Enabled bool `json:"enabled"` + ChannelID string `json:"channel_id"` +} + +// A GuildAuditLog stores data for a guild audit log. +// https://discord.com/developers/docs/resources/audit-log#audit-log-object-audit-log-structure +type GuildAuditLog struct { + Webhooks []*Webhook `json:"webhooks,omitempty"` + Users []*User `json:"users,omitempty"` + AuditLogEntries []*AuditLogEntry `json:"audit_log_entries"` + Integrations []*Integration `json:"integrations"` +} + +// AuditLogEntry for a GuildAuditLog +// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-entry-structure +type AuditLogEntry struct { + TargetID string `json:"target_id"` + Changes []*AuditLogChange `json:"changes"` + UserID string `json:"user_id"` + ID string `json:"id"` + ActionType *AuditLogAction `json:"action_type"` + Options *AuditLogOptions `json:"options"` + Reason string `json:"reason"` +} + +// AuditLogChange for an AuditLogEntry +type AuditLogChange struct { + NewValue interface{} `json:"new_value"` + OldValue interface{} `json:"old_value"` + Key *AuditLogChangeKey `json:"key"` +} + +// AuditLogChangeKey value for AuditLogChange +// https://discord.com/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-key +type AuditLogChangeKey string + +// Block of valid AuditLogChangeKey +const ( + // AuditLogChangeKeyAfkChannelID is sent when afk channel changed (snowflake) - guild + AuditLogChangeKeyAfkChannelID AuditLogChangeKey = "afk_channel_id" + // AuditLogChangeKeyAfkTimeout is sent when afk timeout duration changed (int) - guild + AuditLogChangeKeyAfkTimeout AuditLogChangeKey = "afk_timeout" + // AuditLogChangeKeyAllow is sent when a permission on a text or voice channel was allowed for a role (string) - role + AuditLogChangeKeyAllow AuditLogChangeKey = "allow" + // AudirChangeKeyApplicationID is sent when application id of the added or removed webhook or bot (snowflake) - channel + AuditLogChangeKeyApplicationID AuditLogChangeKey = "application_id" + // AuditLogChangeKeyArchived is sent when thread was archived/unarchived (bool) - thread + AuditLogChangeKeyArchived AuditLogChangeKey = "archived" + // AuditLogChangeKeyAsset is sent when asset is changed (string) - sticker + AuditLogChangeKeyAsset AuditLogChangeKey = "asset" + // AuditLogChangeKeyAutoArchiveDuration is sent when auto archive duration changed (int) - thread + AuditLogChangeKeyAutoArchiveDuration AuditLogChangeKey = "auto_archive_duration" + // AuditLogChangeKeyAvailable is sent when availability of sticker changed (bool) - sticker + AuditLogChangeKeyAvailable AuditLogChangeKey = "available" + // AuditLogChangeKeyAvatarHash is sent when user avatar changed (string) - user + AuditLogChangeKeyAvatarHash AuditLogChangeKey = "avatar_hash" + // AuditLogChangeKeyBannerHash is sent when guild banner changed (string) - guild + AuditLogChangeKeyBannerHash AuditLogChangeKey = "banner_hash" + // AuditLogChangeKeyBitrate is sent when voice channel bitrate changed (int) - channel + AuditLogChangeKeyBitrate AuditLogChangeKey = "bitrate" + // AuditLogChangeKeyChannelID is sent when channel for invite code or guild scheduled event changed (snowflake) - invite or guild scheduled event + AuditLogChangeKeyChannelID AuditLogChangeKey = "channel_id" + // AuditLogChangeKeyCode is sent when invite code changed (string) - invite + AuditLogChangeKeyCode AuditLogChangeKey = "code" + // AuditLogChangeKeyColor is sent when role color changed (int) - role + AuditLogChangeKeyColor AuditLogChangeKey = "color" + // AuditLogChangeKeyCommunicationDisabledUntil is sent when member timeout state changed (ISO8601 timestamp) - member + AuditLogChangeKeyCommunicationDisabledUntil AuditLogChangeKey = "communication_disabled_until" + // AuditLogChangeKeyDeaf is sent when user server deafened/undeafened (bool) - member + AuditLogChangeKeyDeaf AuditLogChangeKey = "deaf" + // AuditLogChangeKeyDefaultAutoArchiveDuration is sent when default auto archive duration for newly created threads changed (int) - channel + AuditLogChangeKeyDefaultAutoArchiveDuration AuditLogChangeKey = "default_auto_archive_duration" + // AuditLogChangeKeyDefaultMessageNotification is sent when default message notification level changed (int) - guild + AuditLogChangeKeyDefaultMessageNotification AuditLogChangeKey = "default_message_notifications" + // AuditLogChangeKeyDeny is sent when a permission on a text or voice channel was denied for a role (string) - role + AuditLogChangeKeyDeny AuditLogChangeKey = "deny" + // AuditLogChangeKeyDescription is sent when description changed (string) - guild, sticker, or guild scheduled event + AuditLogChangeKeyDescription AuditLogChangeKey = "description" + // AuditLogChangeKeyDiscoverySplashHash is sent when discovery splash changed (string) - guild + AuditLogChangeKeyDiscoverySplashHash AuditLogChangeKey = "discovery_splash_hash" + // AuditLogChangeKeyEnableEmoticons is sent when integration emoticons enabled/disabled (bool) - integration + AuditLogChangeKeyEnableEmoticons AuditLogChangeKey = "enable_emoticons" + // AuditLogChangeKeyEntityType is sent when entity type of guild scheduled event was changed (int) - guild scheduled event + AuditLogChangeKeyEntityType AuditLogChangeKey = "entity_type" + // AuditLogChangeKeyExpireBehavior is sent when integration expiring subscriber behavior changed (int) - integration + AuditLogChangeKeyExpireBehavior AuditLogChangeKey = "expire_behavior" + // AuditLogChangeKeyExpireGracePeriod is sent when integration expire grace period changed (int) - integration + AuditLogChangeKeyExpireGracePeriod AuditLogChangeKey = "expire_grace_period" + // AuditLogChangeKeyExplicitContentFilter is sent when change in whose messages are scanned and deleted for explicit content in the server is made (int) - guild + AuditLogChangeKeyExplicitContentFilter AuditLogChangeKey = "explicit_content_filter" + // AuditLogChangeKeyFormatType is sent when format type of sticker changed (int - sticker format type) - sticker + AuditLogChangeKeyFormatType AuditLogChangeKey = "format_type" + // AuditLogChangeKeyGuildID is sent when guild sticker is in changed (snowflake) - sticker + AuditLogChangeKeyGuildID AuditLogChangeKey = "guild_id" + // AuditLogChangeKeyHoist is sent when role is now displayed/no longer displayed separate from online users (bool) - role + AuditLogChangeKeyHoist AuditLogChangeKey = "hoist" + // AuditLogChangeKeyIconHash is sent when icon changed (string) - guild or role + AuditLogChangeKeyIconHash AuditLogChangeKey = "icon_hash" + // AuditLogChangeKeyID is sent when the id of the changed entity - sometimes used in conjunction with other keys (snowflake) - any + AuditLogChangeKeyID AuditLogChangeKey = "id" + // AuditLogChangeKeyInvitable is sent when private thread is now invitable/uninvitable (bool) - thread + AuditLogChangeKeyInvitable AuditLogChangeKey = "invitable" + // AuditLogChangeKeyInviterID is sent when person who created invite code changed (snowflake) - invite + AuditLogChangeKeyInviterID AuditLogChangeKey = "inviter_id" + // AuditLogChangeKeyLocation is sent when channel id for guild scheduled event changed (string) - guild scheduled event + AuditLogChangeKeyLocation AuditLogChangeKey = "location" + // AuditLogChangeKeyLocked is sent when thread was locked/unlocked (bool) - thread + AuditLogChangeKeyLocked AuditLogChangeKey = "locked" + // AuditLogChangeKeyMaxAge is sent when invite code expiration time changed (int) - invite + AuditLogChangeKeyMaxAge AuditLogChangeKey = "max_age" + // AuditLogChangeKeyMaxUses is sent when max number of times invite code can be used changed (int) - invite + AuditLogChangeKeyMaxUses AuditLogChangeKey = "max_uses" + // AuditLogChangeKeyMentionable is sent when role is now mentionable/unmentionable (bool) - role + AuditLogChangeKeyMentionable AuditLogChangeKey = "mentionable" + // AuditLogChangeKeyMfaLevel is sent when two-factor auth requirement changed (int - mfa level) - guild + AuditLogChangeKeyMfaLevel AuditLogChangeKey = "mfa_level" + // AuditLogChangeKeyMute is sent when user server muted/unmuted (bool) - member + AuditLogChangeKeyMute AuditLogChangeKey = "mute" + // AuditLogChangeKeyName is sent when name changed (string) - any + AuditLogChangeKeyName AuditLogChangeKey = "name" + // AuditLogChangeKeyNick is sent when user nickname changed (string) - member + AuditLogChangeKeyNick AuditLogChangeKey = "nick" + // AuditLogChangeKeyNSFW is sent when channel nsfw restriction changed (bool) - channel + AuditLogChangeKeyNSFW AuditLogChangeKey = "nsfw" + // AuditLogChangeKeyOwnerID is sent when owner changed (snowflake) - guild + AuditLogChangeKeyOwnerID AuditLogChangeKey = "owner_id" + // AuditLogChangeKeyPermissionOverwrite is sent when permissions on a channel changed (array of channel overwrite objects) - channel + AuditLogChangeKeyPermissionOverwrite AuditLogChangeKey = "permission_overwrites" + // AuditLogChangeKeyPermissions is sent when permissions for a role changed (string) - role + AuditLogChangeKeyPermissions AuditLogChangeKey = "permissions" + // AuditLogChangeKeyPosition is sent when text or voice channel position changed (int) - channel + AuditLogChangeKeyPosition AuditLogChangeKey = "position" + // AuditLogChangeKeyPreferredLocale is sent when preferred locale changed (string) - guild + AuditLogChangeKeyPreferredLocale AuditLogChangeKey = "preferred_locale" + // AuditLogChangeKeyPrivacylevel is sent when privacy level of the stage instance changed (integer - privacy level) - stage instance or guild scheduled event + AuditLogChangeKeyPrivacylevel AuditLogChangeKey = "privacy_level" + // AuditLogChangeKeyPruneDeleteDays is sent when number of days after which inactive and role-unassigned members are kicked changed (int) - guild + AuditLogChangeKeyPruneDeleteDays AuditLogChangeKey = "prune_delete_days" + // AuditLogChangeKeyPulibUpdatesChannelID is sent when id of the public updates channel changed (snowflake) - guild + AuditLogChangeKeyPulibUpdatesChannelID AuditLogChangeKey = "public_updates_channel_id" + // AuditLogChangeKeyRateLimitPerUser is sent when amount of seconds a user has to wait before sending another message changed (int) - channel + AuditLogChangeKeyRateLimitPerUser AuditLogChangeKey = "rate_limit_per_user" + // AuditLogChangeKeyRegion is sent when region changed (string) - guild + AuditLogChangeKeyRegion AuditLogChangeKey = "region" + // AuditLogChangeKeyRulesChannelID is sent when id of the rules channel changed (snowflake) - guild + AuditLogChangeKeyRulesChannelID AuditLogChangeKey = "rules_channel_id" + // AuditLogChangeKeySplashHash is sent when invite splash page artwork changed (string) - guild + AuditLogChangeKeySplashHash AuditLogChangeKey = "splash_hash" + // AuditLogChangeKeyStatus is sent when status of guild scheduled event was changed (int - guild scheduled event status) - guild scheduled event + AuditLogChangeKeyStatus AuditLogChangeKey = "status" + // AuditLogChangeKeySystemChannelID is sent when id of the system channel changed (snowflake) - guild + AuditLogChangeKeySystemChannelID AuditLogChangeKey = "system_channel_id" + // AuditLogChangeKeyTags is sent when related emoji of sticker changed (string) - sticker + AuditLogChangeKeyTags AuditLogChangeKey = "tags" + // AuditLogChangeKeyTemporary is sent when invite code is now temporary or never expires (bool) - invite + AuditLogChangeKeyTemporary AuditLogChangeKey = "temporary" + // TODO: remove when compatibility is not required + AuditLogChangeKeyTempoary = AuditLogChangeKeyTemporary + // AuditLogChangeKeyTopic is sent when text channel topic or stage instance topic changed (string) - channel or stage instance + AuditLogChangeKeyTopic AuditLogChangeKey = "topic" + // AuditLogChangeKeyType is sent when type of entity created (int or string) - any + AuditLogChangeKeyType AuditLogChangeKey = "type" + // AuditLogChangeKeyUnicodeEmoji is sent when role unicode emoji changed (string) - role + AuditLogChangeKeyUnicodeEmoji AuditLogChangeKey = "unicode_emoji" + // AuditLogChangeKeyUserLimit is sent when new user limit in a voice channel set (int) - voice channel + AuditLogChangeKeyUserLimit AuditLogChangeKey = "user_limit" + // AuditLogChangeKeyUses is sent when number of times invite code used changed (int) - invite + AuditLogChangeKeyUses AuditLogChangeKey = "uses" + // AuditLogChangeKeyVanityURLCode is sent when guild invite vanity url changed (string) - guild + AuditLogChangeKeyVanityURLCode AuditLogChangeKey = "vanity_url_code" + // AuditLogChangeKeyVerificationLevel is sent when required verification level changed (int - verification level) - guild + AuditLogChangeKeyVerificationLevel AuditLogChangeKey = "verification_level" + // AuditLogChangeKeyWidgetChannelID is sent when channel id of the server widget changed (snowflake) - guild + AuditLogChangeKeyWidgetChannelID AuditLogChangeKey = "widget_channel_id" + // AuditLogChangeKeyWidgetEnabled is sent when server widget enabled/disabled (bool) - guild + AuditLogChangeKeyWidgetEnabled AuditLogChangeKey = "widget_enabled" + // AuditLogChangeKeyRoleAdd is sent when new role added (array of partial role objects) - guild + AuditLogChangeKeyRoleAdd AuditLogChangeKey = "$add" + // AuditLogChangeKeyRoleRemove is sent when role removed (array of partial role objects) - guild + AuditLogChangeKeyRoleRemove AuditLogChangeKey = "$remove" +) + +// AuditLogOptions optional data for the AuditLog +// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info +type AuditLogOptions struct { + DeleteMemberDays string `json:"delete_member_days"` + MembersRemoved string `json:"members_removed"` + ChannelID string `json:"channel_id"` + MessageID string `json:"message_id"` + Count string `json:"count"` + ID string `json:"id"` + Type *AuditLogOptionsType `json:"type"` + RoleName string `json:"role_name"` +} + +// AuditLogOptionsType of the AuditLogOption +// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info +type AuditLogOptionsType string + +// Valid Types for AuditLogOptionsType +const ( + AuditLogOptionsTypeMember AuditLogOptionsType = "member" + AuditLogOptionsTypeRole AuditLogOptionsType = "role" +) + +// AuditLogAction is the Action of the AuditLog (see AuditLogAction* consts) +// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events +type AuditLogAction int + +// Block contains Discord Audit Log Action Types +const ( + AuditLogActionGuildUpdate AuditLogAction = 1 + + AuditLogActionChannelCreate AuditLogAction = 10 + AuditLogActionChannelUpdate AuditLogAction = 11 + AuditLogActionChannelDelete AuditLogAction = 12 + AuditLogActionChannelOverwriteCreate AuditLogAction = 13 + AuditLogActionChannelOverwriteUpdate AuditLogAction = 14 + AuditLogActionChannelOverwriteDelete AuditLogAction = 15 + + AuditLogActionMemberKick AuditLogAction = 20 + AuditLogActionMemberPrune AuditLogAction = 21 + AuditLogActionMemberBanAdd AuditLogAction = 22 + AuditLogActionMemberBanRemove AuditLogAction = 23 + AuditLogActionMemberUpdate AuditLogAction = 24 + AuditLogActionMemberRoleUpdate AuditLogAction = 25 + AuditLogActionMemberMove AuditLogAction = 26 + AuditLogActionMemberDisconnect AuditLogAction = 27 + AuditLogActionBotAdd AuditLogAction = 28 + + AuditLogActionRoleCreate AuditLogAction = 30 + AuditLogActionRoleUpdate AuditLogAction = 31 + AuditLogActionRoleDelete AuditLogAction = 32 + + AuditLogActionInviteCreate AuditLogAction = 40 + AuditLogActionInviteUpdate AuditLogAction = 41 + AuditLogActionInviteDelete AuditLogAction = 42 + + AuditLogActionWebhookCreate AuditLogAction = 50 + AuditLogActionWebhookUpdate AuditLogAction = 51 + AuditLogActionWebhookDelete AuditLogAction = 52 + + AuditLogActionEmojiCreate AuditLogAction = 60 + AuditLogActionEmojiUpdate AuditLogAction = 61 + AuditLogActionEmojiDelete AuditLogAction = 62 + + AuditLogActionMessageDelete AuditLogAction = 72 + AuditLogActionMessageBulkDelete AuditLogAction = 73 + AuditLogActionMessagePin AuditLogAction = 74 + AuditLogActionMessageUnpin AuditLogAction = 75 + + AuditLogActionIntegrationCreate AuditLogAction = 80 + AuditLogActionIntegrationUpdate AuditLogAction = 81 + AuditLogActionIntegrationDelete AuditLogAction = 82 + AuditLogActionStageInstanceCreate AuditLogAction = 83 + AuditLogActionStageInstanceUpdate AuditLogAction = 84 + AuditLogActionStageInstanceDelete AuditLogAction = 85 + + AuditLogActionStickerCreate AuditLogAction = 90 + AuditLogActionStickerUpdate AuditLogAction = 91 + AuditLogActionStickerDelete AuditLogAction = 92 + + AuditLogGuildScheduledEventCreate AuditLogAction = 100 + AuditLogGuildScheduledEventUpdare AuditLogAction = 101 + AuditLogGuildScheduledEventDelete AuditLogAction = 102 + + AuditLogActionThreadCreate AuditLogAction = 110 + AuditLogActionThreadUpdate AuditLogAction = 111 + AuditLogActionThreadDelete AuditLogAction = 112 +) + +// A UserGuildSettingsChannelOverride stores data for a channel override for a users guild settings. +type UserGuildSettingsChannelOverride struct { + Muted bool `json:"muted"` + MessageNotifications int `json:"message_notifications"` + ChannelID string `json:"channel_id"` +} + +// A UserGuildSettings stores data for a users guild settings. +type UserGuildSettings struct { + SupressEveryone bool `json:"suppress_everyone"` + Muted bool `json:"muted"` + MobilePush bool `json:"mobile_push"` + MessageNotifications int `json:"message_notifications"` + GuildID string `json:"guild_id"` + ChannelOverrides []*UserGuildSettingsChannelOverride `json:"channel_overrides"` +} + +// A UserGuildSettingsEdit stores data for editing UserGuildSettings +type UserGuildSettingsEdit struct { + SupressEveryone bool `json:"suppress_everyone"` + Muted bool `json:"muted"` + MobilePush bool `json:"mobile_push"` + MessageNotifications int `json:"message_notifications"` + ChannelOverrides map[string]*UserGuildSettingsChannelOverride `json:"channel_overrides"` +} + +// An APIErrorMessage is an api error message returned from discord +type APIErrorMessage struct { + Code int `json:"code"` + Message string `json:"message"` +} + +// MessageReaction stores the data for a message reaction. +type MessageReaction struct { + UserID string `json:"user_id"` + MessageID string `json:"message_id"` + Emoji Emoji `json:"emoji"` + ChannelID string `json:"channel_id"` + GuildID string `json:"guild_id,omitempty"` +} + +// GatewayBotResponse stores the data for the gateway/bot response +type GatewayBotResponse struct { + URL string `json:"url"` + Shards int `json:"shards"` + SessionStartLimit SessionInformation `json:"session_start_limit"` +} + +// SessionInformation provides the information for max concurrency sharding +type SessionInformation struct { + Total int `json:"total,omitempty"` + Remaining int `json:"remaining,omitempty"` + ResetAfter int `json:"reset_after,omitempty"` + MaxConcurrency int `json:"max_concurrency,omitempty"` +} + +// GatewayStatusUpdate is sent by the client to indicate a presence or status update +// https://discord.com/developers/docs/topics/gateway#update-status-gateway-status-update-structure +type GatewayStatusUpdate struct { + Since int `json:"since"` + Game Activity `json:"game"` + Status string `json:"status"` + AFK bool `json:"afk"` +} + +// Activity defines the Activity sent with GatewayStatusUpdate +// https://discord.com/developers/docs/topics/gateway#activity-object +type Activity struct { + Name string `json:"name"` + Type ActivityType `json:"type"` + URL string `json:"url,omitempty"` + CreatedAt time.Time `json:"created_at"` + ApplicationID string `json:"application_id,omitempty"` + State string `json:"state,omitempty"` + Details string `json:"details,omitempty"` + Timestamps TimeStamps `json:"timestamps,omitempty"` + Emoji Emoji `json:"emoji,omitempty"` + Party Party `json:"party,omitempty"` + Assets Assets `json:"assets,omitempty"` + Secrets Secrets `json:"secrets,omitempty"` + Instance bool `json:"instance,omitempty"` + Flags int `json:"flags,omitempty"` +} + +// UnmarshalJSON is a custom unmarshaljson to make CreatedAt a time.Time instead of an int +func (activity *Activity) UnmarshalJSON(b []byte) error { + temp := struct { + Name string `json:"name"` + Type ActivityType `json:"type"` + URL string `json:"url,omitempty"` + CreatedAt int64 `json:"created_at"` + ApplicationID string `json:"application_id,omitempty"` + State string `json:"state,omitempty"` + Details string `json:"details,omitempty"` + Timestamps TimeStamps `json:"timestamps,omitempty"` + Emoji Emoji `json:"emoji,omitempty"` + Party Party `json:"party,omitempty"` + Assets Assets `json:"assets,omitempty"` + Secrets Secrets `json:"secrets,omitempty"` + Instance bool `json:"instance,omitempty"` + Flags int `json:"flags,omitempty"` + }{} + err := json.Unmarshal(b, &temp) + if err != nil { + return err + } + activity.CreatedAt = time.Unix(0, temp.CreatedAt*1000000) + activity.ApplicationID = temp.ApplicationID + activity.Assets = temp.Assets + activity.Details = temp.Details + activity.Emoji = temp.Emoji + activity.Flags = temp.Flags + activity.Instance = temp.Instance + activity.Name = temp.Name + activity.Party = temp.Party + activity.Secrets = temp.Secrets + activity.State = temp.State + activity.Timestamps = temp.Timestamps + activity.Type = temp.Type + activity.URL = temp.URL + return nil +} + +// Party defines the Party field in the Activity struct +// https://discord.com/developers/docs/topics/gateway#activity-object +type Party struct { + ID string `json:"id,omitempty"` + Size []int `json:"size,omitempty"` +} + +// Secrets defines the Secrets field for the Activity struct +// https://discord.com/developers/docs/topics/gateway#activity-object +type Secrets struct { + Join string `json:"join,omitempty"` + Spectate string `json:"spectate,omitempty"` + Match string `json:"match,omitempty"` +} + +// ActivityType is the type of Activity (see ActivityType* consts) in the Activity struct +// https://discord.com/developers/docs/topics/gateway#activity-object-activity-types +type ActivityType int + +// Valid ActivityType values +const ( + ActivityTypeGame ActivityType = 0 + ActivityTypeStreaming ActivityType = 1 + ActivityTypeListening ActivityType = 2 + ActivityTypeWatching ActivityType = 3 + ActivityTypeCustom ActivityType = 4 + ActivityTypeCompeting ActivityType = 5 +) + +// Identify is sent during initial handshake with the discord gateway. +// https://discord.com/developers/docs/topics/gateway#identify +type Identify struct { + Token string `json:"token"` + Properties IdentifyProperties `json:"properties"` + Compress bool `json:"compress"` + LargeThreshold int `json:"large_threshold"` + Shard *[2]int `json:"shard,omitempty"` + Presence GatewayStatusUpdate `json:"presence,omitempty"` + GuildSubscriptions bool `json:"guild_subscriptions"` + Intents Intent `json:"intents"` +} + +// IdentifyProperties contains the "properties" portion of an Identify packet +// https://discord.com/developers/docs/topics/gateway#identify-identify-connection-properties +type IdentifyProperties struct { + OS string `json:"$os"` + Browser string `json:"$browser"` + Device string `json:"$device"` + Referer string `json:"$referer"` + ReferringDomain string `json:"$referring_domain"` +} + +// Constants for the different bit offsets of text channel permissions +const ( + // Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels + PermissionReadMessages = 0x0000000000000400 + PermissionSendMessages = 0x0000000000000800 + PermissionSendTTSMessages = 0x0000000000001000 + PermissionManageMessages = 0x0000000000002000 + PermissionEmbedLinks = 0x0000000000004000 + PermissionAttachFiles = 0x0000000000008000 + PermissionReadMessageHistory = 0x0000000000010000 + PermissionMentionEveryone = 0x0000000000020000 + PermissionUseExternalEmojis = 0x0000000000040000 + PermissionUseSlashCommands = 0x0000000080000000 + PermissionManageThreads = 0x0000000400000000 + PermissionCreatePublicThreads = 0x0000000800000000 + PermissionCreatePrivateThreads = 0x0000001000000000 + PermissionSendMessagesInThreads = 0x0000004000000000 +) + +// Constants for the different bit offsets of voice permissions +const ( + PermissionVoicePrioritySpeaker = 0x0000000000000100 + PermissionVoiceStreamVideo = 0x0000000000000200 + PermissionVoiceConnect = 0x0000000000100000 + PermissionVoiceSpeak = 0x0000000000200000 + PermissionVoiceMuteMembers = 0x0000000000400000 + PermissionVoiceDeafenMembers = 0x0000000000800000 + PermissionVoiceMoveMembers = 0x0000000001000000 + PermissionVoiceUseVAD = 0x0000000002000000 + PermissionVoiceRequestToSpeak = 0x0000000100000000 +) + +// Constants for general management. +const ( + PermissionChangeNickname = 0x0000000004000000 + PermissionManageNicknames = 0x0000000008000000 + PermissionManageRoles = 0x0000000010000000 + PermissionManageWebhooks = 0x0000000020000000 + PermissionManageEmojis = 0x0000000040000000 +) + +// Constants for the different bit offsets of general permissions +const ( + PermissionCreateInstantInvite = 0x0000000000000001 + PermissionKickMembers = 0x0000000000000002 + PermissionBanMembers = 0x0000000000000004 + PermissionAdministrator = 0x0000000000000008 + PermissionManageChannels = 0x0000000000000010 + PermissionManageServer = 0x0000000000000020 + PermissionAddReactions = 0x0000000000000040 + PermissionViewAuditLogs = 0x0000000000000080 + PermissionViewChannel = 0x0000000000000400 + PermissionViewGuildInsights = 0x0000000000080000 + PermissionModerateMembers = 0x0000010000000000 + + PermissionAllText = PermissionViewChannel | + PermissionSendMessages | + PermissionSendTTSMessages | + PermissionManageMessages | + PermissionEmbedLinks | + PermissionAttachFiles | + PermissionReadMessageHistory | + PermissionMentionEveryone + PermissionAllVoice = PermissionViewChannel | + PermissionVoiceConnect | + PermissionVoiceSpeak | + PermissionVoiceMuteMembers | + PermissionVoiceDeafenMembers | + PermissionVoiceMoveMembers | + PermissionVoiceUseVAD | + PermissionVoicePrioritySpeaker + PermissionAllChannel = PermissionAllText | + PermissionAllVoice | + PermissionCreateInstantInvite | + PermissionManageRoles | + PermissionManageChannels | + PermissionAddReactions | + PermissionViewAuditLogs + PermissionAll = PermissionAllChannel | + PermissionKickMembers | + PermissionBanMembers | + PermissionManageServer | + PermissionAdministrator | + PermissionManageWebhooks | + PermissionManageEmojis +) + +// Block contains Discord JSON Error Response codes +const ( + ErrCodeGeneralError = 0 + + ErrCodeUnknownAccount = 10001 + ErrCodeUnknownApplication = 10002 + ErrCodeUnknownChannel = 10003 + ErrCodeUnknownGuild = 10004 + ErrCodeUnknownIntegration = 10005 + ErrCodeUnknownInvite = 10006 + ErrCodeUnknownMember = 10007 + ErrCodeUnknownMessage = 10008 + ErrCodeUnknownOverwrite = 10009 + ErrCodeUnknownProvider = 10010 + ErrCodeUnknownRole = 10011 + ErrCodeUnknownToken = 10012 + ErrCodeUnknownUser = 10013 + ErrCodeUnknownEmoji = 10014 + ErrCodeUnknownWebhook = 10015 + ErrCodeUnknownWebhookService = 10016 + ErrCodeUnknownSession = 10020 + ErrCodeUnknownBan = 10026 + ErrCodeUnknownSKU = 10027 + ErrCodeUnknownStoreListing = 10028 + ErrCodeUnknownEntitlement = 10029 + ErrCodeUnknownBuild = 10030 + ErrCodeUnknownLobby = 10031 + ErrCodeUnknownBranch = 10032 + ErrCodeUnknownStoreDirectoryLayout = 10033 + ErrCodeUnknownRedistributable = 10036 + ErrCodeUnknownGiftCode = 10038 + ErrCodeUnknownStream = 10049 + ErrCodeUnknownPremiumServerSubscribeCooldown = 10050 + ErrCodeUnknownGuildTemplate = 10057 + ErrCodeUnknownDiscoveryCategory = 10059 + ErrCodeUnknownSticker = 10060 + ErrCodeUnknownInteraction = 10062 + ErrCodeUnknownApplicationCommand = 10063 + ErrCodeUnknownApplicationCommandPermissions = 10066 + ErrCodeUnknownStageInstance = 10067 + ErrCodeUnknownGuildMemberVerificationForm = 10068 + ErrCodeUnknownGuildWelcomeScreen = 10069 + ErrCodeUnknownGuildScheduledEvent = 10070 + ErrCodeUnknownGuildScheduledEventUser = 10071 + + ErrCodeBotsCannotUseEndpoint = 20001 + ErrCodeOnlyBotsCanUseEndpoint = 20002 + ErrCodeExplicitContentCannotBeSentToTheDesiredRecipients = 20009 + ErrCodeYouAreNotAuthorizedToPerformThisActionOnThisApplication = 20012 + ErrCodeThisActionCannotBePerformedDueToSlowmodeRateLimit = 20016 + ErrCodeOnlyTheOwnerOfThisAccountCanPerformThisAction = 20018 + ErrCodeMessageCannotBeEditedDueToAnnouncementRateLimits = 20022 + ErrCodeChannelHasHitWriteRateLimit = 20028 + ErrCodeTheWriteActionYouArePerformingOnTheServerHasHitTheWriteRateLimit = 20029 + ErrCodeStageTopicContainsNotAllowedWordsForPublicStages = 20031 + ErrCodeGuildPremiumSubscriptionLevelTooLow = 20035 + + ErrCodeMaximumGuildsReached = 30001 + ErrCodeMaximumPinsReached = 30003 + ErrCodeMaximumNumberOfRecipientsReached = 30004 + ErrCodeMaximumGuildRolesReached = 30005 + ErrCodeMaximumNumberOfWebhooksReached = 30007 + ErrCodeMaximumNumberOfEmojisReached = 30008 + ErrCodeTooManyReactions = 30010 + ErrCodeMaximumNumberOfGuildChannelsReached = 30013 + ErrCodeMaximumNumberOfAttachmentsInAMessageReached = 30015 + ErrCodeMaximumNumberOfInvitesReached = 30016 + ErrCodeMaximumNumberOfAnimatedEmojisReached = 30018 + ErrCodeMaximumNumberOfServerMembersReached = 30019 + ErrCodeMaximumNumberOfGuildDiscoverySubcategoriesReached = 30030 + ErrCodeGuildAlreadyHasATemplate = 30031 + ErrCodeMaximumNumberOfThreadParticipantsReached = 30033 + ErrCodeMaximumNumberOfBansForNonGuildMembersHaveBeenExceeded = 30035 + ErrCodeMaximumNumberOfBansFetchesHasBeenReached = 30037 + ErrCodeMaximumNumberOfUncompletedGuildScheduledEventsReached = 30038 + ErrCodeMaximumNumberOfStickersReached = 30039 + ErrCodeMaximumNumberOfPruneRequestsHasBeenReached = 30040 + ErrCodeMaximumNumberOfGuildWidgetSettingsUpdatesHasBeenReached = 30042 + ErrCodeMaximumNumberOfEditsToMessagesOlderThanOneHourReached = 30046 + + ErrCodeUnauthorized = 40001 + ErrCodeActionRequiredVerifiedAccount = 40002 + ErrCodeOpeningDirectMessagesTooFast = 40003 + ErrCodeSendMessagesHasBeenTemporarilyDisabled = 40004 + ErrCodeRequestEntityTooLarge = 40005 + ErrCodeFeatureTemporarilyDisabledServerSide = 40006 + ErrCodeUserIsBannedFromThisGuild = 40007 + ErrCodeTargetIsNotConnectedToVoice = 40032 + ErrCodeMessageAlreadyCrossposted = 40033 + ErrCodeAnApplicationWithThatNameAlreadyExists = 40041 + ErrCodeInteractionHasAlreadyBeenAcknowledged = 40060 + + ErrCodeMissingAccess = 50001 + ErrCodeInvalidAccountType = 50002 + ErrCodeCannotExecuteActionOnDMChannel = 50003 + ErrCodeEmbedDisabled = 50004 + ErrCodeGuildWidgetDisabled = 50004 + ErrCodeCannotEditFromAnotherUser = 50005 + ErrCodeCannotSendEmptyMessage = 50006 + ErrCodeCannotSendMessagesToThisUser = 50007 + ErrCodeCannotSendMessagesInVoiceChannel = 50008 + ErrCodeChannelVerificationLevelTooHigh = 50009 + ErrCodeOAuth2ApplicationDoesNotHaveBot = 50010 + ErrCodeOAuth2ApplicationLimitReached = 50011 + ErrCodeInvalidOAuthState = 50012 + ErrCodeMissingPermissions = 50013 + ErrCodeInvalidAuthenticationToken = 50014 + ErrCodeTooFewOrTooManyMessagesToDelete = 50016 + ErrCodeCanOnlyPinMessageToOriginatingChannel = 50019 + ErrCodeInviteCodeWasEitherInvalidOrTaken = 50020 + ErrCodeCannotExecuteActionOnSystemMessage = 50021 + ErrCodeCannotExecuteActionOnThisChannelType = 50024 + ErrCodeInvalidOAuth2AccessTokenProvided = 50025 + ErrCodeMissingRequiredOAuth2Scope = 50026 + ErrCodeInvalidWebhookTokenProvided = 50027 + ErrCodeInvalidRole = 50028 + ErrCodeInvalidRecipients = 50033 + ErrCodeMessageProvidedTooOldForBulkDelete = 50034 + ErrCodeInvalidFormBody = 50035 + ErrCodeInviteAcceptedToGuildApplicationsBotNotIn = 50036 + ErrCodeInvalidAPIVersionProvided = 50041 + ErrCodeFileUploadedExceedsTheMaximumSize = 50045 + ErrCodeInvalidFileUploaded = 50046 + ErrCodeInvalidGuild = 50055 + ErrCodeInvalidMessageType = 50068 + ErrCodeCannotDeleteAChannelRequiredForCommunityGuilds = 50074 + ErrCodeInvalidStickerSent = 50081 + ErrCodePerformedOperationOnArchivedThread = 50083 + ErrCodeBeforeValueIsEarlierThanThreadCreationDate = 50085 + ErrCodeCommunityServerChannelsMustBeTextChannels = 50086 + ErrCodeThisServerIsNotAvailableInYourLocation = 50095 + ErrCodeThisServerNeedsMonetizationEnabledInOrderToPerformThisAction = 50097 + ErrCodeThisServerNeedsMoreBoostsToPerformThisAction = 50101 + ErrCodeTheRequestBodyContainsInvalidJSON = 50109 + + ErrCodeNoUsersWithDiscordTagExist = 80004 + + ErrCodeReactionBlocked = 90001 + + ErrCodeAPIResourceIsCurrentlyOverloaded = 130000 + + ErrCodeTheStageIsAlreadyOpen = 150006 + + ErrCodeCannotReplyWithoutPermissionToReadMessageHistory = 160002 + ErrCodeThreadAlreadyCreatedForThisMessage = 160004 + ErrCodeThreadIsLocked = 160005 + ErrCodeMaximumNumberOfActiveThreadsReached = 160006 + ErrCodeMaximumNumberOfActiveAnnouncementThreadsReached = 160007 + + ErrCodeInvalidJSONForUploadedLottieFile = 170001 + ErrCodeUploadedLottiesCannotContainRasterizedImages = 170002 + ErrCodeStickerMaximumFramerateExceeded = 170003 + ErrCodeStickerFrameCountExceedsMaximumOfOneThousandFrames = 170004 + ErrCodeLottieAnimationMaximumDimensionsExceeded = 170005 + ErrCodeStickerFrameRateOutOfRange = 170006 + ErrCodeStickerAnimationDurationExceedsMaximumOfFiveSeconds = 170007 + + ErrCodeCannotUpdateAFinishedEvent = 180000 + ErrCodeFailedToCreateStageNeededForStageEvent = 180002 +) + +// Intent is the type of a Gateway Intent +// https://discord.com/developers/docs/topics/gateway#gateway-intents +type Intent int + +// Constants for the different bit offsets of intents +const ( + IntentGuilds Intent = 1 << 0 + IntentGuildMembers Intent = 1 << 1 + IntentGuildBans Intent = 1 << 2 + IntentGuildEmojis Intent = 1 << 3 + IntentGuildIntegrations Intent = 1 << 4 + IntentGuildWebhooks Intent = 1 << 5 + IntentGuildInvites Intent = 1 << 6 + IntentGuildVoiceStates Intent = 1 << 7 + IntentGuildPresences Intent = 1 << 8 + IntentGuildMessages Intent = 1 << 9 + IntentGuildMessageReactions Intent = 1 << 10 + IntentGuildMessageTyping Intent = 1 << 11 + IntentDirectMessages Intent = 1 << 12 + IntentDirectMessageReactions Intent = 1 << 13 + IntentDirectMessageTyping Intent = 1 << 14 + IntentMessageContent Intent = 1 << 15 + IntentGuildScheduledEvents Intent = 1 << 16 + + // TODO: remove when compatibility is not needed + + IntentsGuilds Intent = 1 << 0 + IntentsGuildMembers Intent = 1 << 1 + IntentsGuildBans Intent = 1 << 2 + IntentsGuildEmojis Intent = 1 << 3 + IntentsGuildIntegrations Intent = 1 << 4 + IntentsGuildWebhooks Intent = 1 << 5 + IntentsGuildInvites Intent = 1 << 6 + IntentsGuildVoiceStates Intent = 1 << 7 + IntentsGuildPresences Intent = 1 << 8 + IntentsGuildMessages Intent = 1 << 9 + IntentsGuildMessageReactions Intent = 1 << 10 + IntentsGuildMessageTyping Intent = 1 << 11 + IntentsDirectMessages Intent = 1 << 12 + IntentsDirectMessageReactions Intent = 1 << 13 + IntentsDirectMessageTyping Intent = 1 << 14 + IntentsMessageContent Intent = 1 << 15 + IntentsGuildScheduledEvents Intent = 1 << 16 + + IntentsAllWithoutPrivileged = IntentGuilds | + IntentGuildBans | + IntentGuildEmojis | + IntentGuildIntegrations | + IntentGuildWebhooks | + IntentGuildInvites | + IntentGuildVoiceStates | + IntentGuildMessages | + IntentGuildMessageReactions | + IntentGuildMessageTyping | + IntentDirectMessages | + IntentDirectMessageReactions | + IntentDirectMessageTyping | + IntentGuildScheduledEvents + + IntentsAll = IntentsAllWithoutPrivileged | + IntentGuildMembers | + IntentGuildPresences | + IntentMessageContent + + IntentsNone Intent = 0 +) + +// MakeIntent used to help convert a gateway intent value for use in the Identify structure; +// this was useful to help support the use of a pointer type when intents were optional. +// This is now a no-op, and is not necessary to use. +func MakeIntent(intents Intent) Intent { + return intents +} diff --git a/vendor/github.com/matterbridge/discordgo/types.go b/vendor/github.com/bwmarrin/discordgo/types.go similarity index 79% rename from vendor/github.com/matterbridge/discordgo/types.go rename to vendor/github.com/bwmarrin/discordgo/types.go index c0ce0131..7f969aef 100644 --- a/vendor/github.com/matterbridge/discordgo/types.go +++ b/vendor/github.com/bwmarrin/discordgo/types.go @@ -12,18 +12,8 @@ package discordgo import ( "encoding/json" "net/http" - "time" ) -// Timestamp stores a timestamp, as sent by the Discord API. -type Timestamp string - -// Parse parses a timestamp string into a time.Time object. -// The only time this can fail is if Discord changes their timestamp format. -func (t Timestamp) Parse() (time.Time, error) { - return time.Parse(time.RFC3339, string(t)) -} - // RESTError stores error information about a request with a bad response code. // Message is not always present, there are cases where api calls can fail // without returning a json message. diff --git a/vendor/github.com/matterbridge/discordgo/user.go b/vendor/github.com/bwmarrin/discordgo/user.go similarity index 61% rename from vendor/github.com/matterbridge/discordgo/user.go rename to vendor/github.com/bwmarrin/discordgo/user.go index b2894d5d..6c48bf25 100644 --- a/vendor/github.com/matterbridge/discordgo/user.go +++ b/vendor/github.com/bwmarrin/discordgo/user.go @@ -1,26 +1,25 @@ package discordgo -import "strings" - // UserFlags is the flags of "user" (see UserFlags* consts) // https://discord.com/developers/docs/resources/user#user-object-user-flags type UserFlags int // Valid UserFlags values const ( - UserFlagDiscordEmployee UserFlags = 1 << 0 - UserFlagDiscordPartner = 1 << 1 - UserFlagHypeSquadEvents = 1 << 2 - UserFlagBugHunterLevel1 = 1 << 3 - UserFlagHouseBravery = 1 << 6 - UserFlagHouseBrilliance = 1 << 7 - UserFlagHouseBalance = 1 << 8 - UserFlagEarlySupporter = 1 << 9 - UserFlagTeamUser = 1 << 10 - UserFlagSystem = 1 << 12 - UserFlagBugHunterLevel2 = 1 << 14 - UserFlagVerifiedBot = 1 << 16 - UserFlagVerifiedBotDeveloper = 1 << 17 + UserFlagDiscordEmployee UserFlags = 1 << 0 + UserFlagDiscordPartner UserFlags = 1 << 1 + UserFlagHypeSquadEvents UserFlags = 1 << 2 + UserFlagBugHunterLevel1 UserFlags = 1 << 3 + UserFlagHouseBravery UserFlags = 1 << 6 + UserFlagHouseBrilliance UserFlags = 1 << 7 + UserFlagHouseBalance UserFlags = 1 << 8 + UserFlagEarlySupporter UserFlags = 1 << 9 + UserFlagTeamUser UserFlags = 1 << 10 + UserFlagSystem UserFlags = 1 << 12 + UserFlagBugHunterLevel2 UserFlags = 1 << 14 + UserFlagVerifiedBot UserFlags = 1 << 16 + UserFlagVerifiedBotDeveloper UserFlags = 1 << 17 + UserFlagDiscordCertifiedModerator UserFlags = 1 << 18 ) // A User stores all data for an individual Discord user. @@ -55,6 +54,12 @@ type User struct { // Whether the user has multi-factor authentication enabled. MFAEnabled bool `json:"mfa_enabled"` + // The hash of the user's banner image. + Banner string `json:"banner"` + + // User's banner color, encoded as an integer representation of hexadecimal color code + AccentColor int `json:"accent_color"` + // Whether the user is a bot. Bot bool `json:"bot"` @@ -90,17 +95,13 @@ func (u *User) Mention() string { // if size is an empty string, no size parameter will // be added to the URL. func (u *User) AvatarURL(size string) string { - var URL string - if u.Avatar == "" { - URL = EndpointDefaultUserAvatar(u.Discriminator) - } else if strings.HasPrefix(u.Avatar, "a_") { - URL = EndpointUserAvatarAnimated(u.ID, u.Avatar) - } else { - URL = EndpointUserAvatar(u.ID, u.Avatar) - } - - if size != "" { - return URL + "?size=" + size - } - return URL + return avatarURL(u.Avatar, EndpointDefaultUserAvatar(u.Discriminator), + EndpointUserAvatar(u.ID, u.Avatar), EndpointUserAvatarAnimated(u.ID, u.Avatar), size) +} + +// BannerURL returns the URL of the users's banner image. +// size: The size of the desired banner image as a power of two +// Image size can be any power of two between 16 and 4096. +func (u *User) BannerURL(size string) string { + return bannerURL(u.Banner, EndpointUserBanner(u.ID, u.Banner), EndpointUserBannerAnimated(u.ID, u.Banner), size) } diff --git a/vendor/github.com/bwmarrin/discordgo/util.go b/vendor/github.com/bwmarrin/discordgo/util.go new file mode 100644 index 00000000..62313033 --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/util.go @@ -0,0 +1,110 @@ +package discordgo + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "mime/multipart" + "net/textproto" + "strconv" + "strings" + "time" +) + +// SnowflakeTimestamp returns the creation time of a Snowflake ID relative to the creation of Discord. +func SnowflakeTimestamp(ID string) (t time.Time, err error) { + i, err := strconv.ParseInt(ID, 10, 64) + if err != nil { + return + } + timestamp := (i >> 22) + 1420070400000 + t = time.Unix(0, timestamp*1000000) + return +} + +// MultipartBodyWithJSON returns the contentType and body for a discord request +// data : The object to encode for payload_json in the multipart request +// files : Files to include in the request +func MultipartBodyWithJSON(data interface{}, files []*File) (requestContentType string, requestBody []byte, err error) { + body := &bytes.Buffer{} + bodywriter := multipart.NewWriter(body) + + payload, err := json.Marshal(data) + if err != nil { + return + } + + var p io.Writer + + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", `form-data; name="payload_json"`) + h.Set("Content-Type", "application/json") + + p, err = bodywriter.CreatePart(h) + if err != nil { + return + } + + if _, err = p.Write(payload); err != nil { + return + } + + for i, file := range files { + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file%d"; filename="%s"`, i, quoteEscaper.Replace(file.Name))) + contentType := file.ContentType + if contentType == "" { + contentType = "application/octet-stream" + } + h.Set("Content-Type", contentType) + + p, err = bodywriter.CreatePart(h) + if err != nil { + return + } + + if _, err = io.Copy(p, file.Reader); err != nil { + return + } + } + + err = bodywriter.Close() + if err != nil { + return + } + + return bodywriter.FormDataContentType(), body.Bytes(), nil +} + +func avatarURL(avatarHash, defaultAvatarURL, staticAvatarURL, animatedAvatarURL, size string) string { + var URL string + if avatarHash == "" { + URL = defaultAvatarURL + } else if strings.HasPrefix(avatarHash, "a_") { + URL = animatedAvatarURL + } else { + URL = staticAvatarURL + } + + if size != "" { + return URL + "?size=" + size + } + return URL +} + +func bannerURL(bannerHash, staticBannerURL, animatedBannerURL, size string) string { + var URL string + if bannerHash == "" { + return "" + } else if strings.HasPrefix(bannerHash, "a_") { + URL = animatedBannerURL + } else { + URL = staticBannerURL + } + + if size != "" { + return URL + "?size=" + size + } + return URL +} diff --git a/vendor/github.com/matterbridge/discordgo/voice.go b/vendor/github.com/bwmarrin/discordgo/voice.go similarity index 98% rename from vendor/github.com/matterbridge/discordgo/voice.go rename to vendor/github.com/bwmarrin/discordgo/voice.go index dbafd837..aedb8790 100644 --- a/vendor/github.com/matterbridge/discordgo/voice.go +++ b/vendor/github.com/bwmarrin/discordgo/voice.go @@ -831,9 +831,15 @@ func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct copy(nonce[:], recvbuf[0:12]) p.Opus, _ = secretbox.Open(nil, recvbuf[12:rlen], &nonce, &v.op4.SecretKey) - if len(p.Opus) > 8 && recvbuf[0] == 0x90 { - // Extension bit is set, first 8 bytes is the extended header - p.Opus = p.Opus[8:] + // extension bit set, and not a RTCP packet + if ((recvbuf[0] & 0x10) == 0x10) && ((recvbuf[1] & 0x80) == 0) { + // get extended header length + extlen := binary.BigEndian.Uint16(p.Opus[2:4]) + // 4 bytes (ext header header) + 4*extlen (ext header data) + shift := int(4 + 4*extlen) + if len(p.Opus) > shift { + p.Opus = p.Opus[shift:] + } } if c != nil { diff --git a/vendor/github.com/bwmarrin/discordgo/webhook.go b/vendor/github.com/bwmarrin/discordgo/webhook.go new file mode 100644 index 00000000..f54a45ce --- /dev/null +++ b/vendor/github.com/bwmarrin/discordgo/webhook.go @@ -0,0 +1,49 @@ +package discordgo + +// Webhook stores the data for a webhook. +type Webhook struct { + ID string `json:"id"` + Type WebhookType `json:"type"` + GuildID string `json:"guild_id"` + ChannelID string `json:"channel_id"` + User *User `json:"user"` + Name string `json:"name"` + Avatar string `json:"avatar"` + Token string `json:"token"` + + // ApplicationID is the bot/OAuth2 application that created this webhook + ApplicationID string `json:"application_id,omitempty"` +} + +// WebhookType is the type of Webhook (see WebhookType* consts) in the Webhook struct +// https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types +type WebhookType int + +// Valid WebhookType values +const ( + WebhookTypeIncoming WebhookType = 1 + WebhookTypeChannelFollower WebhookType = 2 +) + +// WebhookParams is a struct for webhook params, used in the WebhookExecute command. +type WebhookParams struct { + Content string `json:"content,omitempty"` + Username string `json:"username,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` + TTS bool `json:"tts,omitempty"` + Files []*File `json:"-"` + Components []MessageComponent `json:"components"` + Embeds []*MessageEmbed `json:"embeds,omitempty"` + AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` + // NOTE: Works only for followup messages. + Flags uint64 `json:"flags,omitempty"` +} + +// WebhookEdit stores data for editing of a webhook message. +type WebhookEdit struct { + Content string `json:"content,omitempty"` + Components []MessageComponent `json:"components"` + Embeds []*MessageEmbed `json:"embeds,omitempty"` + Files []*File `json:"-"` + AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` +} diff --git a/vendor/github.com/matterbridge/discordgo/wsapi.go b/vendor/github.com/bwmarrin/discordgo/wsapi.go similarity index 96% rename from vendor/github.com/matterbridge/discordgo/wsapi.go rename to vendor/github.com/bwmarrin/discordgo/wsapi.go index 29a4f613..f2c228d5 100644 --- a/vendor/github.com/matterbridge/discordgo/wsapi.go +++ b/vendor/github.com/bwmarrin/discordgo/wsapi.go @@ -33,7 +33,7 @@ var ErrWSAlreadyOpen = errors.New("web socket already opened") var ErrWSNotFound = errors.New("no websocket connection exists") // ErrWSShardBounds is thrown when you try to use a shard ID that is -// less than the total shard count +// more than the total shard count var ErrWSShardBounds = errors.New("ShardID must be less than ShardCount") type resumePacket struct { @@ -383,6 +383,17 @@ func (s *Session) UpdateListeningStatus(name string) (err error) { // UpdateStatusComplex allows for sending the raw status update data untouched by discordgo. func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) { + // The comment does say "untouched by discordgo", but we might need to lie a bit here. + // The Discord documentation lists `activities` as being nullable, but in practice this + // doesn't seem to be the case. I had filed an issue about this at + // https://github.com/discord/discord-api-docs/issues/2559, but as of writing this + // haven't had any movement on it, so at this point I'm assuming this is an error, + // and am fixing this bug accordingly. Because sending `null` for `activities` instantly + // disconnects us, I think that disallowing it from being sent in `UpdateStatusComplex` + // isn't that big of an issue. + if usd.Activities == nil { + usd.Activities = make([]*Activity, 0) + } s.RLock() defer s.RUnlock() @@ -755,13 +766,13 @@ func (s *Session) identify() error { s.log(LogDebug, "called") // TODO: This is a temporary block of code to help - // maintain backwards compatability + // maintain backwards compatibility if s.Compress == false { s.Identify.Compress = false } // TODO: This is a temporary block of code to help - // maintain backwards compatability + // maintain backwards compatibility if s.Token != "" && s.Identify.Token == "" { s.Identify.Token = s.Token } diff --git a/vendor/github.com/matterbridge/discordgo/.gitignore b/vendor/github.com/matterbridge/discordgo/.gitignore deleted file mode 100644 index 34d2efa5..00000000 --- a/vendor/github.com/matterbridge/discordgo/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# IDE-specific metadata -.idea/ diff --git a/vendor/github.com/matterbridge/discordgo/discord.go b/vendor/github.com/matterbridge/discordgo/discord.go deleted file mode 100644 index d319b7b4..00000000 --- a/vendor/github.com/matterbridge/discordgo/discord.go +++ /dev/null @@ -1,161 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/matterbridge/discordgo - -// Copyright 2015-2016 Bruce Marriner . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains high level helper functions and easy entry points for the -// entire discordgo package. These functions are being developed and are very -// experimental at this point. They will most likely change so please use the -// low level functions if that's a problem. - -// Package discordgo provides Discord binding for Go -package discordgo - -import ( - "errors" - "fmt" - "net/http" - "runtime" - "time" -) - -// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/) -const VERSION = "0.23.0" - -// ErrMFA will be risen by New when the user has 2FA. -var ErrMFA = errors.New("account has 2FA enabled") - -// New creates a new Discord session and will automate some startup -// tasks if given enough information to do so. Currently you can pass zero -// arguments and it will return an empty Discord session. -// There are 3 ways to call New: -// With a single auth token - All requests will use the token blindly -// (just tossing it into the HTTP Authorization header); -// no verification of the token will be done and requests may fail. -// IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT ` -// eg: `"Bot "` -// IF IT IS AN OAUTH2 ACCESS TOKEN, IT MUST BE PREFIXED WITH `Bearer ` -// eg: `"Bearer "` -// With an email and password - Discord will sign in with the provided -// credentials. -// With an email, password and auth token - Discord will verify the auth -// token, if it is invalid it will sign in with the provided -// credentials. This is the Discord recommended way to sign in. -// -// NOTE: While email/pass authentication is supported by DiscordGo it is -// HIGHLY DISCOURAGED by Discord. Please only use email/pass to obtain a token -// and then use that authentication token for all future connections. -// Also, doing any form of automation with a user (non Bot) account may result -// in that account being permanently banned from Discord. -func New(args ...interface{}) (s *Session, err error) { - - // Create an empty Session interface. - s = &Session{ - State: NewState(), - Ratelimiter: NewRatelimiter(), - StateEnabled: true, - Compress: true, - ShouldReconnectOnError: true, - ShardID: 0, - ShardCount: 1, - MaxRestRetries: 3, - Client: &http.Client{Timeout: (20 * time.Second)}, - UserAgent: "DiscordBot (https://github.com/matterbridge/discordgo, v" + VERSION + ")", - sequence: new(int64), - LastHeartbeatAck: time.Now().UTC(), - } - - // Initilize the Identify Package with defaults - // These can be modified prior to calling Open() - s.Identify.Compress = true - s.Identify.LargeThreshold = 250 - s.Identify.GuildSubscriptions = true - s.Identify.Properties.OS = runtime.GOOS - s.Identify.Properties.Browser = "DiscordGo v" + VERSION - s.Identify.Intents = MakeIntent(IntentsAllWithoutPrivileged) - - // If no arguments are passed return the empty Session interface. - if args == nil { - return - } - - // Variables used below when parsing func arguments - var auth, pass string - - // Parse passed arguments - for _, arg := range args { - - switch v := arg.(type) { - - case []string: - if len(v) > 3 { - err = fmt.Errorf("too many string parameters provided") - return - } - - // First string is either token or username - if len(v) > 0 { - auth = v[0] - } - - // If second string exists, it must be a password. - if len(v) > 1 { - pass = v[1] - } - - // If third string exists, it must be an auth token. - if len(v) > 2 { - s.Identify.Token = v[2] - s.Token = v[2] // TODO: Remove, Deprecated - Kept for backwards compatibility. - } - - case string: - // First string must be either auth token or username. - // Second string must be a password. - // Only 2 input strings are supported. - - if auth == "" { - auth = v - } else if pass == "" { - pass = v - } else if s.Token == "" { - s.Identify.Token = v - s.Token = v // TODO: Remove, Deprecated - Kept for backwards compatibility. - } else { - err = fmt.Errorf("too many string parameters provided") - return - } - - // case Config: - // TODO: Parse configuration struct - - default: - err = fmt.Errorf("unsupported parameter type provided") - return - } - } - - // If only one string was provided, assume it is an auth token. - // Otherwise get auth token from Discord, if a token was specified - // Discord will verify it for free, or log the user in if it is - // invalid. - if pass == "" { - s.Identify.Token = auth - s.Token = auth // TODO: Remove, Deprecated - Kept for backwards compatibility. - } else { - err = s.Login(auth, pass) - // TODO: Remove last s.Token part, Deprecated - Kept for backwards compatibility. - if err != nil || s.Identify.Token == "" || s.Token == "" { - if s.MFA { - err = ErrMFA - } else { - err = fmt.Errorf("Unable to fetch discord authentication token. %v", err) - } - return - } - } - - return -} diff --git a/vendor/github.com/matterbridge/discordgo/endpoints.go b/vendor/github.com/matterbridge/discordgo/endpoints.go deleted file mode 100644 index 89d56eda..00000000 --- a/vendor/github.com/matterbridge/discordgo/endpoints.go +++ /dev/null @@ -1,153 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains variables for all known Discord end points. All functions -// throughout the Discordgo package use these variables for all connections -// to Discord. These are all exported and you may modify them if needed. - -package discordgo - -import "strconv" - -// APIVersion is the Discord API version used for the REST and Websocket API. -var APIVersion = "8" - -// Known Discord API Endpoints. -var ( - EndpointStatus = "https://status.discord.com/api/v2/" - EndpointSm = EndpointStatus + "scheduled-maintenances/" - EndpointSmActive = EndpointSm + "active.json" - EndpointSmUpcoming = EndpointSm + "upcoming.json" - - EndpointDiscord = "https://discord.com/" - EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/" - EndpointGuilds = EndpointAPI + "guilds/" - EndpointChannels = EndpointAPI + "channels/" - EndpointUsers = EndpointAPI + "users/" - EndpointGateway = EndpointAPI + "gateway" - EndpointGatewayBot = EndpointGateway + "/bot" - EndpointWebhooks = EndpointAPI + "webhooks/" - - EndpointCDN = "https://cdn.discordapp.com/" - EndpointCDNAttachments = EndpointCDN + "attachments/" - EndpointCDNAvatars = EndpointCDN + "avatars/" - EndpointCDNIcons = EndpointCDN + "icons/" - EndpointCDNSplashes = EndpointCDN + "splashes/" - EndpointCDNChannelIcons = EndpointCDN + "channel-icons/" - EndpointCDNBanners = EndpointCDN + "banners/" - - EndpointAuth = EndpointAPI + "auth/" - EndpointLogin = EndpointAuth + "login" - EndpointLogout = EndpointAuth + "logout" - EndpointVerify = EndpointAuth + "verify" - EndpointVerifyResend = EndpointAuth + "verify/resend" - EndpointForgotPassword = EndpointAuth + "forgot" - EndpointResetPassword = EndpointAuth + "reset" - EndpointRegister = EndpointAuth + "register" - - EndpointVoice = EndpointAPI + "/voice/" - EndpointVoiceRegions = EndpointVoice + "regions" - EndpointVoiceIce = EndpointVoice + "ice" - - EndpointTutorial = EndpointAPI + "tutorial/" - EndpointTutorialIndicators = EndpointTutorial + "indicators" - - EndpointTrack = EndpointAPI + "track" - EndpointSso = EndpointAPI + "sso" - EndpointReport = EndpointAPI + "report" - EndpointIntegrations = EndpointAPI + "integrations" - - EndpointUser = func(uID string) string { return EndpointUsers + uID } - EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" } - EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" } - EndpointDefaultUserAvatar = func(uDiscriminator string) string { - uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator) - return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png" - } - EndpointUserSettings = func(uID string) string { return EndpointUsers + uID + "/settings" } - EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" } - EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID } - EndpointUserGuildSettings = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID + "/settings" } - EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" } - EndpointUserDevices = func(uID string) string { return EndpointUsers + uID + "/devices" } - EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" } - EndpointUserNotes = func(uID string) string { return EndpointUsers + "@me/notes/" + uID } - - EndpointGuild = func(gID string) string { return EndpointGuilds + gID } - EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" } - EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" } - EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID } - EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID } - EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" } - EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID } - EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" } - EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID } - EndpointGuildIntegrationSync = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID + "/sync" } - EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" } - EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID } - EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" } - EndpointGuildWidget = func(gID string) string { return EndpointGuilds + gID + "/widget" } - EndpointGuildEmbed = EndpointGuildWidget - EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" } - EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" } - EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" } - EndpointGuildSplash = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" } - EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" } - EndpointGuildAuditLogs = func(gID string) string { return EndpointGuilds + gID + "/audit-logs" } - EndpointGuildEmojis = func(gID string) string { return EndpointGuilds + gID + "/emojis" } - EndpointGuildEmoji = func(gID, eID string) string { return EndpointGuilds + gID + "/emojis/" + eID } - EndpointGuildBanner = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".png" } - - EndpointChannel = func(cID string) string { return EndpointChannels + cID } - EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" } - EndpointChannelPermission = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID } - EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" } - EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" } - EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" } - EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID } - EndpointChannelMessageAck = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" } - EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" } - EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" } - EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID } - EndpointChannelMessageCrosspost = func(cID, mID string) string { return EndpointChannel(cID) + "/messages/" + mID + "/crosspost" } - EndpointChannelFollow = func(cID string) string { return EndpointChannel(cID) + "/followers" } - - EndpointGroupIcon = func(cID, hash string) string { return EndpointCDNChannelIcons + cID + "/" + hash + ".png" } - - EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" } - EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID } - EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token } - - EndpointMessageReactionsAll = func(cID, mID string) string { - return EndpointChannelMessage(cID, mID) + "/reactions" - } - EndpointMessageReactions = func(cID, mID, eID string) string { - return EndpointChannelMessage(cID, mID) + "/reactions/" + eID - } - EndpointMessageReaction = func(cID, mID, eID, uID string) string { - return EndpointMessageReactions(cID, mID, eID) + "/" + uID - } - - EndpointRelationships = func() string { return EndpointUsers + "@me" + "/relationships" } - EndpointRelationship = func(uID string) string { return EndpointRelationships() + "/" + uID } - EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" } - - EndpointGuildCreate = EndpointAPI + "guilds" - - EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID } - - EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" } - - EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" } - EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" } - - EndpointOauth2 = EndpointAPI + "oauth2/" - EndpointApplications = EndpointOauth2 + "applications" - EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID } - EndpointApplicationsBot = func(aID string) string { return EndpointApplications + "/" + aID + "/bot" } - EndpointApplicationAssets = func(aID string) string { return EndpointApplications + "/" + aID + "/assets" } -) diff --git a/vendor/github.com/matterbridge/discordgo/interactions.go b/vendor/github.com/matterbridge/discordgo/interactions.go deleted file mode 100644 index 6fc2f55e..00000000 --- a/vendor/github.com/matterbridge/discordgo/interactions.go +++ /dev/null @@ -1,54 +0,0 @@ -package discordgo - -import ( - "bytes" - "crypto/ed25519" - "encoding/hex" - "io" - "io/ioutil" - "net/http" -) - -// VerifyInteraction implements message verification of the discord interactions api -// signing algorithm, as documented here: -// https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization -func VerifyInteraction(r *http.Request, key ed25519.PublicKey) bool { - var msg bytes.Buffer - - signature := r.Header.Get("X-Signature-Ed25519") - if signature == "" { - return false - } - - sig, err := hex.DecodeString(signature) - if err != nil { - return false - } - - if len(sig) != ed25519.SignatureSize { - return false - } - - timestamp := r.Header.Get("X-Signature-Timestamp") - if timestamp == "" { - return false - } - - msg.WriteString(timestamp) - - defer r.Body.Close() - var body bytes.Buffer - - // at the end of the function, copy the original body back into the request - defer func() { - r.Body = ioutil.NopCloser(&body) - }() - - // copy body into buffers - _, err = io.Copy(&msg, io.TeeReader(r.Body, &body)) - if err != nil { - return false - } - - return ed25519.Verify(key, msg.Bytes(), sig) -} diff --git a/vendor/github.com/matterbridge/discordgo/structs.go b/vendor/github.com/matterbridge/discordgo/structs.go deleted file mode 100644 index 8cebfdc5..00000000 --- a/vendor/github.com/matterbridge/discordgo/structs.go +++ /dev/null @@ -1,1339 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner . All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains all structures for the discordgo package. These -// may be moved about later into separate files but I find it easier to have -// them all located together. - -package discordgo - -import ( - "encoding/json" - "fmt" - "math" - "net/http" - "strings" - "sync" - "time" - - "github.com/gorilla/websocket" -) - -// A Session represents a connection to the Discord API. -type Session struct { - sync.RWMutex - - // General configurable settings. - - // Authentication token for this session - // TODO: Remove Below, Deprecated, Use Identify struct - Token string - - MFA bool - - // Debug for printing JSON request/responses - Debug bool // Deprecated, will be removed. - LogLevel int - - // Should the session reconnect the websocket on errors. - ShouldReconnectOnError bool - - // Identify is sent during initial handshake with the discord gateway. - // https://discord.com/developers/docs/topics/gateway#identify - Identify Identify - - // TODO: Remove Below, Deprecated, Use Identify struct - // Should the session request compressed websocket data. - Compress bool - - // Sharding - ShardID int - ShardCount int - - // Should state tracking be enabled. - // State tracking is the best way for getting the the users - // active guilds and the members of the guilds. - StateEnabled bool - - // Whether or not to call event handlers synchronously. - // e.g false = launch event handlers in their own goroutines. - SyncEvents bool - - // Exposed but should not be modified by User. - - // Whether the Data Websocket is ready - DataReady bool // NOTE: Maye be deprecated soon - - // Max number of REST API retries - MaxRestRetries int - - // Status stores the currect status of the websocket connection - // this is being tested, may stay, may go away. - status int32 - - // Whether the Voice Websocket is ready - VoiceReady bool // NOTE: Deprecated. - - // Whether the UDP Connection is ready - UDPReady bool // NOTE: Deprecated - - // Stores a mapping of guild id's to VoiceConnections - VoiceConnections map[string]*VoiceConnection - - // Managed state object, updated internally with events when - // StateEnabled is true. - State *State - - // The http client used for REST requests - Client *http.Client - - // The user agent used for REST APIs - UserAgent string - - // Stores the last HeartbeatAck that was recieved (in UTC) - LastHeartbeatAck time.Time - - // Stores the last Heartbeat sent (in UTC) - LastHeartbeatSent time.Time - - // used to deal with rate limits - Ratelimiter *RateLimiter - - // Event handlers - handlersMu sync.RWMutex - handlers map[string][]*eventHandlerInstance - onceHandlers map[string][]*eventHandlerInstance - - // The websocket connection. - wsConn *websocket.Conn - - // When nil, the session is not listening. - listening chan interface{} - - // sequence tracks the current gateway api websocket sequence number - sequence *int64 - - // stores sessions current Discord Gateway - gateway string - - // stores session ID of current Gateway connection - sessionID string - - // used to make sure gateway websocket writes do not happen concurrently - wsMutex sync.Mutex -} - -// UserConnection is a Connection returned from the UserConnections endpoint -type UserConnection struct { - ID string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Revoked bool `json:"revoked"` - Integrations []*Integration `json:"integrations"` -} - -// Integration stores integration information -type Integration struct { - ID string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Enabled bool `json:"enabled"` - Syncing bool `json:"syncing"` - RoleID string `json:"role_id"` - EnableEmoticons bool `json:"enable_emoticons"` - ExpireBehavior ExpireBehavior `json:"expire_behavior"` - ExpireGracePeriod int `json:"expire_grace_period"` - User *User `json:"user"` - Account IntegrationAccount `json:"account"` - SyncedAt Timestamp `json:"synced_at"` -} - -//ExpireBehavior of Integration -// https://discord.com/developers/docs/resources/guild#integration-object-integration-expire-behaviors -type ExpireBehavior int - -// Block of valid ExpireBehaviors -const ( - ExpireBehaviorRemoveRole ExpireBehavior = iota - ExpireBehaviorKick -) - -// IntegrationAccount is integration account information -// sent by the UserConnections endpoint -type IntegrationAccount struct { - ID string `json:"id"` - Name string `json:"name"` -} - -// A VoiceRegion stores data for a specific voice region server. -type VoiceRegion struct { - ID string `json:"id"` - Name string `json:"name"` - Hostname string `json:"sample_hostname"` - Port int `json:"sample_port"` -} - -// A VoiceICE stores data for voice ICE servers. -type VoiceICE struct { - TTL string `json:"ttl"` - Servers []*ICEServer `json:"servers"` -} - -// A ICEServer stores data for a specific voice ICE server. -type ICEServer struct { - URL string `json:"url"` - Username string `json:"username"` - Credential string `json:"credential"` -} - -// A Invite stores all data related to a specific Discord Guild or Channel invite. -type Invite struct { - Guild *Guild `json:"guild"` - Channel *Channel `json:"channel"` - Inviter *User `json:"inviter"` - Code string `json:"code"` - CreatedAt Timestamp `json:"created_at"` - MaxAge int `json:"max_age"` - Uses int `json:"uses"` - MaxUses int `json:"max_uses"` - Revoked bool `json:"revoked"` - Temporary bool `json:"temporary"` - Unique bool `json:"unique"` - TargetUser *User `json:"target_user"` - TargetUserType TargetUserType `json:"target_user_type"` - - // will only be filled when using InviteWithCounts - ApproximatePresenceCount int `json:"approximate_presence_count"` - ApproximateMemberCount int `json:"approximate_member_count"` -} - -// TargetUserType is the type of the target user -// https://discord.com/developers/docs/resources/invite#invite-object-target-user-types -type TargetUserType int - -// Block contains known TargetUserType values -const ( - TargetUserTypeStream TargetUserType = iota -) - -// ChannelType is the type of a Channel -type ChannelType int - -// Block contains known ChannelType values -const ( - ChannelTypeGuildText ChannelType = iota - ChannelTypeDM - ChannelTypeGuildVoice - ChannelTypeGroupDM - ChannelTypeGuildCategory - ChannelTypeGuildNews - ChannelTypeGuildStore -) - -// A Channel holds all data related to an individual Discord channel. -type Channel struct { - // The ID of the channel. - ID string `json:"id"` - - // The ID of the guild to which the channel belongs, if it is in a guild. - // Else, this ID is empty (e.g. DM channels). - GuildID string `json:"guild_id"` - - // The name of the channel. - Name string `json:"name"` - - // The topic of the channel. - Topic string `json:"topic"` - - // The type of the channel. - Type ChannelType `json:"type"` - - // The ID of the last message sent in the channel. This is not - // guaranteed to be an ID of a valid message. - LastMessageID string `json:"last_message_id"` - - // The timestamp of the last pinned message in the channel. - // Empty if the channel has no pinned messages. - LastPinTimestamp Timestamp `json:"last_pin_timestamp"` - - // Whether the channel is marked as NSFW. - NSFW bool `json:"nsfw"` - - // Icon of the group DM channel. - Icon string `json:"icon"` - - // The position of the channel, used for sorting in client. - Position int `json:"position"` - - // The bitrate of the channel, if it is a voice channel. - Bitrate int `json:"bitrate"` - - // The recipients of the channel. This is only populated in DM channels. - Recipients []*User `json:"recipients"` - - // The messages in the channel. This is only present in state-cached channels, - // and State.MaxMessageCount must be non-zero. - Messages []*Message `json:"-"` - - // A list of permission overwrites present for the channel. - PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"` - - // The user limit of the voice channel. - UserLimit int `json:"user_limit"` - - // The ID of the parent channel, if the channel is under a category - ParentID string `json:"parent_id"` - - // Amount of seconds a user has to wait before sending another message (0-21600) - // bots, as well as users with the permission manage_messages or manage_channel, are unaffected - RateLimitPerUser int `json:"rate_limit_per_user"` - - // ID of the DM creator Zeroed if guild channel - OwnerID string `json:"owner_id"` - - // ApplicationID of the DM creator Zeroed if guild channel or not a bot user - ApplicationID string `json:"application_id"` -} - -// Mention returns a string which mentions the channel -func (c *Channel) Mention() string { - return fmt.Sprintf("<#%s>", c.ID) -} - -// A ChannelEdit holds Channel Field data for a channel edit. -type ChannelEdit struct { - Name string `json:"name,omitempty"` - Topic string `json:"topic,omitempty"` - NSFW bool `json:"nsfw,omitempty"` - Position int `json:"position"` - Bitrate int `json:"bitrate,omitempty"` - UserLimit int `json:"user_limit,omitempty"` - PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"` - ParentID string `json:"parent_id,omitempty"` - RateLimitPerUser int `json:"rate_limit_per_user,omitempty"` -} - -// A ChannelFollow holds data returned after following a news channel -type ChannelFollow struct { - ChannelID string `json:"channel_id"` - WebhookID string `json:"webhook_id"` -} - -// PermissionOverwriteType represents the type of resource on which -// a permission overwrite acts. -type PermissionOverwriteType int - -// The possible permission overwrite types. -const ( - PermissionOverwriteTypeRole PermissionOverwriteType = iota - PermissionOverwriteTypeMember -) - -// A PermissionOverwrite holds permission overwrite data for a Channel -type PermissionOverwrite struct { - ID string `json:"id"` - Type PermissionOverwriteType `json:"type"` - Deny int64 `json:"deny,string"` - Allow int64 `json:"allow,string"` -} - -// Emoji struct holds data related to Emoji's -type Emoji struct { - ID string `json:"id"` - Name string `json:"name"` - Roles []string `json:"roles"` - User *User `json:"user"` - RequireColons bool `json:"require_colons"` - Managed bool `json:"managed"` - Animated bool `json:"animated"` - Available bool `json:"available"` -} - -// MessageFormat returns a correctly formatted Emoji for use in Message content and embeds -func (e *Emoji) MessageFormat() string { - if e.ID != "" && e.Name != "" { - if e.Animated { - return "" - } - - return "<:" + e.APIName() + ">" - } - - return e.APIName() -} - -// APIName returns an correctly formatted API name for use in the MessageReactions endpoints. -func (e *Emoji) APIName() string { - if e.ID != "" && e.Name != "" { - return e.Name + ":" + e.ID - } - if e.Name != "" { - return e.Name - } - return e.ID -} - -// VerificationLevel type definition -type VerificationLevel int - -// Constants for VerificationLevel levels from 0 to 4 inclusive -const ( - VerificationLevelNone VerificationLevel = iota - VerificationLevelLow - VerificationLevelMedium - VerificationLevelHigh - VerificationLevelVeryHigh -) - -// ExplicitContentFilterLevel type definition -type ExplicitContentFilterLevel int - -// Constants for ExplicitContentFilterLevel levels from 0 to 2 inclusive -const ( - ExplicitContentFilterDisabled ExplicitContentFilterLevel = iota - ExplicitContentFilterMembersWithoutRoles - ExplicitContentFilterAllMembers -) - -// MfaLevel type definition -type MfaLevel int - -// Constants for MfaLevel levels from 0 to 1 inclusive -const ( - MfaLevelNone MfaLevel = iota - MfaLevelElevated -) - -// PremiumTier type definition -type PremiumTier int - -// Constants for PremiumTier levels from 0 to 3 inclusive -const ( - PremiumTierNone PremiumTier = iota - PremiumTier1 - PremiumTier2 - PremiumTier3 -) - -// A Guild holds all data related to a specific Discord Guild. Guilds are also -// sometimes referred to as Servers in the Discord client. -type Guild struct { - // The ID of the guild. - ID string `json:"id"` - - // The name of the guild. (2–100 characters) - Name string `json:"name"` - - // The hash of the guild's icon. Use Session.GuildIcon - // to retrieve the icon itself. - Icon string `json:"icon"` - - // The voice region of the guild. - Region string `json:"region"` - - // The ID of the AFK voice channel. - AfkChannelID string `json:"afk_channel_id"` - - // The user ID of the owner of the guild. - OwnerID string `json:"owner_id"` - - // If we are the owner of the guild - Owner bool `json:"owner"` - - // The time at which the current user joined the guild. - // This field is only present in GUILD_CREATE events and websocket - // update events, and thus is only present in state-cached guilds. - JoinedAt Timestamp `json:"joined_at"` - - // The hash of the guild's discovery splash. - DiscoverySplash string `json:"discovery_splash"` - - // The hash of the guild's splash. - Splash string `json:"splash"` - - // The timeout, in seconds, before a user is considered AFK in voice. - AfkTimeout int `json:"afk_timeout"` - - // The number of members in the guild. - // This field is only present in GUILD_CREATE events and websocket - // update events, and thus is only present in state-cached guilds. - MemberCount int `json:"member_count"` - - // The verification level required for the guild. - VerificationLevel VerificationLevel `json:"verification_level"` - - // Whether the guild is considered large. This is - // determined by a member threshold in the identify packet, - // and is currently hard-coded at 250 members in the library. - Large bool `json:"large"` - - // The default message notification setting for the guild. - DefaultMessageNotifications MessageNotifications `json:"default_message_notifications"` - - // A list of roles in the guild. - Roles []*Role `json:"roles"` - - // A list of the custom emojis present in the guild. - Emojis []*Emoji `json:"emojis"` - - // A list of the members in the guild. - // This field is only present in GUILD_CREATE events and websocket - // update events, and thus is only present in state-cached guilds. - Members []*Member `json:"members"` - - // A list of partial presence objects for members in the guild. - // This field is only present in GUILD_CREATE events and websocket - // update events, and thus is only present in state-cached guilds. - Presences []*Presence `json:"presences"` - - // The maximum number of presences for the guild (the default value, currently 25000, is in effect when null is returned) - MaxPresences int `json:"max_presences"` - - // The maximum number of members for the guild - MaxMembers int `json:"max_members"` - - // A list of channels in the guild. - // This field is only present in GUILD_CREATE events and websocket - // update events, and thus is only present in state-cached guilds. - Channels []*Channel `json:"channels"` - - // A list of voice states for the guild. - // This field is only present in GUILD_CREATE events and websocket - // update events, and thus is only present in state-cached guilds. - VoiceStates []*VoiceState `json:"voice_states"` - - // Whether this guild is currently unavailable (most likely due to outage). - // This field is only present in GUILD_CREATE events and websocket - // update events, and thus is only present in state-cached guilds. - Unavailable bool `json:"unavailable"` - - // The explicit content filter level - ExplicitContentFilter ExplicitContentFilterLevel `json:"explicit_content_filter"` - - // The list of enabled guild features - Features []string `json:"features"` - - // Required MFA level for the guild - MfaLevel MfaLevel `json:"mfa_level"` - - // The application id of the guild if bot created. - ApplicationID string `json:"application_id"` - - // Whether or not the Server Widget is enabled - WidgetEnabled bool `json:"widget_enabled"` - - // The Channel ID for the Server Widget - WidgetChannelID string `json:"widget_channel_id"` - - // The Channel ID to which system messages are sent (eg join and leave messages) - SystemChannelID string `json:"system_channel_id"` - - // The System channel flags - SystemChannelFlags SystemChannelFlag `json:"system_channel_flags"` - - // The ID of the rules channel ID, used for rules. - RulesChannelID string `json:"rules_channel_id"` - - // the vanity url code for the guild - VanityURLCode string `json:"vanity_url_code"` - - // the description for the guild - Description string `json:"description"` - - // The hash of the guild's banner - Banner string `json:"banner"` - - // The premium tier of the guild - PremiumTier PremiumTier `json:"premium_tier"` - - // The total number of users currently boosting this server - PremiumSubscriptionCount int `json:"premium_subscription_count"` - - // The preferred locale of a guild with the "PUBLIC" feature; used in server discovery and notices from Discord; defaults to "en-US" - PreferredLocale string `json:"preferred_locale"` - - // The id of the channel where admins and moderators of guilds with the "PUBLIC" feature receive notices from Discord - PublicUpdatesChannelID string `json:"public_updates_channel_id"` - - // The maximum amount of users in a video channel - MaxVideoChannelUsers int `json:"max_video_channel_users"` - - // Approximate number of members in this guild, returned from the GET /guild/ endpoint when with_counts is true - ApproximateMemberCount int `json:"approximate_member_count"` - - // Approximate number of non-offline members in this guild, returned from the GET /guild/ endpoint when with_counts is true - ApproximatePresenceCount int `json:"approximate_presence_count"` - - // Permissions of our user - Permissions int64 `json:"permissions,string"` -} - -// MessageNotifications is the notification level for a guild -// https://discord.com/developers/docs/resources/guild#guild-object-default-message-notification-level -type MessageNotifications int - -// Block containing known MessageNotifications values -const ( - MessageNotificationsAllMessages MessageNotifications = iota - MessageNotificationsOnlyMentions -) - -// SystemChannelFlag is the type of flags in the system channel (see SystemChannelFlag* consts) -// https://discord.com/developers/docs/resources/guild#guild-object-system-channel-flags -type SystemChannelFlag int - -// Block containing known SystemChannelFlag values -const ( - SystemChannelFlagsSuppressJoin SystemChannelFlag = 1 << iota - SystemChannelFlagsSuppressPremium -) - -// IconURL returns a URL to the guild's icon. -func (g *Guild) IconURL() string { - if g.Icon == "" { - return "" - } - - if strings.HasPrefix(g.Icon, "a_") { - return EndpointGuildIconAnimated(g.ID, g.Icon) - } - - return EndpointGuildIcon(g.ID, g.Icon) -} - -// A UserGuild holds a brief version of a Guild -type UserGuild struct { - ID string `json:"id"` - Name string `json:"name"` - Icon string `json:"icon"` - Owner bool `json:"owner"` - Permissions int64 `json:"permissions,string"` -} - -// A GuildParams stores all the data needed to update discord guild settings -type GuildParams struct { - Name string `json:"name,omitempty"` - Region string `json:"region,omitempty"` - VerificationLevel *VerificationLevel `json:"verification_level,omitempty"` - DefaultMessageNotifications int `json:"default_message_notifications,omitempty"` // TODO: Separate type? - AfkChannelID string `json:"afk_channel_id,omitempty"` - AfkTimeout int `json:"afk_timeout,omitempty"` - Icon string `json:"icon,omitempty"` - OwnerID string `json:"owner_id,omitempty"` - Splash string `json:"splash,omitempty"` - Banner string `json:"banner,omitempty"` -} - -// A Role stores information about Discord guild member roles. -type Role struct { - // The ID of the role. - ID string `json:"id"` - - // The name of the role. - Name string `json:"name"` - - // Whether this role is managed by an integration, and - // thus cannot be manually added to, or taken from, members. - Managed bool `json:"managed"` - - // Whether this role is mentionable. - Mentionable bool `json:"mentionable"` - - // Whether this role is hoisted (shows up separately in member list). - Hoist bool `json:"hoist"` - - // The hex color of this role. - Color int `json:"color"` - - // The position of this role in the guild's role hierarchy. - Position int `json:"position"` - - // The permissions of the role on the guild (doesn't include channel overrides). - // This is a combination of bit masks; the presence of a certain permission can - // be checked by performing a bitwise AND between this int and the permission. - Permissions int64 `json:"permissions,string"` -} - -// Mention returns a string which mentions the role -func (r *Role) Mention() string { - return fmt.Sprintf("<@&%s>", r.ID) -} - -// Roles are a collection of Role -type Roles []*Role - -func (r Roles) Len() int { - return len(r) -} - -func (r Roles) Less(i, j int) bool { - return r[i].Position > r[j].Position -} - -func (r Roles) Swap(i, j int) { - r[i], r[j] = r[j], r[i] -} - -// A VoiceState stores the voice states of Guilds -type VoiceState struct { - UserID string `json:"user_id"` - SessionID string `json:"session_id"` - ChannelID string `json:"channel_id"` - GuildID string `json:"guild_id"` - Suppress bool `json:"suppress"` - SelfMute bool `json:"self_mute"` - SelfDeaf bool `json:"self_deaf"` - Mute bool `json:"mute"` - Deaf bool `json:"deaf"` -} - -// A Presence stores the online, offline, or idle and game status of Guild members. -type Presence struct { - User *User `json:"user"` - Status Status `json:"status"` - Activities []*Activity `json:"activities"` - Since *int `json:"since"` -} - -// A TimeStamps struct contains start and end times used in the rich presence "playing .." Game -type TimeStamps struct { - EndTimestamp int64 `json:"end,omitempty"` - StartTimestamp int64 `json:"start,omitempty"` -} - -// UnmarshalJSON unmarshals JSON into TimeStamps struct -func (t *TimeStamps) UnmarshalJSON(b []byte) error { - temp := struct { - End float64 `json:"end,omitempty"` - Start float64 `json:"start,omitempty"` - }{} - err := json.Unmarshal(b, &temp) - if err != nil { - return err - } - t.EndTimestamp = int64(temp.End) - t.StartTimestamp = int64(temp.Start) - return nil -} - -// An Assets struct contains assets and labels used in the rich presence "playing .." Game -type Assets struct { - LargeImageID string `json:"large_image,omitempty"` - SmallImageID string `json:"small_image,omitempty"` - LargeText string `json:"large_text,omitempty"` - SmallText string `json:"small_text,omitempty"` -} - -// A Member stores user information for Guild members. A guild -// member represents a certain user's presence in a guild. -type Member struct { - // The guild ID on which the member exists. - GuildID string `json:"guild_id"` - - // The time at which the member joined the guild, in ISO8601. - JoinedAt Timestamp `json:"joined_at"` - - // The nickname of the member, if they have one. - Nick string `json:"nick"` - - // Whether the member is deafened at a guild level. - Deaf bool `json:"deaf"` - - // Whether the member is muted at a guild level. - Mute bool `json:"mute"` - - // The underlying user on which the member is based. - User *User `json:"user"` - - // A list of IDs of the roles which are possessed by the member. - Roles []string `json:"roles"` - - // When the user used their Nitro boost on the server - PremiumSince Timestamp `json:"premium_since"` - - // Is true while the member hasn't accepted the membership screen. - Pending bool `json:"pending"` -} - -// Mention creates a member mention -func (m *Member) Mention() string { - return "<@!" + m.User.ID + ">" -} - -// A Settings stores data for a specific users Discord client settings. -type Settings struct { - RenderEmbeds bool `json:"render_embeds"` - InlineEmbedMedia bool `json:"inline_embed_media"` - InlineAttachmentMedia bool `json:"inline_attachment_media"` - EnableTTSCommand bool `json:"enable_tts_command"` - MessageDisplayCompact bool `json:"message_display_compact"` - ShowCurrentGame bool `json:"show_current_game"` - ConvertEmoticons bool `json:"convert_emoticons"` - Locale string `json:"locale"` - Theme string `json:"theme"` - GuildPositions []string `json:"guild_positions"` - RestrictedGuilds []string `json:"restricted_guilds"` - FriendSourceFlags *FriendSourceFlags `json:"friend_source_flags"` - Status Status `json:"status"` - DetectPlatformAccounts bool `json:"detect_platform_accounts"` - DeveloperMode bool `json:"developer_mode"` -} - -// Status type definition -type Status string - -// Constants for Status with the different current available status -const ( - StatusOnline Status = "online" - StatusIdle Status = "idle" - StatusDoNotDisturb Status = "dnd" - StatusInvisible Status = "invisible" - StatusOffline Status = "offline" -) - -// FriendSourceFlags stores ... TODO :) -type FriendSourceFlags struct { - All bool `json:"all"` - MutualGuilds bool `json:"mutual_guilds"` - MutualFriends bool `json:"mutual_friends"` -} - -// A Relationship between the logged in user and Relationship.User -type Relationship struct { - User *User `json:"user"` - Type int `json:"type"` // 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req - ID string `json:"id"` -} - -// A TooManyRequests struct holds information received from Discord -// when receiving a HTTP 429 response. -type TooManyRequests struct { - Bucket string `json:"bucket"` - Message string `json:"message"` - RetryAfter time.Duration `json:"retry_after"` -} - -// UnmarshalJSON helps support translation of a milliseconds-based float -// into a time.Duration on TooManyRequests. -func (t *TooManyRequests) UnmarshalJSON(b []byte) error { - u := struct { - Bucket string `json:"bucket"` - Message string `json:"message"` - RetryAfter float64 `json:"retry_after"` - }{} - err := json.Unmarshal(b, &u) - if err != nil { - return err - } - - t.Bucket = u.Bucket - t.Message = u.Message - whole, frac := math.Modf(u.RetryAfter) - t.RetryAfter = time.Duration(whole)*time.Second + time.Duration(frac*1000)*time.Millisecond - return nil -} - -// A ReadState stores data on the read state of channels. -type ReadState struct { - MentionCount int `json:"mention_count"` - LastMessageID string `json:"last_message_id"` - ID string `json:"id"` -} - -// An Ack is used to ack messages -type Ack struct { - Token string `json:"token"` -} - -// A GuildRole stores data for guild roles. -type GuildRole struct { - Role *Role `json:"role"` - GuildID string `json:"guild_id"` -} - -// A GuildBan stores data for a guild ban. -type GuildBan struct { - Reason string `json:"reason"` - User *User `json:"user"` -} - -// A GuildEmbed stores data for a guild embed. -type GuildEmbed struct { - Enabled bool `json:"enabled"` - ChannelID string `json:"channel_id"` -} - -// A GuildAuditLog stores data for a guild audit log. -// https://discord.com/developers/docs/resources/audit-log#audit-log-object-audit-log-structure -type GuildAuditLog struct { - Webhooks []*Webhook `json:"webhooks,omitempty"` - Users []*User `json:"users,omitempty"` - AuditLogEntries []*AuditLogEntry `json:"audit_log_entries"` - Integrations []*Integration `json:"integrations"` -} - -// AuditLogEntry for a GuildAuditLog -// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-entry-structure -type AuditLogEntry struct { - TargetID string `json:"target_id"` - Changes []*AuditLogChange `json:"changes"` - UserID string `json:"user_id"` - ID string `json:"id"` - ActionType *AuditLogAction `json:"action_type"` - Options *AuditLogOptions `json:"options"` - Reason string `json:"reason"` -} - -// AuditLogChange for an AuditLogEntry -type AuditLogChange struct { - NewValue interface{} `json:"new_value"` - OldValue interface{} `json:"old_value"` - Key *AuditLogChangeKey `json:"key"` -} - -// AuditLogChangeKey value for AuditLogChange -// https://discord.com/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-key -type AuditLogChangeKey string - -// Block of valid AuditLogChangeKey -const ( - AuditLogChangeKeyName AuditLogChangeKey = "name" - AuditLogChangeKeyIconHash AuditLogChangeKey = "icon_hash" - AuditLogChangeKeySplashHash AuditLogChangeKey = "splash_hash" - AuditLogChangeKeyOwnerID AuditLogChangeKey = "owner_id" - AuditLogChangeKeyRegion AuditLogChangeKey = "region" - AuditLogChangeKeyAfkChannelID AuditLogChangeKey = "afk_channel_id" - AuditLogChangeKeyAfkTimeout AuditLogChangeKey = "afk_timeout" - AuditLogChangeKeyMfaLevel AuditLogChangeKey = "mfa_level" - AuditLogChangeKeyVerificationLevel AuditLogChangeKey = "verification_level" - AuditLogChangeKeyExplicitContentFilter AuditLogChangeKey = "explicit_content_filter" - AuditLogChangeKeyDefaultMessageNotification AuditLogChangeKey = "default_message_notifications" - AuditLogChangeKeyVanityURLCode AuditLogChangeKey = "vanity_url_code" - AuditLogChangeKeyRoleAdd AuditLogChangeKey = "$add" - AuditLogChangeKeyRoleRemove AuditLogChangeKey = "$remove" - AuditLogChangeKeyPruneDeleteDays AuditLogChangeKey = "prune_delete_days" - AuditLogChangeKeyWidgetEnabled AuditLogChangeKey = "widget_enabled" - AuditLogChangeKeyWidgetChannelID AuditLogChangeKey = "widget_channel_id" - AuditLogChangeKeySystemChannelID AuditLogChangeKey = "system_channel_id" - AuditLogChangeKeyPosition AuditLogChangeKey = "position" - AuditLogChangeKeyTopic AuditLogChangeKey = "topic" - AuditLogChangeKeyBitrate AuditLogChangeKey = "bitrate" - AuditLogChangeKeyPermissionOverwrite AuditLogChangeKey = "permission_overwrites" - AuditLogChangeKeyNSFW AuditLogChangeKey = "nsfw" - AuditLogChangeKeyApplicationID AuditLogChangeKey = "application_id" - AuditLogChangeKeyRateLimitPerUser AuditLogChangeKey = "rate_limit_per_user" - AuditLogChangeKeyPermissions AuditLogChangeKey = "permissions" - AuditLogChangeKeyColor AuditLogChangeKey = "color" - AuditLogChangeKeyHoist AuditLogChangeKey = "hoist" - AuditLogChangeKeyMentionable AuditLogChangeKey = "mentionable" - AuditLogChangeKeyAllow AuditLogChangeKey = "allow" - AuditLogChangeKeyDeny AuditLogChangeKey = "deny" - AuditLogChangeKeyCode AuditLogChangeKey = "code" - AuditLogChangeKeyChannelID AuditLogChangeKey = "channel_id" - AuditLogChangeKeyInviterID AuditLogChangeKey = "inviter_id" - AuditLogChangeKeyMaxUses AuditLogChangeKey = "max_uses" - AuditLogChangeKeyUses AuditLogChangeKey = "uses" - AuditLogChangeKeyMaxAge AuditLogChangeKey = "max_age" - AuditLogChangeKeyTempoary AuditLogChangeKey = "temporary" - AuditLogChangeKeyDeaf AuditLogChangeKey = "deaf" - AuditLogChangeKeyMute AuditLogChangeKey = "mute" - AuditLogChangeKeyNick AuditLogChangeKey = "nick" - AuditLogChangeKeyAvatarHash AuditLogChangeKey = "avatar_hash" - AuditLogChangeKeyID AuditLogChangeKey = "id" - AuditLogChangeKeyType AuditLogChangeKey = "type" - AuditLogChangeKeyEnableEmoticons AuditLogChangeKey = "enable_emoticons" - AuditLogChangeKeyExpireBehavior AuditLogChangeKey = "expire_behavior" - AuditLogChangeKeyExpireGracePeriod AuditLogChangeKey = "expire_grace_period" -) - -// AuditLogOptions optional data for the AuditLog -// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info -type AuditLogOptions struct { - DeleteMemberDays string `json:"delete_member_days"` - MembersRemoved string `json:"members_removed"` - ChannelID string `json:"channel_id"` - MessageID string `json:"message_id"` - Count string `json:"count"` - ID string `json:"id"` - Type *AuditLogOptionsType `json:"type"` - RoleName string `json:"role_name"` -} - -// AuditLogOptionsType of the AuditLogOption -// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info -type AuditLogOptionsType string - -// Valid Types for AuditLogOptionsType -const ( - AuditLogOptionsTypeMember AuditLogOptionsType = "member" - AuditLogOptionsTypeRole AuditLogOptionsType = "role" -) - -// AuditLogAction is the Action of the AuditLog (see AuditLogAction* consts) -// https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events -type AuditLogAction int - -// Block contains Discord Audit Log Action Types -const ( - AuditLogActionGuildUpdate AuditLogAction = 1 - - AuditLogActionChannelCreate AuditLogAction = 10 - AuditLogActionChannelUpdate AuditLogAction = 11 - AuditLogActionChannelDelete AuditLogAction = 12 - AuditLogActionChannelOverwriteCreate AuditLogAction = 13 - AuditLogActionChannelOverwriteUpdate AuditLogAction = 14 - AuditLogActionChannelOverwriteDelete AuditLogAction = 15 - - AuditLogActionMemberKick AuditLogAction = 20 - AuditLogActionMemberPrune AuditLogAction = 21 - AuditLogActionMemberBanAdd AuditLogAction = 22 - AuditLogActionMemberBanRemove AuditLogAction = 23 - AuditLogActionMemberUpdate AuditLogAction = 24 - AuditLogActionMemberRoleUpdate AuditLogAction = 25 - - AuditLogActionRoleCreate AuditLogAction = 30 - AuditLogActionRoleUpdate AuditLogAction = 31 - AuditLogActionRoleDelete AuditLogAction = 32 - - AuditLogActionInviteCreate AuditLogAction = 40 - AuditLogActionInviteUpdate AuditLogAction = 41 - AuditLogActionInviteDelete AuditLogAction = 42 - - AuditLogActionWebhookCreate AuditLogAction = 50 - AuditLogActionWebhookUpdate AuditLogAction = 51 - AuditLogActionWebhookDelete AuditLogAction = 52 - - AuditLogActionEmojiCreate AuditLogAction = 60 - AuditLogActionEmojiUpdate AuditLogAction = 61 - AuditLogActionEmojiDelete AuditLogAction = 62 - - AuditLogActionMessageDelete AuditLogAction = 72 - AuditLogActionMessageBulkDelete AuditLogAction = 73 - AuditLogActionMessagePin AuditLogAction = 74 - AuditLogActionMessageUnpin AuditLogAction = 75 - - AuditLogActionIntegrationCreate AuditLogAction = 80 - AuditLogActionIntegrationUpdate AuditLogAction = 81 - AuditLogActionIntegrationDelete AuditLogAction = 82 -) - -// A UserGuildSettingsChannelOverride stores data for a channel override for a users guild settings. -type UserGuildSettingsChannelOverride struct { - Muted bool `json:"muted"` - MessageNotifications int `json:"message_notifications"` - ChannelID string `json:"channel_id"` -} - -// A UserGuildSettings stores data for a users guild settings. -type UserGuildSettings struct { - SupressEveryone bool `json:"suppress_everyone"` - Muted bool `json:"muted"` - MobilePush bool `json:"mobile_push"` - MessageNotifications int `json:"message_notifications"` - GuildID string `json:"guild_id"` - ChannelOverrides []*UserGuildSettingsChannelOverride `json:"channel_overrides"` -} - -// A UserGuildSettingsEdit stores data for editing UserGuildSettings -type UserGuildSettingsEdit struct { - SupressEveryone bool `json:"suppress_everyone"` - Muted bool `json:"muted"` - MobilePush bool `json:"mobile_push"` - MessageNotifications int `json:"message_notifications"` - ChannelOverrides map[string]*UserGuildSettingsChannelOverride `json:"channel_overrides"` -} - -// An APIErrorMessage is an api error message returned from discord -type APIErrorMessage struct { - Code int `json:"code"` - Message string `json:"message"` -} - -// Webhook stores the data for a webhook. -type Webhook struct { - ID string `json:"id"` - Type WebhookType `json:"type"` - GuildID string `json:"guild_id"` - ChannelID string `json:"channel_id"` - User *User `json:"user"` - Name string `json:"name"` - Avatar string `json:"avatar"` - Token string `json:"token"` - - // ApplicationID is the bot/OAuth2 application that created this webhook - ApplicationID string `json:"application_id,omitempty"` -} - -// WebhookType is the type of Webhook (see WebhookType* consts) in the Webhook struct -// https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types -type WebhookType int - -// Valid WebhookType values -const ( - WebhookTypeIncoming WebhookType = iota - WebhookTypeChannelFollower -) - -// WebhookParams is a struct for webhook params, used in the WebhookExecute command. -type WebhookParams struct { - Content string `json:"content,omitempty"` - Username string `json:"username,omitempty"` - AvatarURL string `json:"avatar_url,omitempty"` - TTS bool `json:"tts,omitempty"` - File *File `json:"-"` - Embeds []*MessageEmbed `json:"embeds,omitempty"` - AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` -} - -// MessageReaction stores the data for a message reaction. -type MessageReaction struct { - UserID string `json:"user_id"` - MessageID string `json:"message_id"` - Emoji Emoji `json:"emoji"` - ChannelID string `json:"channel_id"` - GuildID string `json:"guild_id,omitempty"` -} - -// GatewayBotResponse stores the data for the gateway/bot response -type GatewayBotResponse struct { - URL string `json:"url"` - Shards int `json:"shards"` -} - -// GatewayStatusUpdate is sent by the client to indicate a presence or status update -// https://discord.com/developers/docs/topics/gateway#update-status-gateway-status-update-structure -type GatewayStatusUpdate struct { - Since int `json:"since"` - Game Activity `json:"game"` - Status string `json:"status"` - AFK bool `json:"afk"` -} - -// Activity defines the Activity sent with GatewayStatusUpdate -// https://discord.com/developers/docs/topics/gateway#activity-object -type Activity struct { - Name string `json:"name"` - Type ActivityType `json:"type"` - URL string `json:"url,omitempty"` -} - -// ActivityType is the type of Activity (see ActivityType* consts) in the Activity struct -// https://discord.com/developers/docs/topics/gateway#activity-object-activity-types -type ActivityType int - -// Valid ActivityType values -const ( - ActivityTypeGame ActivityType = iota - ActivityTypeStreaming - ActivityTypeListening - // ActivityTypeWatching // not valid in this use case? - ActivityTypeCustom = 4 -) - -// Identify is sent during initial handshake with the discord gateway. -// https://discord.com/developers/docs/topics/gateway#identify -type Identify struct { - Token string `json:"token"` - Properties IdentifyProperties `json:"properties"` - Compress bool `json:"compress"` - LargeThreshold int `json:"large_threshold"` - Shard *[2]int `json:"shard,omitempty"` - Presence GatewayStatusUpdate `json:"presence,omitempty"` - GuildSubscriptions bool `json:"guild_subscriptions"` - Intents Intent `json:"intents"` -} - -// IdentifyProperties contains the "properties" portion of an Identify packet -// https://discord.com/developers/docs/topics/gateway#identify-identify-connection-properties -type IdentifyProperties struct { - OS string `json:"$os"` - Browser string `json:"$browser"` - Device string `json:"$device"` - Referer string `json:"$referer"` - ReferringDomain string `json:"$referring_domain"` -} - -// Constants for the different bit offsets of text channel permissions -const ( - // Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels - PermissionReadMessages = 1 << (iota + 10) - PermissionSendMessages - PermissionSendTTSMessages - PermissionManageMessages - PermissionEmbedLinks - PermissionAttachFiles - PermissionReadMessageHistory - PermissionMentionEveryone - PermissionUseExternalEmojis -) - -// Constants for the different bit offsets of voice permissions -const ( - PermissionVoiceConnect = 1 << (iota + 20) - PermissionVoiceSpeak - PermissionVoiceMuteMembers - PermissionVoiceDeafenMembers - PermissionVoiceMoveMembers - PermissionVoiceUseVAD - PermissionVoicePrioritySpeaker = 1 << (iota + 2) -) - -// Constants for general management. -const ( - PermissionChangeNickname = 1 << (iota + 26) - PermissionManageNicknames - PermissionManageRoles - PermissionManageWebhooks - PermissionManageEmojis -) - -// Constants for the different bit offsets of general permissions -const ( - PermissionCreateInstantInvite = 1 << iota - PermissionKickMembers - PermissionBanMembers - PermissionAdministrator - PermissionManageChannels - PermissionManageServer - PermissionAddReactions - PermissionViewAuditLogs - PermissionViewChannel = 1 << (iota + 2) - - PermissionAllText = PermissionViewChannel | - PermissionSendMessages | - PermissionSendTTSMessages | - PermissionManageMessages | - PermissionEmbedLinks | - PermissionAttachFiles | - PermissionReadMessageHistory | - PermissionMentionEveryone - PermissionAllVoice = PermissionViewChannel | - PermissionVoiceConnect | - PermissionVoiceSpeak | - PermissionVoiceMuteMembers | - PermissionVoiceDeafenMembers | - PermissionVoiceMoveMembers | - PermissionVoiceUseVAD | - PermissionVoicePrioritySpeaker - PermissionAllChannel = PermissionAllText | - PermissionAllVoice | - PermissionCreateInstantInvite | - PermissionManageRoles | - PermissionManageChannels | - PermissionAddReactions | - PermissionViewAuditLogs - PermissionAll = PermissionAllChannel | - PermissionKickMembers | - PermissionBanMembers | - PermissionManageServer | - PermissionAdministrator | - PermissionManageWebhooks | - PermissionManageEmojis -) - -// Block contains Discord JSON Error Response codes -const ( - ErrCodeUnknownAccount = 10001 - ErrCodeUnknownApplication = 10002 - ErrCodeUnknownChannel = 10003 - ErrCodeUnknownGuild = 10004 - ErrCodeUnknownIntegration = 10005 - ErrCodeUnknownInvite = 10006 - ErrCodeUnknownMember = 10007 - ErrCodeUnknownMessage = 10008 - ErrCodeUnknownOverwrite = 10009 - ErrCodeUnknownProvider = 10010 - ErrCodeUnknownRole = 10011 - ErrCodeUnknownToken = 10012 - ErrCodeUnknownUser = 10013 - ErrCodeUnknownEmoji = 10014 - ErrCodeUnknownWebhook = 10015 - ErrCodeUnknownBan = 10026 - - ErrCodeBotsCannotUseEndpoint = 20001 - ErrCodeOnlyBotsCanUseEndpoint = 20002 - - ErrCodeMaximumGuildsReached = 30001 - ErrCodeMaximumFriendsReached = 30002 - ErrCodeMaximumPinsReached = 30003 - ErrCodeMaximumGuildRolesReached = 30005 - ErrCodeTooManyReactions = 30010 - - ErrCodeUnauthorized = 40001 - - ErrCodeMissingAccess = 50001 - ErrCodeInvalidAccountType = 50002 - ErrCodeCannotExecuteActionOnDMChannel = 50003 - ErrCodeEmbedDisabled = 50004 - ErrCodeCannotEditFromAnotherUser = 50005 - ErrCodeCannotSendEmptyMessage = 50006 - ErrCodeCannotSendMessagesToThisUser = 50007 - ErrCodeCannotSendMessagesInVoiceChannel = 50008 - ErrCodeChannelVerificationLevelTooHigh = 50009 - ErrCodeOAuth2ApplicationDoesNotHaveBot = 50010 - ErrCodeOAuth2ApplicationLimitReached = 50011 - ErrCodeInvalidOAuthState = 50012 - ErrCodeMissingPermissions = 50013 - ErrCodeInvalidAuthenticationToken = 50014 - ErrCodeNoteTooLong = 50015 - ErrCodeTooFewOrTooManyMessagesToDelete = 50016 - ErrCodeCanOnlyPinMessageToOriginatingChannel = 50019 - ErrCodeCannotExecuteActionOnSystemMessage = 50021 - ErrCodeMessageProvidedTooOldForBulkDelete = 50034 - ErrCodeInvalidFormBody = 50035 - ErrCodeInviteAcceptedToGuildApplicationsBotNotIn = 50036 - - ErrCodeReactionBlocked = 90001 -) - -// Intent is the type of a Gateway Intent -// https://discord.com/developers/docs/topics/gateway#gateway-intents -type Intent int - -// Constants for the different bit offsets of intents -const ( - IntentsGuilds Intent = 1 << iota - IntentsGuildMembers - IntentsGuildBans - IntentsGuildEmojis - IntentsGuildIntegrations - IntentsGuildWebhooks - IntentsGuildInvites - IntentsGuildVoiceStates - IntentsGuildPresences - IntentsGuildMessages - IntentsGuildMessageReactions - IntentsGuildMessageTyping - IntentsDirectMessages - IntentsDirectMessageReactions - IntentsDirectMessageTyping - - IntentsAllWithoutPrivileged = IntentsGuilds | - IntentsGuildBans | - IntentsGuildEmojis | - IntentsGuildIntegrations | - IntentsGuildWebhooks | - IntentsGuildInvites | - IntentsGuildVoiceStates | - IntentsGuildMessages | - IntentsGuildMessageReactions | - IntentsGuildMessageTyping | - IntentsDirectMessages | - IntentsDirectMessageReactions | - IntentsDirectMessageTyping - IntentsAll = IntentsAllWithoutPrivileged | - IntentsGuildMembers | - IntentsGuildPresences - IntentsNone Intent = 0 -) - -// MakeIntent used to help convert a gateway intent value for use in the Identify structure; -// this was useful to help support the use of a pointer type when intents were optional. -// This is now a no-op, and is not necessary to use. -func MakeIntent(intents Intent) Intent { - return intents -} diff --git a/vendor/github.com/matterbridge/discordgo/util.go b/vendor/github.com/matterbridge/discordgo/util.go deleted file mode 100644 index 8a2b2e01..00000000 --- a/vendor/github.com/matterbridge/discordgo/util.go +++ /dev/null @@ -1,17 +0,0 @@ -package discordgo - -import ( - "strconv" - "time" -) - -// SnowflakeTimestamp returns the creation time of a Snowflake ID relative to the creation of Discord. -func SnowflakeTimestamp(ID string) (t time.Time, err error) { - i, err := strconv.ParseInt(ID, 10, 64) - if err != nil { - return - } - timestamp := (i >> 22) + 1420070400000 - t = time.Unix(0, timestamp*1000000) - return -} diff --git a/vendor/modules.txt b/vendor/modules.txt index a129a55f..030df42b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -51,6 +51,9 @@ github.com/av-elier/go-decimal-to-rational # github.com/blang/semver v3.5.1+incompatible ## explicit github.com/blang/semver +# github.com/bwmarrin/discordgo v0.24.0 +## explicit; go 1.13 +github.com/bwmarrin/discordgo # github.com/d5/tengo/v2 v2.10.0 ## explicit; go 1.13 github.com/d5/tengo/v2 @@ -194,9 +197,6 @@ github.com/matrix-org/gomatrix github.com/matterbridge/Rocket.Chat.Go.SDK/models github.com/matterbridge/Rocket.Chat.Go.SDK/realtime github.com/matterbridge/Rocket.Chat.Go.SDK/rest -# github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7 -## explicit; go 1.10 -github.com/matterbridge/discordgo # github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be ## explicit github.com/matterbridge/go-xmpp