diff options
Diffstat (limited to 'vendor/github.com/bwmarrin')
23 files changed, 0 insertions, 9144 deletions
diff --git a/vendor/github.com/bwmarrin/discordgo/.travis.yml b/vendor/github.com/bwmarrin/discordgo/.travis.yml deleted file mode 100644 index 2656ae53..00000000 --- a/vendor/github.com/bwmarrin/discordgo/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go -go: - - 1.9.x - - 1.10.x - - 1.11.x -install: - - go get github.com/bwmarrin/discordgo - - go get -v . - - go get -v golang.org/x/lint/golint -script: - - diff <(gofmt -d .) <(echo -n) - - go vet -x ./... - - golint -set_exit_status ./... - - go test -v -race ./... diff --git a/vendor/github.com/bwmarrin/discordgo/LICENSE b/vendor/github.com/bwmarrin/discordgo/LICENSE deleted file mode 100644 index 8d062ea5..00000000 --- a/vendor/github.com/bwmarrin/discordgo/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2015, Bruce Marriner -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of discordgo nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/vendor/github.com/bwmarrin/discordgo/README.md b/vendor/github.com/bwmarrin/discordgo/README.md deleted file mode 100644 index 7a83b9e2..00000000 --- a/vendor/github.com/bwmarrin/discordgo/README.md +++ /dev/null @@ -1,130 +0,0 @@ -# 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://discordapp.com/invite/discord-api) - -<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png"> - -DiscordGo is a [Go](https://golang.org/) package that provides low level -bindings to the [Discord](https://discordapp.com/) chat client API. DiscordGo -has nearly complete support for all of the Discord API endpoints, websocket -interface, and voice interface. - -If you would like to help the DiscordGo package please use -[this link](https://discordapp.com/oauth2/authorize?client_id=173113690092994561&scope=bot) -to add the official DiscordGo test bot **dgo** to your server. This provides -indispensable help to this project. - -* See [dgVoice](https://github.com/bwmarrin/dgvoice) package for an example of -additional voice helper functions and features for DiscordGo. - -* See [dca](https://github.com/bwmarrin/dca) for an **experimental** stand alone -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.** - -## Getting Started - -### master vs develop Branch -* The master branch represents the latest released version of DiscordGo. This -branch will always have a stable and tested version of the library. Each release -is tagged and you can easily download a specific release and view release notes -on the github [releases](https://github.com/bwmarrin/discordgo/releases) page. - -* The develop branch is where all development happens and almost always has -new features over the master branch. However breaking changes are frequently -added to develop and even sometimes bugs are introduced. Bugs get fixed and -the breaking changes get documented before pushing to master. - -*So, what should you use?* - -If you can accept the constant changing nature of *develop*, it is the -recommended branch to use. Otherwise, if you want to tail behind development -slightly and have a more stable package with documented releases, use *master*. - -### Installing - -This assumes you already have a working Go environment, if not please see -[this page](https://golang.org/doc/install) first. - -`go get` *will always pull the latest released version from the master branch.* - -```sh -go get github.com/bwmarrin/discordgo -``` - -If you want to use the develop branch, follow these steps next. - -```sh -cd $GOPATH/src/github.com/bwmarrin/discordgo -git checkout develop -``` - -### Usage - -Import the package into your project. - -```go -import "github.com/bwmarrin/discordgo" -``` - -Construct a new Discord client which can be used to access the variety of -Discord API functions and to set callback functions for Discord events. - -```go -discord, err := discordgo.New("Bot " + "authentication token") -``` - -See Documentation and Examples below for more detailed information. - - -## Documentation - -**NOTICE** : This library and the Discord API are unfinished. -Because of that there may be major changes to library in the future. - -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) -- Hand crafted documentation coming eventually. - - -## Examples - -Below is a list of examples and other projects using DiscordGo. Please submit -an issue if you would like your project added or removed from this list. - -- [DiscordGo Examples](https://github.com/bwmarrin/discordgo/tree/master/examples) - A collection of example programs written with DiscordGo -- [Awesome DiscordGo](https://github.com/bwmarrin/discordgo/wiki/Awesome-DiscordGo) - A curated list of high quality projects using DiscordGo - -## Troubleshooting -For help with common problems please reference the -[Troubleshooting](https://github.com/bwmarrin/discordgo/wiki/Troubleshooting) -section of the project wiki. - - -## Contributing -Contributions are very welcomed, however please follow the below guidelines. - -- First open an issue describing the bug or enhancement so it can be -discussed. -- Fork the develop branch and make your changes. -- Try to match current naming conventions as closely as possible. -- This package is intended to be a low level direct mapping of the Discord API, -so please avoid adding enhancements outside of that scope without first -discussing it. -- Create a Pull Request with your changes against the develop branch. - - -## List of Discord APIs - -See [this chart](https://abal.moe/Discord/Libraries.html) for a feature -comparison and list of other Discord API libraries. - -## Special Thanks - -[Chris Rhodes](https://github.com/iopred) - For the DiscordGo logo and tons of PRs. diff --git a/vendor/github.com/bwmarrin/discordgo/discord.go b/vendor/github.com/bwmarrin/discordgo/discord.go deleted file mode 100644 index ac4e5426..00000000 --- a/vendor/github.com/bwmarrin/discordgo/discord.go +++ /dev/null @@ -1,147 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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" - "time" -) - -// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/) -const VERSION = "0.20.2" - -// 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, -// 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 <token>"` -// 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/bwmarrin/discordgo, v" + VERSION + ")", - sequence: new(int64), - LastHeartbeatAck: time.Now().UTC(), - } - - // 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.Token = v[2] - } - - 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.Token = v - } 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.Token = auth - } else { - err = s.Login(auth, pass) - if err != nil || s.Token == "" { - if s.MFA { - err = ErrMFA - } else { - err = fmt.Errorf("Unable to fetch discord authentication token. %v", err) - } - return - } - } - - // The Session is now able to have RestAPI methods called on it. - // It is recommended that you now call Open() so that events will trigger. - - return -} diff --git a/vendor/github.com/bwmarrin/discordgo/endpoints.go b/vendor/github.com/bwmarrin/discordgo/endpoints.go deleted file mode 100644 index 6f86b67f..00000000 --- a/vendor/github.com/bwmarrin/discordgo/endpoints.go +++ /dev/null @@ -1,150 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 = "6" - -// Known Discord API Endpoints. -var ( - EndpointStatus = "https://status.discordapp.com/api/v2/" - EndpointSm = EndpointStatus + "scheduled-maintenances/" - EndpointSmActive = EndpointSm + "active.json" - EndpointSmUpcoming = EndpointSm + "upcoming.json" - - EndpointDiscord = "https://discordapp.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" } - EndpointGuildEmbed = func(gID string) string { return EndpointGuilds + gID + "/embed" } - 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 } - - 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 EndpointAPI + "emojis/" + eID + ".png" } - EndpointEmojiAnimated = func(eID string) string { return EndpointAPI + "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/bwmarrin/discordgo/event.go b/vendor/github.com/bwmarrin/discordgo/event.go deleted file mode 100644 index 97cc00a2..00000000 --- a/vendor/github.com/bwmarrin/discordgo/event.go +++ /dev/null @@ -1,247 +0,0 @@ -package discordgo - -// EventHandler is an interface for Discord events. -type EventHandler interface { - // Type returns the type of event this handler belongs to. - Type() string - - // Handle is called whenever an event of Type() happens. - // It is the receivers responsibility to type assert that the interface - // is the expected struct. - Handle(*Session, interface{}) -} - -// EventInterfaceProvider is an interface for providing empty interfaces for -// Discord events. -type EventInterfaceProvider interface { - // Type is the type of event this handler belongs to. - Type() string - - // New returns a new instance of the struct this event handler handles. - // This is called once per event. - // The struct is provided to all handlers of the same Type(). - New() interface{} -} - -// interfaceEventType is the event handler type for interface{} events. -const interfaceEventType = "__INTERFACE__" - -// interfaceEventHandler is an event handler for interface{} events. -type interfaceEventHandler func(*Session, interface{}) - -// Type returns the event type for interface{} events. -func (eh interfaceEventHandler) Type() string { - return interfaceEventType -} - -// Handle is the handler for an interface{} event. -func (eh interfaceEventHandler) Handle(s *Session, i interface{}) { - eh(s, i) -} - -var registeredInterfaceProviders = map[string]EventInterfaceProvider{} - -// registerInterfaceProvider registers a provider so that DiscordGo can -// access it's New() method. -func registerInterfaceProvider(eh EventInterfaceProvider) { - if _, ok := registeredInterfaceProviders[eh.Type()]; ok { - return - // XXX: - // if we should error here, we need to do something with it. - // fmt.Errorf("event %s already registered", eh.Type()) - } - registeredInterfaceProviders[eh.Type()] = eh - return -} - -// eventHandlerInstance is a wrapper around an event handler, as functions -// cannot be compared directly. -type eventHandlerInstance struct { - eventHandler EventHandler -} - -// addEventHandler adds an event handler that will be fired anytime -// the Discord WSAPI matching eventHandler.Type() fires. -func (s *Session) addEventHandler(eventHandler EventHandler) func() { - s.handlersMu.Lock() - defer s.handlersMu.Unlock() - - if s.handlers == nil { - s.handlers = map[string][]*eventHandlerInstance{} - } - - ehi := &eventHandlerInstance{eventHandler} - s.handlers[eventHandler.Type()] = append(s.handlers[eventHandler.Type()], ehi) - - return func() { - s.removeEventHandlerInstance(eventHandler.Type(), ehi) - } -} - -// addEventHandler adds an event handler that will be fired the next time -// the Discord WSAPI matching eventHandler.Type() fires. -func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() { - s.handlersMu.Lock() - defer s.handlersMu.Unlock() - - if s.onceHandlers == nil { - s.onceHandlers = map[string][]*eventHandlerInstance{} - } - - ehi := &eventHandlerInstance{eventHandler} - s.onceHandlers[eventHandler.Type()] = append(s.onceHandlers[eventHandler.Type()], ehi) - - return func() { - s.removeEventHandlerInstance(eventHandler.Type(), ehi) - } -} - -// AddHandler allows you to add an event handler that will be fired anytime -// the Discord WSAPI event that matches the function fires. -// The first parameter is a *Session, and the second parameter is a pointer -// to a struct corresponding to the event for which you want to listen. -// -// eg: -// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) { -// }) -// -// or: -// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) { -// }) -// -// List of events can be found at this page, with corresponding names in the -// library for each event: https://discordapp.com/developers/docs/topics/gateway#event-names -// There are also synthetic events fired by the library internally which are -// available for handling, like Connect, Disconnect, and RateLimit. -// events.go contains all of the Discord WSAPI and synthetic events that can be handled. -// -// The return value of this method is a function, that when called will remove the -// event handler. -func (s *Session) AddHandler(handler interface{}) func() { - eh := handlerForInterface(handler) - - if eh == nil { - s.log(LogError, "Invalid handler type, handler will never be called") - return func() {} - } - - return s.addEventHandler(eh) -} - -// AddHandlerOnce allows you to add an event handler that will be fired the next time -// the Discord WSAPI event that matches the function fires. -// See AddHandler for more details. -func (s *Session) AddHandlerOnce(handler interface{}) func() { - eh := handlerForInterface(handler) - - if eh == nil { - s.log(LogError, "Invalid handler type, handler will never be called") - return func() {} - } - - return s.addEventHandlerOnce(eh) -} - -// removeEventHandler instance removes an event handler instance. -func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) { - s.handlersMu.Lock() - defer s.handlersMu.Unlock() - - handlers := s.handlers[t] - for i := range handlers { - if handlers[i] == ehi { - s.handlers[t] = append(handlers[:i], handlers[i+1:]...) - } - } - - onceHandlers := s.onceHandlers[t] - for i := range onceHandlers { - if onceHandlers[i] == ehi { - s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...) - } - } -} - -// Handles calling permanent and once handlers for an event type. -func (s *Session) handle(t string, i interface{}) { - for _, eh := range s.handlers[t] { - if s.SyncEvents { - eh.eventHandler.Handle(s, i) - } else { - go eh.eventHandler.Handle(s, i) - } - } - - if len(s.onceHandlers[t]) > 0 { - for _, eh := range s.onceHandlers[t] { - if s.SyncEvents { - eh.eventHandler.Handle(s, i) - } else { - go eh.eventHandler.Handle(s, i) - } - } - s.onceHandlers[t] = nil - } -} - -// Handles an event type by calling internal methods, firing handlers and firing the -// interface{} event. -func (s *Session) handleEvent(t string, i interface{}) { - s.handlersMu.RLock() - defer s.handlersMu.RUnlock() - - // All events are dispatched internally first. - s.onInterface(i) - - // Then they are dispatched to anyone handling interface{} events. - s.handle(interfaceEventType, i) - - // Finally they are dispatched to any typed handlers. - s.handle(t, i) -} - -// setGuildIds will set the GuildID on all the members of a guild. -// This is done as event data does not have it set. -func setGuildIds(g *Guild) { - for _, c := range g.Channels { - c.GuildID = g.ID - } - - for _, m := range g.Members { - m.GuildID = g.ID - } - - for _, vs := range g.VoiceStates { - vs.GuildID = g.ID - } -} - -// onInterface handles all internal events and routes them to the appropriate internal handler. -func (s *Session) onInterface(i interface{}) { - switch t := i.(type) { - case *Ready: - for _, g := range t.Guilds { - setGuildIds(g) - } - s.onReady(t) - case *GuildCreate: - setGuildIds(t.Guild) - case *GuildUpdate: - setGuildIds(t.Guild) - case *VoiceServerUpdate: - go s.onVoiceServerUpdate(t) - case *VoiceStateUpdate: - go s.onVoiceStateUpdate(t) - } - err := s.State.OnInterface(s, i) - if err != nil { - s.log(LogDebug, "error dispatching internal event, %s", err) - } -} - -// onReady handles the ready event. -func (s *Session) onReady(r *Ready) { - - // Store the SessionID within the Session struct. - s.sessionID = r.SessionID -} diff --git a/vendor/github.com/bwmarrin/discordgo/eventhandlers.go b/vendor/github.com/bwmarrin/discordgo/eventhandlers.go deleted file mode 100644 index d2b9a98b..00000000 --- a/vendor/github.com/bwmarrin/discordgo/eventhandlers.go +++ /dev/null @@ -1,1054 +0,0 @@ -// Code generated by \"eventhandlers\"; DO NOT EDIT -// See events.go - -package discordgo - -// Following are all the event types. -// 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" -) - -// channelCreateEventHandler is an event handler for ChannelCreate events. -type channelCreateEventHandler func(*Session, *ChannelCreate) - -// Type returns the event type for ChannelCreate events. -func (eh channelCreateEventHandler) Type() string { - return channelCreateEventType -} - -// New returns a new instance of ChannelCreate. -func (eh channelCreateEventHandler) New() interface{} { - return &ChannelCreate{} -} - -// Handle is the handler for ChannelCreate events. -func (eh channelCreateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*ChannelCreate); ok { - eh(s, t) - } -} - -// channelDeleteEventHandler is an event handler for ChannelDelete events. -type channelDeleteEventHandler func(*Session, *ChannelDelete) - -// Type returns the event type for ChannelDelete events. -func (eh channelDeleteEventHandler) Type() string { - return channelDeleteEventType -} - -// New returns a new instance of ChannelDelete. -func (eh channelDeleteEventHandler) New() interface{} { - return &ChannelDelete{} -} - -// Handle is the handler for ChannelDelete events. -func (eh channelDeleteEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*ChannelDelete); ok { - eh(s, t) - } -} - -// channelPinsUpdateEventHandler is an event handler for ChannelPinsUpdate events. -type channelPinsUpdateEventHandler func(*Session, *ChannelPinsUpdate) - -// Type returns the event type for ChannelPinsUpdate events. -func (eh channelPinsUpdateEventHandler) Type() string { - return channelPinsUpdateEventType -} - -// New returns a new instance of ChannelPinsUpdate. -func (eh channelPinsUpdateEventHandler) New() interface{} { - return &ChannelPinsUpdate{} -} - -// Handle is the handler for ChannelPinsUpdate events. -func (eh channelPinsUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*ChannelPinsUpdate); ok { - eh(s, t) - } -} - -// channelUpdateEventHandler is an event handler for ChannelUpdate events. -type channelUpdateEventHandler func(*Session, *ChannelUpdate) - -// Type returns the event type for ChannelUpdate events. -func (eh channelUpdateEventHandler) Type() string { - return channelUpdateEventType -} - -// New returns a new instance of ChannelUpdate. -func (eh channelUpdateEventHandler) New() interface{} { - return &ChannelUpdate{} -} - -// Handle is the handler for ChannelUpdate events. -func (eh channelUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*ChannelUpdate); ok { - eh(s, t) - } -} - -// connectEventHandler is an event handler for Connect events. -type connectEventHandler func(*Session, *Connect) - -// Type returns the event type for Connect events. -func (eh connectEventHandler) Type() string { - return connectEventType -} - -// Handle is the handler for Connect events. -func (eh connectEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*Connect); ok { - eh(s, t) - } -} - -// disconnectEventHandler is an event handler for Disconnect events. -type disconnectEventHandler func(*Session, *Disconnect) - -// Type returns the event type for Disconnect events. -func (eh disconnectEventHandler) Type() string { - return disconnectEventType -} - -// Handle is the handler for Disconnect events. -func (eh disconnectEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*Disconnect); ok { - eh(s, t) - } -} - -// eventEventHandler is an event handler for Event events. -type eventEventHandler func(*Session, *Event) - -// Type returns the event type for Event events. -func (eh eventEventHandler) Type() string { - return eventEventType -} - -// Handle is the handler for Event events. -func (eh eventEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*Event); ok { - eh(s, t) - } -} - -// guildBanAddEventHandler is an event handler for GuildBanAdd events. -type guildBanAddEventHandler func(*Session, *GuildBanAdd) - -// Type returns the event type for GuildBanAdd events. -func (eh guildBanAddEventHandler) Type() string { - return guildBanAddEventType -} - -// New returns a new instance of GuildBanAdd. -func (eh guildBanAddEventHandler) New() interface{} { - return &GuildBanAdd{} -} - -// Handle is the handler for GuildBanAdd events. -func (eh guildBanAddEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildBanAdd); ok { - eh(s, t) - } -} - -// guildBanRemoveEventHandler is an event handler for GuildBanRemove events. -type guildBanRemoveEventHandler func(*Session, *GuildBanRemove) - -// Type returns the event type for GuildBanRemove events. -func (eh guildBanRemoveEventHandler) Type() string { - return guildBanRemoveEventType -} - -// New returns a new instance of GuildBanRemove. -func (eh guildBanRemoveEventHandler) New() interface{} { - return &GuildBanRemove{} -} - -// Handle is the handler for GuildBanRemove events. -func (eh guildBanRemoveEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildBanRemove); ok { - eh(s, t) - } -} - -// guildCreateEventHandler is an event handler for GuildCreate events. -type guildCreateEventHandler func(*Session, *GuildCreate) - -// Type returns the event type for GuildCreate events. -func (eh guildCreateEventHandler) Type() string { - return guildCreateEventType -} - -// New returns a new instance of GuildCreate. -func (eh guildCreateEventHandler) New() interface{} { - return &GuildCreate{} -} - -// Handle is the handler for GuildCreate events. -func (eh guildCreateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildCreate); ok { - eh(s, t) - } -} - -// guildDeleteEventHandler is an event handler for GuildDelete events. -type guildDeleteEventHandler func(*Session, *GuildDelete) - -// Type returns the event type for GuildDelete events. -func (eh guildDeleteEventHandler) Type() string { - return guildDeleteEventType -} - -// New returns a new instance of GuildDelete. -func (eh guildDeleteEventHandler) New() interface{} { - return &GuildDelete{} -} - -// Handle is the handler for GuildDelete events. -func (eh guildDeleteEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildDelete); ok { - eh(s, t) - } -} - -// guildEmojisUpdateEventHandler is an event handler for GuildEmojisUpdate events. -type guildEmojisUpdateEventHandler func(*Session, *GuildEmojisUpdate) - -// Type returns the event type for GuildEmojisUpdate events. -func (eh guildEmojisUpdateEventHandler) Type() string { - return guildEmojisUpdateEventType -} - -// New returns a new instance of GuildEmojisUpdate. -func (eh guildEmojisUpdateEventHandler) New() interface{} { - return &GuildEmojisUpdate{} -} - -// Handle is the handler for GuildEmojisUpdate events. -func (eh guildEmojisUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildEmojisUpdate); ok { - eh(s, t) - } -} - -// guildIntegrationsUpdateEventHandler is an event handler for GuildIntegrationsUpdate events. -type guildIntegrationsUpdateEventHandler func(*Session, *GuildIntegrationsUpdate) - -// Type returns the event type for GuildIntegrationsUpdate events. -func (eh guildIntegrationsUpdateEventHandler) Type() string { - return guildIntegrationsUpdateEventType -} - -// New returns a new instance of GuildIntegrationsUpdate. -func (eh guildIntegrationsUpdateEventHandler) New() interface{} { - return &GuildIntegrationsUpdate{} -} - -// Handle is the handler for GuildIntegrationsUpdate events. -func (eh guildIntegrationsUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildIntegrationsUpdate); ok { - eh(s, t) - } -} - -// guildMemberAddEventHandler is an event handler for GuildMemberAdd events. -type guildMemberAddEventHandler func(*Session, *GuildMemberAdd) - -// Type returns the event type for GuildMemberAdd events. -func (eh guildMemberAddEventHandler) Type() string { - return guildMemberAddEventType -} - -// New returns a new instance of GuildMemberAdd. -func (eh guildMemberAddEventHandler) New() interface{} { - return &GuildMemberAdd{} -} - -// Handle is the handler for GuildMemberAdd events. -func (eh guildMemberAddEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildMemberAdd); ok { - eh(s, t) - } -} - -// guildMemberRemoveEventHandler is an event handler for GuildMemberRemove events. -type guildMemberRemoveEventHandler func(*Session, *GuildMemberRemove) - -// Type returns the event type for GuildMemberRemove events. -func (eh guildMemberRemoveEventHandler) Type() string { - return guildMemberRemoveEventType -} - -// New returns a new instance of GuildMemberRemove. -func (eh guildMemberRemoveEventHandler) New() interface{} { - return &GuildMemberRemove{} -} - -// Handle is the handler for GuildMemberRemove events. -func (eh guildMemberRemoveEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildMemberRemove); ok { - eh(s, t) - } -} - -// guildMemberUpdateEventHandler is an event handler for GuildMemberUpdate events. -type guildMemberUpdateEventHandler func(*Session, *GuildMemberUpdate) - -// Type returns the event type for GuildMemberUpdate events. -func (eh guildMemberUpdateEventHandler) Type() string { - return guildMemberUpdateEventType -} - -// New returns a new instance of GuildMemberUpdate. -func (eh guildMemberUpdateEventHandler) New() interface{} { - return &GuildMemberUpdate{} -} - -// Handle is the handler for GuildMemberUpdate events. -func (eh guildMemberUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildMemberUpdate); ok { - eh(s, t) - } -} - -// guildMembersChunkEventHandler is an event handler for GuildMembersChunk events. -type guildMembersChunkEventHandler func(*Session, *GuildMembersChunk) - -// Type returns the event type for GuildMembersChunk events. -func (eh guildMembersChunkEventHandler) Type() string { - return guildMembersChunkEventType -} - -// New returns a new instance of GuildMembersChunk. -func (eh guildMembersChunkEventHandler) New() interface{} { - return &GuildMembersChunk{} -} - -// Handle is the handler for GuildMembersChunk events. -func (eh guildMembersChunkEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildMembersChunk); ok { - eh(s, t) - } -} - -// guildRoleCreateEventHandler is an event handler for GuildRoleCreate events. -type guildRoleCreateEventHandler func(*Session, *GuildRoleCreate) - -// Type returns the event type for GuildRoleCreate events. -func (eh guildRoleCreateEventHandler) Type() string { - return guildRoleCreateEventType -} - -// New returns a new instance of GuildRoleCreate. -func (eh guildRoleCreateEventHandler) New() interface{} { - return &GuildRoleCreate{} -} - -// Handle is the handler for GuildRoleCreate events. -func (eh guildRoleCreateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildRoleCreate); ok { - eh(s, t) - } -} - -// guildRoleDeleteEventHandler is an event handler for GuildRoleDelete events. -type guildRoleDeleteEventHandler func(*Session, *GuildRoleDelete) - -// Type returns the event type for GuildRoleDelete events. -func (eh guildRoleDeleteEventHandler) Type() string { - return guildRoleDeleteEventType -} - -// New returns a new instance of GuildRoleDelete. -func (eh guildRoleDeleteEventHandler) New() interface{} { - return &GuildRoleDelete{} -} - -// Handle is the handler for GuildRoleDelete events. -func (eh guildRoleDeleteEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildRoleDelete); ok { - eh(s, t) - } -} - -// guildRoleUpdateEventHandler is an event handler for GuildRoleUpdate events. -type guildRoleUpdateEventHandler func(*Session, *GuildRoleUpdate) - -// Type returns the event type for GuildRoleUpdate events. -func (eh guildRoleUpdateEventHandler) Type() string { - return guildRoleUpdateEventType -} - -// New returns a new instance of GuildRoleUpdate. -func (eh guildRoleUpdateEventHandler) New() interface{} { - return &GuildRoleUpdate{} -} - -// Handle is the handler for GuildRoleUpdate events. -func (eh guildRoleUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildRoleUpdate); ok { - eh(s, t) - } -} - -// guildUpdateEventHandler is an event handler for GuildUpdate events. -type guildUpdateEventHandler func(*Session, *GuildUpdate) - -// Type returns the event type for GuildUpdate events. -func (eh guildUpdateEventHandler) Type() string { - return guildUpdateEventType -} - -// New returns a new instance of GuildUpdate. -func (eh guildUpdateEventHandler) New() interface{} { - return &GuildUpdate{} -} - -// Handle is the handler for GuildUpdate events. -func (eh guildUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*GuildUpdate); ok { - eh(s, t) - } -} - -// messageAckEventHandler is an event handler for MessageAck events. -type messageAckEventHandler func(*Session, *MessageAck) - -// Type returns the event type for MessageAck events. -func (eh messageAckEventHandler) Type() string { - return messageAckEventType -} - -// New returns a new instance of MessageAck. -func (eh messageAckEventHandler) New() interface{} { - return &MessageAck{} -} - -// Handle is the handler for MessageAck events. -func (eh messageAckEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*MessageAck); ok { - eh(s, t) - } -} - -// messageCreateEventHandler is an event handler for MessageCreate events. -type messageCreateEventHandler func(*Session, *MessageCreate) - -// Type returns the event type for MessageCreate events. -func (eh messageCreateEventHandler) Type() string { - return messageCreateEventType -} - -// New returns a new instance of MessageCreate. -func (eh messageCreateEventHandler) New() interface{} { - return &MessageCreate{} -} - -// Handle is the handler for MessageCreate events. -func (eh messageCreateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*MessageCreate); ok { - eh(s, t) - } -} - -// messageDeleteEventHandler is an event handler for MessageDelete events. -type messageDeleteEventHandler func(*Session, *MessageDelete) - -// Type returns the event type for MessageDelete events. -func (eh messageDeleteEventHandler) Type() string { - return messageDeleteEventType -} - -// New returns a new instance of MessageDelete. -func (eh messageDeleteEventHandler) New() interface{} { - return &MessageDelete{} -} - -// Handle is the handler for MessageDelete events. -func (eh messageDeleteEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*MessageDelete); ok { - eh(s, t) - } -} - -// messageDeleteBulkEventHandler is an event handler for MessageDeleteBulk events. -type messageDeleteBulkEventHandler func(*Session, *MessageDeleteBulk) - -// Type returns the event type for MessageDeleteBulk events. -func (eh messageDeleteBulkEventHandler) Type() string { - return messageDeleteBulkEventType -} - -// New returns a new instance of MessageDeleteBulk. -func (eh messageDeleteBulkEventHandler) New() interface{} { - return &MessageDeleteBulk{} -} - -// Handle is the handler for MessageDeleteBulk events. -func (eh messageDeleteBulkEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*MessageDeleteBulk); ok { - eh(s, t) - } -} - -// messageReactionAddEventHandler is an event handler for MessageReactionAdd events. -type messageReactionAddEventHandler func(*Session, *MessageReactionAdd) - -// Type returns the event type for MessageReactionAdd events. -func (eh messageReactionAddEventHandler) Type() string { - return messageReactionAddEventType -} - -// New returns a new instance of MessageReactionAdd. -func (eh messageReactionAddEventHandler) New() interface{} { - return &MessageReactionAdd{} -} - -// Handle is the handler for MessageReactionAdd events. -func (eh messageReactionAddEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*MessageReactionAdd); ok { - eh(s, t) - } -} - -// messageReactionRemoveEventHandler is an event handler for MessageReactionRemove events. -type messageReactionRemoveEventHandler func(*Session, *MessageReactionRemove) - -// Type returns the event type for MessageReactionRemove events. -func (eh messageReactionRemoveEventHandler) Type() string { - return messageReactionRemoveEventType -} - -// New returns a new instance of MessageReactionRemove. -func (eh messageReactionRemoveEventHandler) New() interface{} { - return &MessageReactionRemove{} -} - -// Handle is the handler for MessageReactionRemove events. -func (eh messageReactionRemoveEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*MessageReactionRemove); ok { - eh(s, t) - } -} - -// messageReactionRemoveAllEventHandler is an event handler for MessageReactionRemoveAll events. -type messageReactionRemoveAllEventHandler func(*Session, *MessageReactionRemoveAll) - -// Type returns the event type for MessageReactionRemoveAll events. -func (eh messageReactionRemoveAllEventHandler) Type() string { - return messageReactionRemoveAllEventType -} - -// New returns a new instance of MessageReactionRemoveAll. -func (eh messageReactionRemoveAllEventHandler) New() interface{} { - return &MessageReactionRemoveAll{} -} - -// Handle is the handler for MessageReactionRemoveAll events. -func (eh messageReactionRemoveAllEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*MessageReactionRemoveAll); ok { - eh(s, t) - } -} - -// messageUpdateEventHandler is an event handler for MessageUpdate events. -type messageUpdateEventHandler func(*Session, *MessageUpdate) - -// Type returns the event type for MessageUpdate events. -func (eh messageUpdateEventHandler) Type() string { - return messageUpdateEventType -} - -// New returns a new instance of MessageUpdate. -func (eh messageUpdateEventHandler) New() interface{} { - return &MessageUpdate{} -} - -// Handle is the handler for MessageUpdate events. -func (eh messageUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*MessageUpdate); ok { - eh(s, t) - } -} - -// presenceUpdateEventHandler is an event handler for PresenceUpdate events. -type presenceUpdateEventHandler func(*Session, *PresenceUpdate) - -// Type returns the event type for PresenceUpdate events. -func (eh presenceUpdateEventHandler) Type() string { - return presenceUpdateEventType -} - -// New returns a new instance of PresenceUpdate. -func (eh presenceUpdateEventHandler) New() interface{} { - return &PresenceUpdate{} -} - -// Handle is the handler for PresenceUpdate events. -func (eh presenceUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*PresenceUpdate); ok { - eh(s, t) - } -} - -// presencesReplaceEventHandler is an event handler for PresencesReplace events. -type presencesReplaceEventHandler func(*Session, *PresencesReplace) - -// Type returns the event type for PresencesReplace events. -func (eh presencesReplaceEventHandler) Type() string { - return presencesReplaceEventType -} - -// New returns a new instance of PresencesReplace. -func (eh presencesReplaceEventHandler) New() interface{} { - return &PresencesReplace{} -} - -// Handle is the handler for PresencesReplace events. -func (eh presencesReplaceEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*PresencesReplace); ok { - eh(s, t) - } -} - -// rateLimitEventHandler is an event handler for RateLimit events. -type rateLimitEventHandler func(*Session, *RateLimit) - -// Type returns the event type for RateLimit events. -func (eh rateLimitEventHandler) Type() string { - return rateLimitEventType -} - -// Handle is the handler for RateLimit events. -func (eh rateLimitEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*RateLimit); ok { - eh(s, t) - } -} - -// readyEventHandler is an event handler for Ready events. -type readyEventHandler func(*Session, *Ready) - -// Type returns the event type for Ready events. -func (eh readyEventHandler) Type() string { - return readyEventType -} - -// New returns a new instance of Ready. -func (eh readyEventHandler) New() interface{} { - return &Ready{} -} - -// Handle is the handler for Ready events. -func (eh readyEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*Ready); ok { - eh(s, t) - } -} - -// relationshipAddEventHandler is an event handler for RelationshipAdd events. -type relationshipAddEventHandler func(*Session, *RelationshipAdd) - -// Type returns the event type for RelationshipAdd events. -func (eh relationshipAddEventHandler) Type() string { - return relationshipAddEventType -} - -// New returns a new instance of RelationshipAdd. -func (eh relationshipAddEventHandler) New() interface{} { - return &RelationshipAdd{} -} - -// Handle is the handler for RelationshipAdd events. -func (eh relationshipAddEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*RelationshipAdd); ok { - eh(s, t) - } -} - -// relationshipRemoveEventHandler is an event handler for RelationshipRemove events. -type relationshipRemoveEventHandler func(*Session, *RelationshipRemove) - -// Type returns the event type for RelationshipRemove events. -func (eh relationshipRemoveEventHandler) Type() string { - return relationshipRemoveEventType -} - -// New returns a new instance of RelationshipRemove. -func (eh relationshipRemoveEventHandler) New() interface{} { - return &RelationshipRemove{} -} - -// Handle is the handler for RelationshipRemove events. -func (eh relationshipRemoveEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*RelationshipRemove); ok { - eh(s, t) - } -} - -// resumedEventHandler is an event handler for Resumed events. -type resumedEventHandler func(*Session, *Resumed) - -// Type returns the event type for Resumed events. -func (eh resumedEventHandler) Type() string { - return resumedEventType -} - -// New returns a new instance of Resumed. -func (eh resumedEventHandler) New() interface{} { - return &Resumed{} -} - -// Handle is the handler for Resumed events. -func (eh resumedEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*Resumed); ok { - eh(s, t) - } -} - -// typingStartEventHandler is an event handler for TypingStart events. -type typingStartEventHandler func(*Session, *TypingStart) - -// Type returns the event type for TypingStart events. -func (eh typingStartEventHandler) Type() string { - return typingStartEventType -} - -// New returns a new instance of TypingStart. -func (eh typingStartEventHandler) New() interface{} { - return &TypingStart{} -} - -// Handle is the handler for TypingStart events. -func (eh typingStartEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*TypingStart); ok { - eh(s, t) - } -} - -// userGuildSettingsUpdateEventHandler is an event handler for UserGuildSettingsUpdate events. -type userGuildSettingsUpdateEventHandler func(*Session, *UserGuildSettingsUpdate) - -// Type returns the event type for UserGuildSettingsUpdate events. -func (eh userGuildSettingsUpdateEventHandler) Type() string { - return userGuildSettingsUpdateEventType -} - -// New returns a new instance of UserGuildSettingsUpdate. -func (eh userGuildSettingsUpdateEventHandler) New() interface{} { - return &UserGuildSettingsUpdate{} -} - -// Handle is the handler for UserGuildSettingsUpdate events. -func (eh userGuildSettingsUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*UserGuildSettingsUpdate); ok { - eh(s, t) - } -} - -// userNoteUpdateEventHandler is an event handler for UserNoteUpdate events. -type userNoteUpdateEventHandler func(*Session, *UserNoteUpdate) - -// Type returns the event type for UserNoteUpdate events. -func (eh userNoteUpdateEventHandler) Type() string { - return userNoteUpdateEventType -} - -// New returns a new instance of UserNoteUpdate. -func (eh userNoteUpdateEventHandler) New() interface{} { - return &UserNoteUpdate{} -} - -// Handle is the handler for UserNoteUpdate events. -func (eh userNoteUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*UserNoteUpdate); ok { - eh(s, t) - } -} - -// userSettingsUpdateEventHandler is an event handler for UserSettingsUpdate events. -type userSettingsUpdateEventHandler func(*Session, *UserSettingsUpdate) - -// Type returns the event type for UserSettingsUpdate events. -func (eh userSettingsUpdateEventHandler) Type() string { - return userSettingsUpdateEventType -} - -// New returns a new instance of UserSettingsUpdate. -func (eh userSettingsUpdateEventHandler) New() interface{} { - return &UserSettingsUpdate{} -} - -// Handle is the handler for UserSettingsUpdate events. -func (eh userSettingsUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*UserSettingsUpdate); ok { - eh(s, t) - } -} - -// userUpdateEventHandler is an event handler for UserUpdate events. -type userUpdateEventHandler func(*Session, *UserUpdate) - -// Type returns the event type for UserUpdate events. -func (eh userUpdateEventHandler) Type() string { - return userUpdateEventType -} - -// New returns a new instance of UserUpdate. -func (eh userUpdateEventHandler) New() interface{} { - return &UserUpdate{} -} - -// Handle is the handler for UserUpdate events. -func (eh userUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*UserUpdate); ok { - eh(s, t) - } -} - -// voiceServerUpdateEventHandler is an event handler for VoiceServerUpdate events. -type voiceServerUpdateEventHandler func(*Session, *VoiceServerUpdate) - -// Type returns the event type for VoiceServerUpdate events. -func (eh voiceServerUpdateEventHandler) Type() string { - return voiceServerUpdateEventType -} - -// New returns a new instance of VoiceServerUpdate. -func (eh voiceServerUpdateEventHandler) New() interface{} { - return &VoiceServerUpdate{} -} - -// Handle is the handler for VoiceServerUpdate events. -func (eh voiceServerUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*VoiceServerUpdate); ok { - eh(s, t) - } -} - -// voiceStateUpdateEventHandler is an event handler for VoiceStateUpdate events. -type voiceStateUpdateEventHandler func(*Session, *VoiceStateUpdate) - -// Type returns the event type for VoiceStateUpdate events. -func (eh voiceStateUpdateEventHandler) Type() string { - return voiceStateUpdateEventType -} - -// New returns a new instance of VoiceStateUpdate. -func (eh voiceStateUpdateEventHandler) New() interface{} { - return &VoiceStateUpdate{} -} - -// Handle is the handler for VoiceStateUpdate events. -func (eh voiceStateUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*VoiceStateUpdate); ok { - eh(s, t) - } -} - -// webhooksUpdateEventHandler is an event handler for WebhooksUpdate events. -type webhooksUpdateEventHandler func(*Session, *WebhooksUpdate) - -// Type returns the event type for WebhooksUpdate events. -func (eh webhooksUpdateEventHandler) Type() string { - return webhooksUpdateEventType -} - -// New returns a new instance of WebhooksUpdate. -func (eh webhooksUpdateEventHandler) New() interface{} { - return &WebhooksUpdate{} -} - -// Handle is the handler for WebhooksUpdate events. -func (eh webhooksUpdateEventHandler) Handle(s *Session, i interface{}) { - if t, ok := i.(*WebhooksUpdate); ok { - eh(s, t) - } -} - -func handlerForInterface(handler interface{}) EventHandler { - switch v := handler.(type) { - case func(*Session, interface{}): - return interfaceEventHandler(v) - case func(*Session, *ChannelCreate): - return channelCreateEventHandler(v) - case func(*Session, *ChannelDelete): - return channelDeleteEventHandler(v) - case func(*Session, *ChannelPinsUpdate): - return channelPinsUpdateEventHandler(v) - case func(*Session, *ChannelUpdate): - return channelUpdateEventHandler(v) - case func(*Session, *Connect): - return connectEventHandler(v) - case func(*Session, *Disconnect): - return disconnectEventHandler(v) - case func(*Session, *Event): - return eventEventHandler(v) - case func(*Session, *GuildBanAdd): - return guildBanAddEventHandler(v) - case func(*Session, *GuildBanRemove): - return guildBanRemoveEventHandler(v) - case func(*Session, *GuildCreate): - return guildCreateEventHandler(v) - case func(*Session, *GuildDelete): - return guildDeleteEventHandler(v) - case func(*Session, *GuildEmojisUpdate): - return guildEmojisUpdateEventHandler(v) - case func(*Session, *GuildIntegrationsUpdate): - return guildIntegrationsUpdateEventHandler(v) - case func(*Session, *GuildMemberAdd): - return guildMemberAddEventHandler(v) - case func(*Session, *GuildMemberRemove): - return guildMemberRemoveEventHandler(v) - case func(*Session, *GuildMemberUpdate): - return guildMemberUpdateEventHandler(v) - case func(*Session, *GuildMembersChunk): - return guildMembersChunkEventHandler(v) - case func(*Session, *GuildRoleCreate): - return guildRoleCreateEventHandler(v) - case func(*Session, *GuildRoleDelete): - return guildRoleDeleteEventHandler(v) - case func(*Session, *GuildRoleUpdate): - return guildRoleUpdateEventHandler(v) - case func(*Session, *GuildUpdate): - return guildUpdateEventHandler(v) - case func(*Session, *MessageAck): - return messageAckEventHandler(v) - case func(*Session, *MessageCreate): - return messageCreateEventHandler(v) - case func(*Session, *MessageDelete): - return messageDeleteEventHandler(v) - case func(*Session, *MessageDeleteBulk): - return messageDeleteBulkEventHandler(v) - case func(*Session, *MessageReactionAdd): - return messageReactionAddEventHandler(v) - case func(*Session, *MessageReactionRemove): - return messageReactionRemoveEventHandler(v) - case func(*Session, *MessageReactionRemoveAll): - return messageReactionRemoveAllEventHandler(v) - case func(*Session, *MessageUpdate): - return messageUpdateEventHandler(v) - case func(*Session, *PresenceUpdate): - return presenceUpdateEventHandler(v) - case func(*Session, *PresencesReplace): - return presencesReplaceEventHandler(v) - case func(*Session, *RateLimit): - return rateLimitEventHandler(v) - case func(*Session, *Ready): - return readyEventHandler(v) - case func(*Session, *RelationshipAdd): - return relationshipAddEventHandler(v) - case func(*Session, *RelationshipRemove): - return relationshipRemoveEventHandler(v) - case func(*Session, *Resumed): - return resumedEventHandler(v) - case func(*Session, *TypingStart): - return typingStartEventHandler(v) - case func(*Session, *UserGuildSettingsUpdate): - return userGuildSettingsUpdateEventHandler(v) - case func(*Session, *UserNoteUpdate): - return userNoteUpdateEventHandler(v) - case func(*Session, *UserSettingsUpdate): - return userSettingsUpdateEventHandler(v) - case func(*Session, *UserUpdate): - return userUpdateEventHandler(v) - case func(*Session, *VoiceServerUpdate): - return voiceServerUpdateEventHandler(v) - case func(*Session, *VoiceStateUpdate): - return voiceStateUpdateEventHandler(v) - case func(*Session, *WebhooksUpdate): - return webhooksUpdateEventHandler(v) - } - - return nil -} - -func init() { - registerInterfaceProvider(channelCreateEventHandler(nil)) - registerInterfaceProvider(channelDeleteEventHandler(nil)) - registerInterfaceProvider(channelPinsUpdateEventHandler(nil)) - registerInterfaceProvider(channelUpdateEventHandler(nil)) - registerInterfaceProvider(guildBanAddEventHandler(nil)) - registerInterfaceProvider(guildBanRemoveEventHandler(nil)) - registerInterfaceProvider(guildCreateEventHandler(nil)) - registerInterfaceProvider(guildDeleteEventHandler(nil)) - registerInterfaceProvider(guildEmojisUpdateEventHandler(nil)) - registerInterfaceProvider(guildIntegrationsUpdateEventHandler(nil)) - registerInterfaceProvider(guildMemberAddEventHandler(nil)) - registerInterfaceProvider(guildMemberRemoveEventHandler(nil)) - registerInterfaceProvider(guildMemberUpdateEventHandler(nil)) - registerInterfaceProvider(guildMembersChunkEventHandler(nil)) - registerInterfaceProvider(guildRoleCreateEventHandler(nil)) - registerInterfaceProvider(guildRoleDeleteEventHandler(nil)) - registerInterfaceProvider(guildRoleUpdateEventHandler(nil)) - registerInterfaceProvider(guildUpdateEventHandler(nil)) - registerInterfaceProvider(messageAckEventHandler(nil)) - registerInterfaceProvider(messageCreateEventHandler(nil)) - registerInterfaceProvider(messageDeleteEventHandler(nil)) - registerInterfaceProvider(messageDeleteBulkEventHandler(nil)) - registerInterfaceProvider(messageReactionAddEventHandler(nil)) - registerInterfaceProvider(messageReactionRemoveEventHandler(nil)) - registerInterfaceProvider(messageReactionRemoveAllEventHandler(nil)) - registerInterfaceProvider(messageUpdateEventHandler(nil)) - registerInterfaceProvider(presenceUpdateEventHandler(nil)) - registerInterfaceProvider(presencesReplaceEventHandler(nil)) - registerInterfaceProvider(readyEventHandler(nil)) - registerInterfaceProvider(relationshipAddEventHandler(nil)) - registerInterfaceProvider(relationshipRemoveEventHandler(nil)) - registerInterfaceProvider(resumedEventHandler(nil)) - registerInterfaceProvider(typingStartEventHandler(nil)) - registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil)) - registerInterfaceProvider(userNoteUpdateEventHandler(nil)) - registerInterfaceProvider(userSettingsUpdateEventHandler(nil)) - registerInterfaceProvider(userUpdateEventHandler(nil)) - registerInterfaceProvider(voiceServerUpdateEventHandler(nil)) - registerInterfaceProvider(voiceStateUpdateEventHandler(nil)) - registerInterfaceProvider(webhooksUpdateEventHandler(nil)) -} diff --git a/vendor/github.com/bwmarrin/discordgo/events.go b/vendor/github.com/bwmarrin/discordgo/events.go deleted file mode 100644 index c416813c..00000000 --- a/vendor/github.com/bwmarrin/discordgo/events.go +++ /dev/null @@ -1,264 +0,0 @@ -package discordgo - -import ( - "encoding/json" -) - -// This file contains all the possible structs that can be -// handled by AddHandler/EventHandler. -// DO NOT ADD ANYTHING BUT EVENT HANDLER STRUCTS TO THIS FILE. -//go:generate go run tools/cmd/eventhandlers/main.go - -// Connect is the data for a Connect event. -// This is a synthetic event and is not dispatched by Discord. -type Connect struct{} - -// Disconnect is the data for a Disconnect event. -// This is a synthetic event and is not dispatched by Discord. -type Disconnect struct{} - -// RateLimit is the data for a RateLimit event. -// This is a synthetic event and is not dispatched by Discord. -type RateLimit struct { - *TooManyRequests - URL string -} - -// Event provides a basic initial struct for all websocket events. -type Event struct { - Operation int `json:"op"` - Sequence int64 `json:"s"` - Type string `json:"t"` - RawData json.RawMessage `json:"d"` - // Struct contains one of the other types in this file. - Struct interface{} `json:"-"` -} - -// A Ready stores all data for the websocket READY event. -type Ready struct { - Version int `json:"v"` - SessionID string `json:"session_id"` - User *User `json:"user"` - ReadState []*ReadState `json:"read_state"` - PrivateChannels []*Channel `json:"private_channels"` - Guilds []*Guild `json:"guilds"` - - // Undocumented fields - Settings *Settings `json:"user_settings"` - UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"` - Relationships []*Relationship `json:"relationships"` - Presences []*Presence `json:"presences"` - Notes map[string]string `json:"notes"` -} - -// ChannelCreate is the data for a ChannelCreate event. -type ChannelCreate struct { - *Channel -} - -// ChannelUpdate is the data for a ChannelUpdate event. -type ChannelUpdate struct { - *Channel -} - -// ChannelDelete is the data for a ChannelDelete event. -type ChannelDelete struct { - *Channel -} - -// ChannelPinsUpdate stores data for a ChannelPinsUpdate event. -type ChannelPinsUpdate struct { - LastPinTimestamp string `json:"last_pin_timestamp"` - ChannelID string `json:"channel_id"` - GuildID string `json:"guild_id,omitempty"` -} - -// GuildCreate is the data for a GuildCreate event. -type GuildCreate struct { - *Guild -} - -// GuildUpdate is the data for a GuildUpdate event. -type GuildUpdate struct { - *Guild -} - -// GuildDelete is the data for a GuildDelete event. -type GuildDelete struct { - *Guild -} - -// GuildBanAdd is the data for a GuildBanAdd event. -type GuildBanAdd struct { - User *User `json:"user"` - GuildID string `json:"guild_id"` -} - -// GuildBanRemove is the data for a GuildBanRemove event. -type GuildBanRemove struct { - User *User `json:"user"` - GuildID string `json:"guild_id"` -} - -// GuildMemberAdd is the data for a GuildMemberAdd event. -type GuildMemberAdd struct { - *Member -} - -// GuildMemberUpdate is the data for a GuildMemberUpdate event. -type GuildMemberUpdate struct { - *Member -} - -// GuildMemberRemove is the data for a GuildMemberRemove event. -type GuildMemberRemove struct { - *Member -} - -// GuildRoleCreate is the data for a GuildRoleCreate event. -type GuildRoleCreate struct { - *GuildRole -} - -// GuildRoleUpdate is the data for a GuildRoleUpdate event. -type GuildRoleUpdate struct { - *GuildRole -} - -// A GuildRoleDelete is the data for a GuildRoleDelete event. -type GuildRoleDelete struct { - RoleID string `json:"role_id"` - GuildID string `json:"guild_id"` -} - -// A GuildEmojisUpdate is the data for a guild emoji update event. -type GuildEmojisUpdate struct { - GuildID string `json:"guild_id"` - Emojis []*Emoji `json:"emojis"` -} - -// A GuildMembersChunk is the data for a GuildMembersChunk event. -type GuildMembersChunk struct { - GuildID string `json:"guild_id"` - Members []*Member `json:"members"` -} - -// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event. -type GuildIntegrationsUpdate struct { - GuildID string `json:"guild_id"` -} - -// MessageAck is the data for a MessageAck event. -type MessageAck struct { - MessageID string `json:"message_id"` - ChannelID string `json:"channel_id"` -} - -// MessageCreate is the data for a MessageCreate event. -type MessageCreate struct { - *Message -} - -// MessageUpdate is the data for a MessageUpdate event. -type MessageUpdate struct { - *Message - // BeforeUpdate will be nil if the Message was not previously cached in the state cache. - BeforeUpdate *Message `json:"-"` -} - -// MessageDelete is the data for a MessageDelete event. -type MessageDelete struct { - *Message -} - -// MessageReactionAdd is the data for a MessageReactionAdd event. -type MessageReactionAdd struct { - *MessageReaction -} - -// MessageReactionRemove is the data for a MessageReactionRemove event. -type MessageReactionRemove struct { - *MessageReaction -} - -// MessageReactionRemoveAll is the data for a MessageReactionRemoveAll event. -type MessageReactionRemoveAll struct { - *MessageReaction -} - -// PresencesReplace is the data for a PresencesReplace event. -type PresencesReplace []*Presence - -// PresenceUpdate is the data for a PresenceUpdate event. -type PresenceUpdate struct { - Presence - GuildID string `json:"guild_id"` - Roles []string `json:"roles"` -} - -// Resumed is the data for a Resumed event. -type Resumed struct { - Trace []string `json:"_trace"` -} - -// RelationshipAdd is the data for a RelationshipAdd event. -type RelationshipAdd struct { - *Relationship -} - -// RelationshipRemove is the data for a RelationshipRemove event. -type RelationshipRemove struct { - *Relationship -} - -// TypingStart is the data for a TypingStart event. -type TypingStart struct { - UserID string `json:"user_id"` - ChannelID string `json:"channel_id"` - GuildID string `json:"guild_id,omitempty"` - Timestamp int `json:"timestamp"` -} - -// UserUpdate is the data for a UserUpdate event. -type UserUpdate struct { - *User -} - -// UserSettingsUpdate is the data for a UserSettingsUpdate event. -type UserSettingsUpdate map[string]interface{} - -// UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event. -type UserGuildSettingsUpdate struct { - *UserGuildSettings -} - -// UserNoteUpdate is the data for a UserNoteUpdate event. -type UserNoteUpdate struct { - ID string `json:"id"` - Note string `json:"note"` -} - -// VoiceServerUpdate is the data for a VoiceServerUpdate event. -type VoiceServerUpdate struct { - Token string `json:"token"` - GuildID string `json:"guild_id"` - Endpoint string `json:"endpoint"` -} - -// VoiceStateUpdate is the data for a VoiceStateUpdate event. -type VoiceStateUpdate struct { - *VoiceState -} - -// MessageDeleteBulk is the data for a MessageDeleteBulk event -type MessageDeleteBulk struct { - Messages []string `json:"ids"` - ChannelID string `json:"channel_id"` - GuildID string `json:"guild_id"` -} - -// WebhooksUpdate is the data for a WebhooksUpdate event -type WebhooksUpdate struct { - GuildID string `json:"guild_id"` - ChannelID string `json:"channel_id"` -} diff --git a/vendor/github.com/bwmarrin/discordgo/go.mod b/vendor/github.com/bwmarrin/discordgo/go.mod deleted file mode 100644 index 2ff88680..00000000 --- a/vendor/github.com/bwmarrin/discordgo/go.mod +++ /dev/null @@ -1,6 +0,0 @@ -module github.com/bwmarrin/discordgo - -require ( - github.com/gorilla/websocket v1.4.0 - golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 -) diff --git a/vendor/github.com/bwmarrin/discordgo/go.sum b/vendor/github.com/bwmarrin/discordgo/go.sum deleted file mode 100644 index a86b0501..00000000 --- a/vendor/github.com/bwmarrin/discordgo/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/vendor/github.com/bwmarrin/discordgo/logging.go b/vendor/github.com/bwmarrin/discordgo/logging.go deleted file mode 100644 index 6460b35b..00000000 --- a/vendor/github.com/bwmarrin/discordgo/logging.go +++ /dev/null @@ -1,103 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 code related to discordgo package logging - -package discordgo - -import ( - "fmt" - "log" - "runtime" - "strings" -) - -const ( - - // LogError level is used for critical errors that could lead to data loss - // or panic that would not be returned to a calling function. - LogError int = iota - - // LogWarning level is used for very abnormal events and errors that are - // also returned to a calling function. - LogWarning - - // LogInformational level is used for normal non-error activity - LogInformational - - // LogDebug level is for very detailed non-error activity. This is - // very spammy and will impact performance. - LogDebug -) - -// Logger can be used to replace the standard logging for discordgo -var Logger func(msgL, caller int, format string, a ...interface{}) - -// msglog provides package wide logging consistancy for discordgo -// the format, a... portion this command follows that of fmt.Printf -// msgL : LogLevel of the message -// caller : 1 + the number of callers away from the message source -// format : Printf style message format -// a ... : comma separated list of values to pass -func msglog(msgL, caller int, format string, a ...interface{}) { - - if Logger != nil { - Logger(msgL, caller, format, a...) - } else { - - pc, file, line, _ := runtime.Caller(caller) - - files := strings.Split(file, "/") - file = files[len(files)-1] - - name := runtime.FuncForPC(pc).Name() - fns := strings.Split(name, ".") - name = fns[len(fns)-1] - - msg := fmt.Sprintf(format, a...) - - log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg) - } -} - -// helper function that wraps msglog for the Session struct -// This adds a check to insure the message is only logged -// if the session log level is equal or higher than the -// message log level -func (s *Session) log(msgL int, format string, a ...interface{}) { - - if msgL > s.LogLevel { - return - } - - msglog(msgL, 2, format, a...) -} - -// helper function that wraps msglog for the VoiceConnection struct -// This adds a check to insure the message is only logged -// if the voice connection log level is equal or higher than the -// message log level -func (v *VoiceConnection) log(msgL int, format string, a ...interface{}) { - - if msgL > v.LogLevel { - return - } - - msglog(msgL, 2, format, a...) -} - -// printJSON is a helper function to display JSON data in a easy to read format. -/* NOT USED ATM -func printJSON(body []byte) { - var prettyJSON bytes.Buffer - error := json.Indent(&prettyJSON, body, "", "\t") - if error != nil { - log.Print("JSON parse error: ", error) - } - log.Println(string(prettyJSON.Bytes())) -} -*/ diff --git a/vendor/github.com/bwmarrin/discordgo/message.go b/vendor/github.com/bwmarrin/discordgo/message.go deleted file mode 100644 index cc874295..00000000 --- a/vendor/github.com/bwmarrin/discordgo/message.go +++ /dev/null @@ -1,370 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 code related to the Message struct - -package discordgo - -import ( - "io" - "regexp" - "strings" -) - -// MessageType is the type of Message -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 -) - -// A Message stores all data related to a specific Discord message. -type Message struct { - // The ID of the message. - ID string `json:"id"` - - // The ID of the channel in which the message was sent. - ChannelID string `json:"channel_id"` - - // The ID of the guild in which the message was sent. - GuildID string `json:"guild_id,omitempty"` - - // The content of the message. - Content string `json:"content"` - - // The time at which the messsage was sent. - // 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"` - - // The time at which the last edit of the message - // occurred, if it has been edited. - EditedTimestamp Timestamp `json:"edited_timestamp"` - - // The roles mentioned in the message. - MentionRoles []string `json:"mention_roles"` - - // Whether the message is text-to-speech. - Tts bool `json:"tts"` - - // Whether the message mentions everyone. - MentionEveryone bool `json:"mention_everyone"` - - // The author of the message. This is not guaranteed to be a - // valid user (webhook-sent messages do not possess a full author). - Author *User `json:"author"` - - // 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. - Embeds []*MessageEmbed `json:"embeds"` - - // A list of users mentioned in the message. - Mentions []*User `json:"mentions"` - - // A list of reactions to the message. - Reactions []*MessageReactions `json:"reactions"` - - // Whether the message is pinned or not. - Pinned bool `json:"pinned"` - - // The type of the message. - Type MessageType `json:"type"` - - // The webhook ID of the message, if it was generated by a webhook - WebhookID string `json:"webhook_id"` - - // Member properties for this message's author, - // contains only partial information - Member *Member `json:"member"` - - // Channels specifically mentioned in this message - // Not all channel mentions in a message will appear in mention_channels. - // Only textual channels that are visible to everyone in a lurkable guild will ever be included. - // Only crossposted messages (via Channel Following) currently include mention_channels at all. - // If no mentions in the message meet these requirements, this field will not be sent. - MentionChannels []*Channel `json:"mention_channels"` - - // Is sent with Rich Presence-related chat embeds - Activity *MessageActivity `json:"activity"` - - // Is sent with Rich Presence-related chat embeds - Application *MessageApplication `json:"application"` - - // MessageReference contains reference data sent with crossposted messages - MessageReference *MessageReference `json:"message_reference"` - - // 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 int `json:"flags"` -} - -// File stores info about files you e.g. send in messages. -type File struct { - Name string - ContentType string - Reader io.Reader -} - -// MessageSend stores all parameters you can send with ChannelMessageSendComplex. -type MessageSend struct { - Content string `json:"content,omitempty"` - Embed *MessageEmbed `json:"embed,omitempty"` - Tts bool `json:"tts"` - Files []*File `json:"-"` - - // TODO: Remove this when compatibility is not required. - File *File `json:"-"` -} - -// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which -// is also where you should get the instance from. -type MessageEdit struct { - Content *string `json:"content,omitempty"` - Embed *MessageEmbed `json:"embed,omitempty"` - - ID string - Channel string -} - -// NewMessageEdit returns a MessageEdit struct, initialized -// with the Channel and ID. -func NewMessageEdit(channelID string, messageID string) *MessageEdit { - return &MessageEdit{ - Channel: channelID, - ID: messageID, - } -} - -// SetContent is the same as setting the variable Content, -// except it doesn't take a pointer. -func (m *MessageEdit) SetContent(str string) *MessageEdit { - m.Content = &str - return m -} - -// SetEmbed is a convenience function for setting the embed, -// so you can chain commands. -func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit { - m.Embed = embed - return m -} - -// A MessageAttachment stores data for message attachments. -type MessageAttachment struct { - ID string `json:"id"` - URL string `json:"url"` - ProxyURL string `json:"proxy_url"` - Filename string `json:"filename"` - Width int `json:"width"` - Height int `json:"height"` - Size int `json:"size"` -} - -// MessageEmbedFooter is a part of a MessageEmbed struct. -type MessageEmbedFooter struct { - Text string `json:"text,omitempty"` - IconURL string `json:"icon_url,omitempty"` - ProxyIconURL string `json:"proxy_icon_url,omitempty"` -} - -// MessageEmbedImage is a part of a MessageEmbed struct. -type MessageEmbedImage struct { - URL string `json:"url,omitempty"` - ProxyURL string `json:"proxy_url,omitempty"` - Width int `json:"width,omitempty"` - Height int `json:"height,omitempty"` -} - -// MessageEmbedThumbnail is a part of a MessageEmbed struct. -type MessageEmbedThumbnail struct { - URL string `json:"url,omitempty"` - ProxyURL string `json:"proxy_url,omitempty"` - Width int `json:"width,omitempty"` - Height int `json:"height,omitempty"` -} - -// MessageEmbedVideo is a part of a MessageEmbed struct. -type MessageEmbedVideo struct { - URL string `json:"url,omitempty"` - ProxyURL string `json:"proxy_url,omitempty"` - Width int `json:"width,omitempty"` - Height int `json:"height,omitempty"` -} - -// MessageEmbedProvider is a part of a MessageEmbed struct. -type MessageEmbedProvider struct { - URL string `json:"url,omitempty"` - Name string `json:"name,omitempty"` -} - -// MessageEmbedAuthor is a part of a MessageEmbed struct. -type MessageEmbedAuthor struct { - URL string `json:"url,omitempty"` - Name string `json:"name,omitempty"` - IconURL string `json:"icon_url,omitempty"` - ProxyIconURL string `json:"proxy_icon_url,omitempty"` -} - -// MessageEmbedField is a part of a MessageEmbed struct. -type MessageEmbedField struct { - Name string `json:"name,omitempty"` - Value string `json:"value,omitempty"` - Inline bool `json:"inline,omitempty"` -} - -// An MessageEmbed stores data for message embeds. -type MessageEmbed struct { - URL string `json:"url,omitempty"` - Type string `json:"type,omitempty"` - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - Timestamp string `json:"timestamp,omitempty"` - Color int `json:"color,omitempty"` - Footer *MessageEmbedFooter `json:"footer,omitempty"` - Image *MessageEmbedImage `json:"image,omitempty"` - Thumbnail *MessageEmbedThumbnail `json:"thumbnail,omitempty"` - Video *MessageEmbedVideo `json:"video,omitempty"` - Provider *MessageEmbedProvider `json:"provider,omitempty"` - Author *MessageEmbedAuthor `json:"author,omitempty"` - Fields []*MessageEmbedField `json:"fields,omitempty"` -} - -// MessageReactions holds a reactions object for a message. -type MessageReactions struct { - Count int `json:"count"` - Me bool `json:"me"` - Emoji *Emoji `json:"emoji"` -} - -// MessageActivity is sent with Rich Presence-related chat embeds -type MessageActivity struct { - Type MessageActivityType `json:"type"` - PartyID string `json:"party_id"` -} - -// MessageActivityType is the type of message activity -type MessageActivityType int - -// Constants for the different types of Message Activity -const ( - MessageActivityTypeJoin = iota + 1 - MessageActivityTypeSpectate - MessageActivityTypeListen - MessageActivityTypeJoinRequest -) - -// MessageFlag describes an extra feature of the message -type MessageFlag int - -// Constants for the different bit offsets of Message Flags -const ( - // This message has been published to subscribed channels (via Channel Following) - MessageFlagCrossposted = 1 << iota - // This message originated from a message in another channel (via Channel Following) - MessageFlagIsCrosspost - // Do not include any embeds when serializing this message - MessageFlagSuppressEmbeds -) - -// MessageApplication is sent with Rich Presence-related chat embeds -type MessageApplication struct { - ID string `json:"id"` - CoverImage string `json:"cover_image"` - Description string `json:"description"` - Icon string `json:"icon"` - Name string `json:"name"` -} - -// MessageReference contains reference data sent with crossposted messages -type MessageReference struct { - MessageID string `json:"message_id"` - ChannelID string `json:"channel_id"` - GuildID string `json:"guild_id"` -} - -// ContentWithMentionsReplaced will replace all @<id> mentions with the -// username of the mention. -func (m *Message) ContentWithMentionsReplaced() (content string) { - content = m.Content - - for _, user := range m.Mentions { - content = strings.NewReplacer( - "<@"+user.ID+">", "@"+user.Username, - "<@!"+user.ID+">", "@"+user.Username, - ).Replace(content) - } - return -} - -var patternChannels = regexp.MustCompile("<#[^>]*>") - -// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the -// username of the mention, but also role IDs and more. -func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) { - content = m.Content - - if !s.StateEnabled { - content = m.ContentWithMentionsReplaced() - return - } - - channel, err := s.State.Channel(m.ChannelID) - if err != nil { - content = m.ContentWithMentionsReplaced() - return - } - - for _, user := range m.Mentions { - nick := user.Username - - member, err := s.State.Member(channel.GuildID, user.ID) - if err == nil && member.Nick != "" { - nick = member.Nick - } - - content = strings.NewReplacer( - "<@"+user.ID+">", "@"+user.Username, - "<@!"+user.ID+">", "@"+nick, - ).Replace(content) - } - for _, roleID := range m.MentionRoles { - role, err := s.State.Role(channel.GuildID, roleID) - if err != nil || !role.Mentionable { - continue - } - - content = strings.Replace(content, "<@&"+role.ID+">", "@"+role.Name, -1) - } - - content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string { - channel, err := s.State.Channel(mention[2 : len(mention)-1]) - if err != nil || channel.Type == ChannelTypeGuildVoice { - return mention - } - - return "#" + channel.Name - }) - return -} diff --git a/vendor/github.com/bwmarrin/discordgo/mkdocs.yml b/vendor/github.com/bwmarrin/discordgo/mkdocs.yml deleted file mode 100644 index 3ee8eb37..00000000 --- a/vendor/github.com/bwmarrin/discordgo/mkdocs.yml +++ /dev/null @@ -1,17 +0,0 @@ -site_name: DiscordGo -site_author: Bruce Marriner -site_url: http://bwmarrin.github.io/discordgo/ -repo_url: https://github.com/bwmarrin/discordgo - -dev_addr: 0.0.0.0:8000 -theme: yeti - -markdown_extensions: - - smarty - - toc: - permalink: True - - sane_lists - -pages: - - 'Home': 'index.md' - - 'Getting Started': 'GettingStarted.md' diff --git a/vendor/github.com/bwmarrin/discordgo/oauth2.go b/vendor/github.com/bwmarrin/discordgo/oauth2.go deleted file mode 100644 index 4a52120b..00000000 --- a/vendor/github.com/bwmarrin/discordgo/oauth2.go +++ /dev/null @@ -1,145 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 functions related to Discord OAuth2 endpoints - -package discordgo - -// ------------------------------------------------------------------------------------------------ -// Code specific to Discord OAuth2 Applications -// ------------------------------------------------------------------------------------------------ - -// An Application struct stores values for a Discord OAuth2 Application -type Application struct { - ID string `json:"id,omitempty"` - Name string `json:"name"` - Description string `json:"description,omitempty"` - Icon string `json:"icon,omitempty"` - Secret string `json:"secret,omitempty"` - RedirectURIs *[]string `json:"redirect_uris,omitempty"` - BotRequireCodeGrant bool `json:"bot_require_code_grant,omitempty"` - BotPublic bool `json:"bot_public,omitempty"` - RPCApplicationState int `json:"rpc_application_state,omitempty"` - Flags int `json:"flags,omitempty"` - Owner *User `json:"owner"` - Bot *User `json:"bot"` -} - -// Application returns an Application structure of a specific Application -// appID : The ID of an Application -func (s *Session) Application(appID string) (st *Application, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointApplication(appID), nil, EndpointApplication("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// Applications returns all applications for the authenticated user -func (s *Session) Applications() (st []*Application, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointApplications, nil, EndpointApplications) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ApplicationCreate creates a new Application -// name : Name of Application / Bot -// uris : Redirect URIs (Not required) -func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error) { - - data := struct { - Name string `json:"name"` - Description string `json:"description"` - RedirectURIs *[]string `json:"redirect_uris,omitempty"` - }{ap.Name, ap.Description, ap.RedirectURIs} - - body, err := s.RequestWithBucketID("POST", EndpointApplications, data, EndpointApplications) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ApplicationUpdate updates an existing Application -// var : desc -func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Application, err error) { - - data := struct { - Name string `json:"name"` - Description string `json:"description"` - RedirectURIs *[]string `json:"redirect_uris,omitempty"` - }{ap.Name, ap.Description, ap.RedirectURIs} - - body, err := s.RequestWithBucketID("PUT", EndpointApplication(appID), data, EndpointApplication("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ApplicationDelete deletes an existing Application -// appID : The ID of an Application -func (s *Session) ApplicationDelete(appID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointApplication(appID), nil, EndpointApplication("")) - if err != nil { - return - } - - return -} - -// Asset struct stores values for an asset of an application -type Asset struct { - Type int `json:"type"` - ID string `json:"id"` - Name string `json:"name"` -} - -// ApplicationAssets returns an application's assets -func (s *Session) ApplicationAssets(appID string) (ass []*Asset, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointApplicationAssets(appID), nil, EndpointApplicationAssets("")) - if err != nil { - return - } - - err = unmarshal(body, &ass) - return -} - -// ------------------------------------------------------------------------------------------------ -// Code specific to Discord OAuth2 Application Bots -// ------------------------------------------------------------------------------------------------ - -// ApplicationBotCreate creates an Application Bot Account -// -// appID : The ID of an Application -// -// NOTE: func name may change, if I can think up something better. -func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) { - - body, err := s.RequestWithBucketID("POST", EndpointApplicationsBot(appID), nil, EndpointApplicationsBot("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} diff --git a/vendor/github.com/bwmarrin/discordgo/ratelimit.go b/vendor/github.com/bwmarrin/discordgo/ratelimit.go deleted file mode 100644 index dc48c924..00000000 --- a/vendor/github.com/bwmarrin/discordgo/ratelimit.go +++ /dev/null @@ -1,194 +0,0 @@ -package discordgo - -import ( - "net/http" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" -) - -// customRateLimit holds information for defining a custom rate limit -type customRateLimit struct { - suffix string - requests int - reset time.Duration -} - -// RateLimiter holds all ratelimit buckets -type RateLimiter struct { - sync.Mutex - global *int64 - buckets map[string]*Bucket - globalRateLimit time.Duration - customRateLimits []*customRateLimit -} - -// NewRatelimiter returns a new RateLimiter -func NewRatelimiter() *RateLimiter { - - return &RateLimiter{ - buckets: make(map[string]*Bucket), - global: new(int64), - customRateLimits: []*customRateLimit{ - &customRateLimit{ - suffix: "//reactions//", - requests: 1, - reset: 200 * time.Millisecond, - }, - }, - } -} - -// GetBucket retrieves or creates a bucket -func (r *RateLimiter) GetBucket(key string) *Bucket { - r.Lock() - defer r.Unlock() - - if bucket, ok := r.buckets[key]; ok { - return bucket - } - - b := &Bucket{ - Remaining: 1, - Key: key, - global: r.global, - } - - // Check if there is a custom ratelimit set for this bucket ID. - for _, rl := range r.customRateLimits { - if strings.HasSuffix(b.Key, rl.suffix) { - b.customRateLimit = rl - break - } - } - - r.buckets[key] = b - return b -} - -// GetWaitTime returns the duration you should wait for a Bucket -func (r *RateLimiter) GetWaitTime(b *Bucket, minRemaining int) time.Duration { - // If we ran out of calls and the reset time is still ahead of us - // then we need to take it easy and relax a little - if b.Remaining < minRemaining && b.reset.After(time.Now()) { - return b.reset.Sub(time.Now()) - } - - // Check for global ratelimits - sleepTo := time.Unix(0, atomic.LoadInt64(r.global)) - if now := time.Now(); now.Before(sleepTo) { - return sleepTo.Sub(now) - } - - return 0 -} - -// LockBucket Locks until a request can be made -func (r *RateLimiter) LockBucket(bucketID string) *Bucket { - return r.LockBucketObject(r.GetBucket(bucketID)) -} - -// LockBucketObject Locks an already resolved bucket until a request can be made -func (r *RateLimiter) LockBucketObject(b *Bucket) *Bucket { - b.Lock() - - if wait := r.GetWaitTime(b, 1); wait > 0 { - time.Sleep(wait) - } - - b.Remaining-- - return b -} - -// Bucket represents a ratelimit bucket, each bucket gets ratelimited individually (-global ratelimits) -type Bucket struct { - sync.Mutex - Key string - Remaining int - limit int - reset time.Time - global *int64 - - lastReset time.Time - customRateLimit *customRateLimit - Userdata interface{} -} - -// Release unlocks the bucket and reads the headers to update the buckets ratelimit info -// and locks up the whole thing in case if there's a global ratelimit. -func (b *Bucket) Release(headers http.Header) error { - defer b.Unlock() - - // Check if the bucket uses a custom ratelimiter - if rl := b.customRateLimit; rl != nil { - if time.Now().Sub(b.lastReset) >= rl.reset { - b.Remaining = rl.requests - 1 - b.lastReset = time.Now() - } - if b.Remaining < 1 { - b.reset = time.Now().Add(rl.reset) - } - return nil - } - - if headers == nil { - return nil - } - - remaining := headers.Get("X-RateLimit-Remaining") - reset := headers.Get("X-RateLimit-Reset") - global := headers.Get("X-RateLimit-Global") - retryAfter := headers.Get("Retry-After") - - // Update global and per bucket reset time if the proper headers are available - // If global is set, then it will block all buckets until after Retry-After - // If Retry-After without global is provided it will use that for the new reset - // time since it's more accurate than X-RateLimit-Reset. - // If Retry-After after is not proided, it will update the reset time from X-RateLimit-Reset - if retryAfter != "" { - parsedAfter, err := strconv.ParseInt(retryAfter, 10, 64) - if err != nil { - return err - } - - resetAt := time.Now().Add(time.Duration(parsedAfter) * time.Millisecond) - - // Lock either this single bucket or all buckets - if global != "" { - atomic.StoreInt64(b.global, resetAt.UnixNano()) - } else { - b.reset = resetAt - } - } else if reset != "" { - // Calculate the reset time by using the date header returned from discord - discordTime, err := http.ParseTime(headers.Get("Date")) - if err != nil { - return err - } - - unix, err := strconv.ParseInt(reset, 10, 64) - if err != nil { - return err - } - - // Calculate the time until reset and add it to the current local time - // some extra time is added because without it i still encountered 429's. - // The added amount is the lowest amount that gave no 429's - // in 1k requests - delta := time.Unix(unix, 0).Sub(discordTime) + time.Millisecond*250 - b.reset = time.Now().Add(delta) - } - - // Udpate remaining if header is present - if remaining != "" { - parsedRemaining, err := strconv.ParseInt(remaining, 10, 32) - if err != nil { - return err - } - b.Remaining = int(parsedRemaining) - } - - return nil -} diff --git a/vendor/github.com/bwmarrin/discordgo/restapi.go b/vendor/github.com/bwmarrin/discordgo/restapi.go deleted file mode 100644 index b5fa0fa4..00000000 --- a/vendor/github.com/bwmarrin/discordgo/restapi.go +++ /dev/null @@ -1,2296 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 functions for interacting with the Discord REST/JSON API -// at the lowest level. - -package discordgo - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "image" - _ "image/jpeg" // For JPEG decoding - _ "image/png" // For PNG decoding - "io" - "io/ioutil" - "log" - "mime/multipart" - "net/http" - "net/textproto" - "net/url" - "strconv" - "strings" - "time" -) - -// All error constants -var ( - ErrJSONUnmarshal = errors.New("json unmarshal") - ErrStatusOffline = errors.New("You can't set your Status to offline") - ErrVerificationLevelBounds = errors.New("VerificationLevel out of bounds, should be between 0 and 3") - ErrPruneDaysBounds = errors.New("the number of days should be more than or equal to 1") - ErrGuildNoIcon = errors.New("guild does not have an icon set") - ErrGuildNoSplash = errors.New("guild does not have a splash set") - ErrUnauthorized = errors.New("HTTP request was unauthorized. This could be because the provided token was not a bot token. Please add \"Bot \" to the start of your token. https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header") -) - -// Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr -func (s *Session) Request(method, urlStr string, data interface{}) (response []byte, err error) { - return s.RequestWithBucketID(method, urlStr, data, strings.SplitN(urlStr, "?", 2)[0]) -} - -// RequestWithBucketID makes a (GET/POST/...) Requests to Discord REST API with JSON data. -func (s *Session) RequestWithBucketID(method, urlStr string, data interface{}, bucketID string) (response []byte, err error) { - var body []byte - if data != nil { - body, err = json.Marshal(data) - if err != nil { - return - } - } - - return s.request(method, urlStr, "application/json", body, bucketID, 0) -} - -// request makes a (GET/POST/...) Requests to Discord REST API. -// Sequence is the sequence number, if it fails with a 502 it will -// retry with sequence+1 until it either succeeds or sequence >= session.MaxRestRetries -func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID string, sequence int) (response []byte, err error) { - if bucketID == "" { - bucketID = strings.SplitN(urlStr, "?", 2)[0] - } - return s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucket(bucketID), sequence) -} - -// RequestWithLockedBucket makes a request using a bucket that's already been locked -func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b []byte, bucket *Bucket, sequence int) (response []byte, err error) { - if s.Debug { - log.Printf("API REQUEST %8s :: %s\n", method, urlStr) - log.Printf("API REQUEST PAYLOAD :: [%s]\n", string(b)) - } - - req, err := http.NewRequest(method, urlStr, bytes.NewBuffer(b)) - if err != nil { - bucket.Release(nil) - return - } - - // Not used on initial login.. - // TODO: Verify if a login, otherwise complain about no-token - if s.Token != "" { - req.Header.Set("authorization", s.Token) - } - - // Discord's API returns a 400 Bad Request is Content-Type is set, but the - // request body is empty. - if b != nil { - req.Header.Set("Content-Type", contentType) - } - - // TODO: Make a configurable static variable. - req.Header.Set("User-Agent", s.UserAgent) - - if s.Debug { - for k, v := range req.Header { - log.Printf("API REQUEST HEADER :: [%s] = %+v\n", k, v) - } - } - - resp, err := s.Client.Do(req) - if err != nil { - bucket.Release(nil) - return - } - defer func() { - err2 := resp.Body.Close() - if err2 != nil { - log.Println("error closing resp body") - } - }() - - err = bucket.Release(resp.Header) - if err != nil { - return - } - - response, err = ioutil.ReadAll(resp.Body) - if err != nil { - return - } - - if s.Debug { - - log.Printf("API RESPONSE STATUS :: %s\n", resp.Status) - for k, v := range resp.Header { - log.Printf("API RESPONSE HEADER :: [%s] = %+v\n", k, v) - } - log.Printf("API RESPONSE BODY :: [%s]\n\n\n", response) - } - - switch resp.StatusCode { - case http.StatusOK: - case http.StatusCreated: - case http.StatusNoContent: - case http.StatusBadGateway: - // Retry sending request if possible - if sequence < s.MaxRestRetries { - - s.log(LogInformational, "%s Failed (%s), Retrying...", urlStr, resp.Status) - response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence+1) - } else { - err = fmt.Errorf("Exceeded Max retries HTTP %s, %s", resp.Status, response) - } - case 429: // TOO MANY REQUESTS - Rate limiting - rl := TooManyRequests{} - err = json.Unmarshal(response, &rl) - if err != nil { - s.log(LogError, "rate limit unmarshal error, %s", err) - return - } - s.log(LogInformational, "Rate Limiting %s, retry in %d", urlStr, rl.RetryAfter) - s.handleEvent(rateLimitEventType, RateLimit{TooManyRequests: &rl, URL: urlStr}) - - time.Sleep(rl.RetryAfter * time.Millisecond) - // we can make the above smarter - // this method can cause longer delays than required - - response, err = s.RequestWithLockedBucket(method, urlStr, contentType, b, s.Ratelimiter.LockBucketObject(bucket), sequence) - case http.StatusUnauthorized: - if strings.Index(s.Token, "Bot ") != 0 { - s.log(LogInformational, ErrUnauthorized.Error()) - err = ErrUnauthorized - } - fallthrough - default: // Error condition - err = newRestError(req, resp, response) - } - - return -} - -func unmarshal(data []byte, v interface{}) error { - err := json.Unmarshal(data, v) - if err != nil { - return ErrJSONUnmarshal - } - - return nil -} - -// ------------------------------------------------------------------------------------------------ -// Functions specific to Discord Sessions -// ------------------------------------------------------------------------------------------------ - -// Login asks the Discord server for an authentication token. -// -// 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 (s *Session) Login(email, password string) (err error) { - - data := struct { - Email string `json:"email"` - Password string `json:"password"` - }{email, password} - - response, err := s.RequestWithBucketID("POST", EndpointLogin, data, EndpointLogin) - if err != nil { - return - } - - temp := struct { - Token string `json:"token"` - MFA bool `json:"mfa"` - }{} - - err = unmarshal(response, &temp) - if err != nil { - return - } - - s.Token = temp.Token - s.MFA = temp.MFA - return -} - -// Register sends a Register request to Discord, and returns the authentication token -// Note that this account is temporary and should be verified for future use. -// Another option is to save the authentication token external, but this isn't recommended. -func (s *Session) Register(username string) (token string, err error) { - - data := struct { - Username string `json:"username"` - }{username} - - response, err := s.RequestWithBucketID("POST", EndpointRegister, data, EndpointRegister) - if err != nil { - return - } - - temp := struct { - Token string `json:"token"` - }{} - - err = unmarshal(response, &temp) - if err != nil { - return - } - - token = temp.Token - return -} - -// Logout sends a logout request to Discord. -// This does not seem to actually invalidate the token. So you can still -// make API calls even after a Logout. So, it seems almost pointless to -// even use. -func (s *Session) Logout() (err error) { - - // _, err = s.Request("POST", LOGOUT, `{"token": "` + s.Token + `"}`) - - if s.Token == "" { - return - } - - data := struct { - Token string `json:"token"` - }{s.Token} - - _, err = s.RequestWithBucketID("POST", EndpointLogout, data, EndpointLogout) - return -} - -// ------------------------------------------------------------------------------------------------ -// Functions specific to Discord Users -// ------------------------------------------------------------------------------------------------ - -// User returns the user details of the given userID -// userID : A user ID or "@me" which is a shortcut of current user ID -func (s *Session) User(userID string) (st *User, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointUser(userID), nil, EndpointUsers) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// UserAvatar is deprecated. Please use UserAvatarDecode -// userID : A user ID or "@me" which is a shortcut of current user ID -func (s *Session) UserAvatar(userID string) (img image.Image, err error) { - u, err := s.User(userID) - if err != nil { - return - } - img, err = s.UserAvatarDecode(u) - return -} - -// UserAvatarDecode returns an image.Image of a user's Avatar -// user : The user which avatar should be retrieved -func (s *Session) UserAvatarDecode(u *User) (img image.Image, err error) { - body, err := s.RequestWithBucketID("GET", EndpointUserAvatar(u.ID, u.Avatar), nil, EndpointUserAvatar("", "")) - if err != nil { - return - } - - img, _, err = image.Decode(bytes.NewReader(body)) - return -} - -// UserUpdate updates a users settings. -func (s *Session) UserUpdate(email, password, username, avatar, newPassword string) (st *User, err error) { - - // NOTE: Avatar must be either the hash/id of existing Avatar or - // data:image/png;base64,BASE64_STRING_OF_NEW_AVATAR_PNG - // to set a new avatar. - // If left blank, avatar will be set to null/blank - - data := struct { - Email string `json:"email,omitempty"` - Password string `json:"password,omitempty"` - Username string `json:"username,omitempty"` - Avatar string `json:"avatar,omitempty"` - NewPassword string `json:"new_password,omitempty"` - }{email, password, username, avatar, newPassword} - - body, err := s.RequestWithBucketID("PATCH", EndpointUser("@me"), data, EndpointUsers) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// UserSettings returns the settings for a given user -func (s *Session) UserSettings() (st *Settings, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointUserSettings("@me"), nil, EndpointUserSettings("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// UserUpdateStatus update the user status -// status : The new status (Actual valid status are 'online','idle','dnd','invisible') -func (s *Session) UserUpdateStatus(status Status) (st *Settings, err error) { - if status == StatusOffline { - err = ErrStatusOffline - return - } - - data := struct { - Status Status `json:"status"` - }{status} - - body, err := s.RequestWithBucketID("PATCH", EndpointUserSettings("@me"), data, EndpointUserSettings("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// UserConnections returns the user's connections -func (s *Session) UserConnections() (conn []*UserConnection, err error) { - response, err := s.RequestWithBucketID("GET", EndpointUserConnections("@me"), nil, EndpointUserConnections("@me")) - if err != nil { - return nil, err - } - - err = unmarshal(response, &conn) - if err != nil { - return - } - - return -} - -// UserChannels returns an array of Channel structures for all private -// channels. -func (s *Session) UserChannels() (st []*Channel, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointUserChannels("@me"), nil, EndpointUserChannels("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// UserChannelCreate creates a new User (Private) Channel with another User -// recipientID : A user ID for the user to which this channel is opened with. -func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) { - - data := struct { - RecipientID string `json:"recipient_id"` - }{recipientID} - - body, err := s.RequestWithBucketID("POST", EndpointUserChannels("@me"), data, EndpointUserChannels("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// UserGuilds returns an array of UserGuild structures for all guilds. -// limit : The number guilds that can be returned. (max 100) -// beforeID : If provided all guilds returned will be before given ID. -// afterID : If provided all guilds returned will be after given ID. -func (s *Session) UserGuilds(limit int, beforeID, afterID string) (st []*UserGuild, err error) { - - v := url.Values{} - - if limit > 0 { - v.Set("limit", strconv.Itoa(limit)) - } - if afterID != "" { - v.Set("after", afterID) - } - if beforeID != "" { - v.Set("before", beforeID) - } - - uri := EndpointUserGuilds("@me") - - if len(v) > 0 { - uri += "?" + v.Encode() - } - - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointUserGuilds("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// UserGuildSettingsEdit Edits the users notification settings for a guild -// guildID : The ID of the guild to edit the settings on -// settings : The settings to update -func (s *Session) UserGuildSettingsEdit(guildID string, settings *UserGuildSettingsEdit) (st *UserGuildSettings, err error) { - - body, err := s.RequestWithBucketID("PATCH", EndpointUserGuildSettings("@me", guildID), settings, EndpointUserGuildSettings("", guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// UserChannelPermissions returns the permission of a user in a channel. -// userID : The ID of the user to calculate permissions for. -// channelID : The ID of the channel to calculate permission for. -// -// NOTE: This function is now deprecated and will be removed in the future. -// Please see the same function inside state.go -func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { - // Try to just get permissions from state. - apermissions, err = s.State.UserChannelPermissions(userID, channelID) - if err == nil { - return - } - - // Otherwise try get as much data from state as possible, falling back to the network. - channel, err := s.State.Channel(channelID) - if err != nil || channel == nil { - channel, err = s.Channel(channelID) - if err != nil { - return - } - } - - guild, err := s.State.Guild(channel.GuildID) - if err != nil || guild == nil { - guild, err = s.Guild(channel.GuildID) - if err != nil { - return - } - } - - if userID == guild.OwnerID { - apermissions = PermissionAll - return - } - - member, err := s.State.Member(guild.ID, userID) - if err != nil || member == nil { - member, err = s.GuildMember(guild.ID, userID) - if err != nil { - return - } - } - - return memberPermissions(guild, channel, member), nil -} - -// Calculates the permissions for a member. -// https://support.discordapp.com/hc/en-us/articles/206141927-How-is-the-permission-hierarchy-structured- -func memberPermissions(guild *Guild, channel *Channel, member *Member) (apermissions int) { - userID := member.User.ID - - if userID == guild.OwnerID { - apermissions = PermissionAll - return - } - - for _, role := range guild.Roles { - if role.ID == guild.ID { - apermissions |= role.Permissions - break - } - } - - for _, role := range guild.Roles { - for _, roleID := range member.Roles { - if role.ID == roleID { - apermissions |= role.Permissions - break - } - } - } - - if apermissions&PermissionAdministrator == PermissionAdministrator { - apermissions |= PermissionAll - } - - // Apply @everyone overrides from the channel. - for _, overwrite := range channel.PermissionOverwrites { - if guild.ID == overwrite.ID { - apermissions &= ^overwrite.Deny - apermissions |= overwrite.Allow - break - } - } - - denies := 0 - allows := 0 - - // Member overwrites can override role overrides, so do two passes - for _, overwrite := range channel.PermissionOverwrites { - for _, roleID := range member.Roles { - if overwrite.Type == "role" && roleID == overwrite.ID { - denies |= overwrite.Deny - allows |= overwrite.Allow - break - } - } - } - - apermissions &= ^denies - apermissions |= allows - - for _, overwrite := range channel.PermissionOverwrites { - if overwrite.Type == "member" && overwrite.ID == userID { - apermissions &= ^overwrite.Deny - apermissions |= overwrite.Allow - break - } - } - - if apermissions&PermissionAdministrator == PermissionAdministrator { - apermissions |= PermissionAllChannel - } - - return apermissions -} - -// ------------------------------------------------------------------------------------------------ -// Functions specific to Discord Guilds -// ------------------------------------------------------------------------------------------------ - -// Guild returns a Guild structure of a specific Guild. -// guildID : The ID of a Guild -func (s *Session) Guild(guildID string) (st *Guild, err error) { - if s.StateEnabled { - // Attempt to grab the guild from State first. - st, err = s.State.Guild(guildID) - if err == nil && !st.Unavailable { - return - } - } - - body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID), nil, EndpointGuild(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildCreate creates a new Guild -// name : A name for the Guild (2-100 characters) -func (s *Session) GuildCreate(name string) (st *Guild, err error) { - - data := struct { - Name string `json:"name"` - }{name} - - body, err := s.RequestWithBucketID("POST", EndpointGuildCreate, data, EndpointGuildCreate) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildEdit edits a new Guild -// guildID : The ID of a Guild -// g : A GuildParams struct with the values Name, Region and VerificationLevel defined. -func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error) { - - // Bounds checking for VerificationLevel, interval: [0, 4] - if g.VerificationLevel != nil { - val := *g.VerificationLevel - if val < 0 || val > 4 { - err = ErrVerificationLevelBounds - return - } - } - - //Bounds checking for regions - if g.Region != "" { - isValid := false - regions, _ := s.VoiceRegions() - for _, r := range regions { - if g.Region == r.ID { - isValid = true - } - } - if !isValid { - var valid []string - for _, r := range regions { - valid = append(valid, r.ID) - } - err = fmt.Errorf("Region not a valid region (%q)", valid) - return - } - } - - body, err := s.RequestWithBucketID("PATCH", EndpointGuild(guildID), g, EndpointGuild(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildDelete deletes a Guild. -// guildID : The ID of a Guild -func (s *Session) GuildDelete(guildID string) (st *Guild, err error) { - - body, err := s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildLeave leaves a Guild. -// guildID : The ID of a Guild -func (s *Session) GuildLeave(guildID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointUserGuild("@me", guildID), nil, EndpointUserGuild("", guildID)) - return -} - -// GuildBans returns an array of GuildBan structures for all bans of a -// given guild. -// guildID : The ID of a Guild. -func (s *Session) GuildBans(guildID string) (st []*GuildBan, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointGuildBans(guildID), nil, EndpointGuildBans(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// GuildBanCreate bans the given user from the given guild. -// guildID : The ID of a Guild. -// userID : The ID of a User -// days : The number of days of previous comments to delete. -func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) { - return s.GuildBanCreateWithReason(guildID, userID, "", days) -} - -// GuildBanCreateWithReason bans the given user from the given guild also providing a reaso. -// guildID : The ID of a Guild. -// userID : The ID of a User -// reason : The reason for this ban -// days : The number of days of previous comments to delete. -func (s *Session) GuildBanCreateWithReason(guildID, userID, reason string, days int) (err error) { - - uri := EndpointGuildBan(guildID, userID) - - queryParams := url.Values{} - if days > 0 { - queryParams.Set("delete-message-days", strconv.Itoa(days)) - } - if reason != "" { - queryParams.Set("reason", reason) - } - - if len(queryParams) > 0 { - uri += "?" + queryParams.Encode() - } - - _, err = s.RequestWithBucketID("PUT", uri, nil, EndpointGuildBan(guildID, "")) - return -} - -// GuildBanDelete removes the given user from the guild bans -// guildID : The ID of a Guild. -// userID : The ID of a User -func (s *Session) GuildBanDelete(guildID, userID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, "")) - return -} - -// GuildMembers returns a list of members for a guild. -// guildID : The ID of a Guild. -// after : The id of the member to return members after -// limit : max number of members to return (max 1000) -func (s *Session) GuildMembers(guildID string, after string, limit int) (st []*Member, err error) { - - uri := EndpointGuildMembers(guildID) - - v := url.Values{} - - if after != "" { - v.Set("after", after) - } - - if limit > 0 { - v.Set("limit", strconv.Itoa(limit)) - } - - if len(v) > 0 { - uri += "?" + v.Encode() - } - - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildMembers(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildMember returns a member of a guild. -// guildID : The ID of a Guild. -// userID : The ID of a User -func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointGuildMember(guildID, userID), nil, EndpointGuildMember(guildID, "")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildMemberAdd force joins a user to the guild. -// accessToken : Valid access_token for the user. -// guildID : The ID of a Guild. -// userID : The ID of a User. -// nick : Value to set users nickname to -// roles : A list of role ID's to set on the member. -// mute : If the user is muted. -// deaf : If the user is deafened. -func (s *Session) GuildMemberAdd(accessToken, guildID, userID, nick string, roles []string, mute, deaf bool) (err error) { - - data := struct { - AccessToken string `json:"access_token"` - Nick string `json:"nick,omitempty"` - Roles []string `json:"roles,omitempty"` - Mute bool `json:"mute,omitempty"` - Deaf bool `json:"deaf,omitempty"` - }{accessToken, nick, roles, mute, deaf} - - _, err = s.RequestWithBucketID("PUT", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) - if err != nil { - return err - } - - return err -} - -// GuildMemberDelete removes the given user from the given guild. -// guildID : The ID of a Guild. -// userID : The ID of a User -func (s *Session) GuildMemberDelete(guildID, userID string) (err error) { - - return s.GuildMemberDeleteWithReason(guildID, userID, "") -} - -// GuildMemberDeleteWithReason removes the given user from the given guild. -// guildID : The ID of a Guild. -// userID : The ID of a User -// reason : The reason for the kick -func (s *Session) GuildMemberDeleteWithReason(guildID, userID, reason string) (err error) { - - uri := EndpointGuildMember(guildID, userID) - if reason != "" { - uri += "?reason=" + url.QueryEscape(reason) - } - - _, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointGuildMember(guildID, "")) - return -} - -// GuildMemberEdit edits the roles of a member. -// guildID : The ID of a Guild. -// userID : The ID of a User. -// roles : A list of role ID's to set on the member. -func (s *Session) GuildMemberEdit(guildID, userID string, roles []string) (err error) { - - data := struct { - Roles []string `json:"roles"` - }{roles} - - _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) - if err != nil { - return - } - - return -} - -// GuildMemberMove moves a guild member from one voice channel to another/none -// guildID : The ID of a Guild. -// userID : The ID of a User. -// channelID : The ID of a channel to move user to, or null? -// NOTE : I am not entirely set on the name of this function and it may change -// prior to the final 1.0.0 release of Discordgo -func (s *Session) GuildMemberMove(guildID, userID, channelID string) (err error) { - - data := struct { - ChannelID string `json:"channel_id"` - }{channelID} - - _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) - if err != nil { - return - } - - return -} - -// GuildMemberNickname updates the nickname of a guild member -// guildID : The ID of a guild -// userID : The ID of a user -// userID : The ID of a user or "@me" which is a shortcut of the current user ID -func (s *Session) GuildMemberNickname(guildID, userID, nickname string) (err error) { - - data := struct { - Nick string `json:"nick"` - }{nickname} - - if userID == "@me" { - userID += "/nick" - } - - _, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, "")) - return -} - -// GuildMemberRoleAdd adds the specified role to a given member -// guildID : The ID of a Guild. -// userID : The ID of a User. -// roleID : The ID of a Role to be assigned to the user. -func (s *Session) GuildMemberRoleAdd(guildID, userID, roleID string) (err error) { - - _, err = s.RequestWithBucketID("PUT", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, "", "")) - - return -} - -// GuildMemberRoleRemove removes the specified role to a given member -// guildID : The ID of a Guild. -// userID : The ID of a User. -// roleID : The ID of a Role to be removed from the user. -func (s *Session) GuildMemberRoleRemove(guildID, userID, roleID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, "", "")) - - return -} - -// GuildChannels returns an array of Channel structures for all channels of a -// given guild. -// guildID : The ID of a Guild. -func (s *Session) GuildChannels(guildID string) (st []*Channel, err error) { - - body, err := s.request("GET", EndpointGuildChannels(guildID), "", nil, EndpointGuildChannels(guildID), 0) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// GuildChannelCreateData is provided to GuildChannelCreateComplex -type GuildChannelCreateData struct { - Name string `json:"name"` - Type ChannelType `json:"type"` - Topic string `json:"topic,omitempty"` - Bitrate int `json:"bitrate,omitempty"` - UserLimit int `json:"user_limit,omitempty"` - PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"` - ParentID string `json:"parent_id,omitempty"` - NSFW bool `json:"nsfw,omitempty"` -} - -// GuildChannelCreateComplex creates a new channel in the given guild -// guildID : The ID of a Guild -// data : A data struct describing the new Channel, Name and Type are mandatory, other fields depending on the type -func (s *Session) GuildChannelCreateComplex(guildID string, data GuildChannelCreateData) (st *Channel, err error) { - body, err := s.RequestWithBucketID("POST", EndpointGuildChannels(guildID), data, EndpointGuildChannels(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildChannelCreate creates a new channel in the given guild -// guildID : The ID of a Guild. -// name : Name of the channel (2-100 chars length) -// ctype : Type of the channel -func (s *Session) GuildChannelCreate(guildID, name string, ctype ChannelType) (st *Channel, err error) { - return s.GuildChannelCreateComplex(guildID, GuildChannelCreateData{ - Name: name, - Type: ctype, - }) -} - -// GuildChannelsReorder updates the order of channels in a guild -// guildID : The ID of a Guild. -// channels : Updated channels. -func (s *Session) GuildChannelsReorder(guildID string, channels []*Channel) (err error) { - - data := make([]struct { - ID string `json:"id"` - Position int `json:"position"` - }, len(channels)) - - for i, c := range channels { - data[i].ID = c.ID - data[i].Position = c.Position - } - - _, err = s.RequestWithBucketID("PATCH", EndpointGuildChannels(guildID), data, EndpointGuildChannels(guildID)) - return -} - -// GuildInvites returns an array of Invite structures for the given guild -// guildID : The ID of a Guild. -func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) { - body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInvites(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildRoles returns all roles for a given guild. -// guildID : The ID of a Guild. -func (s *Session) GuildRoles(guildID string) (st []*Role, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointGuildRoles(guildID), nil, EndpointGuildRoles(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return // TODO return pointer -} - -// GuildRoleCreate returns a new Guild Role. -// guildID: The ID of a Guild. -func (s *Session) GuildRoleCreate(guildID string) (st *Role, err error) { - - body, err := s.RequestWithBucketID("POST", EndpointGuildRoles(guildID), nil, EndpointGuildRoles(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// GuildRoleEdit updates an existing Guild Role with new values -// guildID : The ID of a Guild. -// roleID : The ID of a Role. -// name : The name of the Role. -// color : The color of the role (decimal, not hex). -// hoist : Whether to display the role's users separately. -// perm : The permissions for the role. -// mention : Whether this role is mentionable -func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist bool, perm int, mention bool) (st *Role, err error) { - - // Prevent sending a color int that is too big. - if color > 0xFFFFFF { - err = fmt.Errorf("color value cannot be larger than 0xFFFFFF") - return nil, err - } - - 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, color, hoist, perm, mention} - - body, err := s.RequestWithBucketID("PATCH", EndpointGuildRole(guildID, roleID), data, EndpointGuildRole(guildID, "")) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// GuildRoleReorder reoders guild roles -// guildID : The ID of a Guild. -// roles : A list of ordered roles. -func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, err error) { - - body, err := s.RequestWithBucketID("PATCH", EndpointGuildRoles(guildID), roles, EndpointGuildRoles(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// GuildRoleDelete deletes an existing role. -// guildID : The ID of a Guild. -// roleID : The ID of a Role. -func (s *Session) GuildRoleDelete(guildID, roleID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointGuildRole(guildID, roleID), nil, EndpointGuildRole(guildID, "")) - - return -} - -// GuildPruneCount Returns the number of members that would be removed in a prune operation. -// Requires 'KICK_MEMBER' permission. -// guildID : The ID of a Guild. -// days : The number of days to count prune for (1 or more). -func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, err error) { - count = 0 - - if days <= 0 { - err = ErrPruneDaysBounds - return - } - - p := struct { - Pruned uint32 `json:"pruned"` - }{} - - uri := EndpointGuildPrune(guildID) + "?days=" + strconv.FormatUint(uint64(days), 10) - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &p) - if err != nil { - return - } - - count = p.Pruned - - return -} - -// GuildPrune Begin as prune operation. Requires the 'KICK_MEMBERS' permission. -// Returns an object with one 'pruned' key indicating the number of members that were removed in the prune operation. -// guildID : The ID of a Guild. -// days : The number of days to count prune for (1 or more). -func (s *Session) GuildPrune(guildID string, days uint32) (count uint32, err error) { - - count = 0 - - if days <= 0 { - err = ErrPruneDaysBounds - return - } - - data := struct { - days uint32 - }{days} - - p := struct { - Pruned uint32 `json:"pruned"` - }{} - - body, err := s.RequestWithBucketID("POST", EndpointGuildPrune(guildID), data, EndpointGuildPrune(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &p) - if err != nil { - return - } - - count = p.Pruned - - return -} - -// GuildIntegrations returns an array of Integrations for a guild. -// guildID : The ID of a Guild. -func (s *Session) GuildIntegrations(guildID string) (st []*Integration, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointGuildIntegrations(guildID), nil, EndpointGuildIntegrations(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// GuildIntegrationCreate creates a Guild Integration. -// guildID : The ID of a Guild. -// integrationType : The Integration type. -// integrationID : The ID of an integration. -func (s *Session) GuildIntegrationCreate(guildID, integrationType, integrationID string) (err error) { - - data := struct { - Type string `json:"type"` - ID string `json:"id"` - }{integrationType, integrationID} - - _, err = s.RequestWithBucketID("POST", EndpointGuildIntegrations(guildID), data, EndpointGuildIntegrations(guildID)) - return -} - -// GuildIntegrationEdit edits a Guild Integration. -// guildID : The ID of a Guild. -// integrationType : The Integration type. -// integrationID : The ID of an integration. -// expireBehavior : The behavior when an integration subscription lapses (see the integration object documentation). -// expireGracePeriod : Period (in seconds) where the integration will ignore lapsed subscriptions. -// enableEmoticons : Whether emoticons should be synced for this integration (twitch only currently). -func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBehavior, expireGracePeriod int, enableEmoticons bool) (err error) { - - data := struct { - ExpireBehavior int `json:"expire_behavior"` - ExpireGracePeriod int `json:"expire_grace_period"` - EnableEmoticons bool `json:"enable_emoticons"` - }{expireBehavior, expireGracePeriod, enableEmoticons} - - _, err = s.RequestWithBucketID("PATCH", EndpointGuildIntegration(guildID, integrationID), data, EndpointGuildIntegration(guildID, "")) - return -} - -// GuildIntegrationDelete removes the given integration from the Guild. -// guildID : The ID of a Guild. -// integrationID : The ID of an integration. -func (s *Session) GuildIntegrationDelete(guildID, integrationID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointGuildIntegration(guildID, integrationID), nil, EndpointGuildIntegration(guildID, "")) - 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) { - g, err := s.Guild(guildID) - if err != nil { - return - } - - if g.Icon == "" { - err = ErrGuildNoIcon - return - } - - body, err := s.RequestWithBucketID("GET", EndpointGuildIcon(guildID, g.Icon), nil, EndpointGuildIcon(guildID, "")) - if err != nil { - return - } - - img, _, err = image.Decode(bytes.NewReader(body)) - return -} - -// GuildSplash returns an image.Image of a guild splash image. -// guildID : The ID of a Guild. -func (s *Session) GuildSplash(guildID string) (img image.Image, err error) { - g, err := s.Guild(guildID) - if err != nil { - return - } - - if g.Splash == "" { - err = ErrGuildNoSplash - return - } - - body, err := s.RequestWithBucketID("GET", EndpointGuildSplash(guildID, g.Splash), nil, EndpointGuildSplash(guildID, "")) - if err != nil { - return - } - - img, _, err = image.Decode(bytes.NewReader(body)) - return -} - -// GuildEmbed returns the embed for a Guild. -// guildID : The ID of a Guild. -func (s *Session) GuildEmbed(guildID string) (st *GuildEmbed, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointGuildEmbed(guildID), nil, EndpointGuildEmbed(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildEmbedEdit returns the embed for a Guild. -// guildID : The ID of a Guild. -func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string) (err error) { - - data := GuildEmbed{enabled, channelID} - - _, err = s.RequestWithBucketID("PATCH", EndpointGuildEmbed(guildID), data, EndpointGuildEmbed(guildID)) - return -} - -// GuildAuditLog returns the audit log for a Guild. -// guildID : The ID of a Guild. -// userID : If provided the log will be filtered for the given ID. -// beforeID : If provided all log entries returned will be before the given ID. -// actionType : If provided the log will be filtered for the given Action Type. -// limit : The number messages that can be returned. (default 50, min 1, max 100) -func (s *Session) GuildAuditLog(guildID, userID, beforeID string, actionType, limit int) (st *GuildAuditLog, err error) { - - uri := EndpointGuildAuditLogs(guildID) - - v := url.Values{} - if userID != "" { - v.Set("user_id", userID) - } - if beforeID != "" { - v.Set("before", beforeID) - } - if actionType > 0 { - v.Set("action_type", strconv.Itoa(actionType)) - } - if limit > 0 { - v.Set("limit", strconv.Itoa(limit)) - } - if len(v) > 0 { - uri = fmt.Sprintf("%s?%s", uri, v.Encode()) - } - - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildAuditLogs(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// GuildEmojiCreate creates a new emoji -// guildID : The ID of a Guild. -// name : The Name of the Emoji. -// image : The base64 encoded emoji image, has to be smaller than 256KB. -// roles : The roles for which this emoji will be whitelisted, can be nil. -func (s *Session) GuildEmojiCreate(guildID, name, image string, roles []string) (emoji *Emoji, err error) { - - data := struct { - Name string `json:"name"` - Image string `json:"image"` - Roles []string `json:"roles,omitempty"` - }{name, image, roles} - - body, err := s.RequestWithBucketID("POST", EndpointGuildEmojis(guildID), data, EndpointGuildEmojis(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &emoji) - return -} - -// GuildEmojiEdit modifies an emoji -// guildID : The ID of a Guild. -// emojiID : The ID of an Emoji. -// name : The Name of the Emoji. -// roles : The roles for which this emoji will be whitelisted, can be nil. -func (s *Session) GuildEmojiEdit(guildID, emojiID, name string, roles []string) (emoji *Emoji, err error) { - - data := struct { - Name string `json:"name"` - Roles []string `json:"roles,omitempty"` - }{name, roles} - - body, err := s.RequestWithBucketID("PATCH", EndpointGuildEmoji(guildID, emojiID), data, EndpointGuildEmojis(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &emoji) - return -} - -// GuildEmojiDelete deletes an Emoji. -// guildID : The ID of a Guild. -// emojiID : The ID of an Emoji. -func (s *Session) GuildEmojiDelete(guildID, emojiID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointGuildEmoji(guildID, emojiID), nil, EndpointGuildEmojis(guildID)) - return -} - -// ------------------------------------------------------------------------------------------------ -// Functions specific to Discord Channels -// ------------------------------------------------------------------------------------------------ - -// Channel returns a Channel structure of a specific Channel. -// channelID : The ID of the Channel you want returned. -func (s *Session) Channel(channelID string) (st *Channel, err error) { - body, err := s.RequestWithBucketID("GET", EndpointChannel(channelID), nil, EndpointChannel(channelID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ChannelEdit edits the given channel -// channelID : The ID of a Channel -// name : The new name to assign the channel. -func (s *Session) ChannelEdit(channelID, name string) (*Channel, error) { - return s.ChannelEditComplex(channelID, &ChannelEdit{ - Name: name, - }) -} - -// ChannelEditComplex edits an existing channel, replacing the parameters entirely with ChannelEdit struct -// channelID : The ID of a Channel -// data : The channel struct to send -func (s *Session) ChannelEditComplex(channelID string, data *ChannelEdit) (st *Channel, err error) { - body, err := s.RequestWithBucketID("PATCH", EndpointChannel(channelID), data, EndpointChannel(channelID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ChannelDelete deletes the given channel -// channelID : The ID of a Channel -func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) { - - body, err := s.RequestWithBucketID("DELETE", EndpointChannel(channelID), nil, EndpointChannel(channelID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ChannelTyping broadcasts to all members that authenticated user is typing in -// the given channel. -// channelID : The ID of a Channel -func (s *Session) ChannelTyping(channelID string) (err error) { - - _, err = s.RequestWithBucketID("POST", EndpointChannelTyping(channelID), nil, EndpointChannelTyping(channelID)) - return -} - -// ChannelMessages returns an array of Message structures for messages within -// a given channel. -// channelID : The ID of a Channel. -// limit : The number messages that can be returned. (max 100) -// beforeID : If provided all messages returned will be before given ID. -// afterID : If provided all messages returned will be after given ID. -// aroundID : If provided all messages returned will be around given ID. -func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID, aroundID string) (st []*Message, err error) { - - uri := EndpointChannelMessages(channelID) - - v := url.Values{} - if limit > 0 { - v.Set("limit", strconv.Itoa(limit)) - } - if afterID != "" { - v.Set("after", afterID) - } - if beforeID != "" { - v.Set("before", beforeID) - } - if aroundID != "" { - v.Set("around", aroundID) - } - if len(v) > 0 { - uri += "?" + v.Encode() - } - - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointChannelMessages(channelID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ChannelMessage gets a single message by ID from a given channel. -// channeld : The ID of a Channel -// messageID : the ID of a Message -func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err error) { - - response, err := s.RequestWithBucketID("GET", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, "")) - if err != nil { - return - } - - err = unmarshal(response, &st) - 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. -func (s *Session) ChannelMessageSend(channelID string, content string) (*Message, error) { - return s.ChannelMessageSendComplex(channelID, &MessageSend{ - Content: content, - }) -} - -var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") - -// ChannelMessageSendComplex sends a message to the given channel. -// 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" - } - - endpoint := EndpointChannelMessages(channelID) - - // TODO: Remove this when compatibility is not required. - files := data.Files - if data.File != nil { - if files == nil { - files = []*File{data.File} - } else { - err = fmt.Errorf("cannot specify both File and Files") - return - } - } - - 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 - } - - 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) - } else { - response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint) - } - if err != nil { - return - } - - err = unmarshal(response, &st) - return -} - -// ChannelMessageSendTTS sends a message to the given channel with Text to Speech. -// channelID : The ID of a Channel. -// content : The message to send. -func (s *Session) ChannelMessageSendTTS(channelID string, content string) (*Message, error) { - return s.ChannelMessageSendComplex(channelID, &MessageSend{ - Content: content, - Tts: true, - }) -} - -// ChannelMessageSendEmbed sends a message to the given channel with embedded data. -// channelID : The ID of a Channel. -// embed : The embed data to send. -func (s *Session) ChannelMessageSendEmbed(channelID string, embed *MessageEmbed) (*Message, error) { - return s.ChannelMessageSendComplex(channelID, &MessageSend{ - Embed: embed, - }) -} - -// ChannelMessageEdit edits an existing message, replacing it entirely with -// the given content. -// channelID : The ID of a Channel -// messageID : The ID of a Message -// content : The contents of the message -func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (*Message, error) { - return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetContent(content)) -} - -// 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" - } - - response, err := s.RequestWithBucketID("PATCH", EndpointChannelMessage(m.Channel, m.ID), m, EndpointChannelMessage(m.Channel, "")) - if err != nil { - return - } - - err = unmarshal(response, &st) - return -} - -// ChannelMessageEditEmbed edits an existing message with embedded data. -// channelID : The ID of a Channel -// 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)) -} - -// ChannelMessageDelete deletes a message from the Channel. -func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, "")) - return -} - -// ChannelMessagesBulkDelete bulk deletes the messages from the channel for the provided messageIDs. -// If only one messageID is in the slice call channelMessageDelete function. -// If the slice is empty do nothing. -// channelID : The ID of the channel for the messages to delete. -// messages : The IDs of the messages to be deleted. A slice of string IDs. A maximum of 100 messages. -func (s *Session) ChannelMessagesBulkDelete(channelID string, messages []string) (err error) { - - if len(messages) == 0 { - return - } - - if len(messages) == 1 { - err = s.ChannelMessageDelete(channelID, messages[0]) - return - } - - if len(messages) > 100 { - messages = messages[:100] - } - - data := struct { - Messages []string `json:"messages"` - }{messages} - - _, err = s.RequestWithBucketID("POST", EndpointChannelMessagesBulkDelete(channelID), data, EndpointChannelMessagesBulkDelete(channelID)) - return -} - -// ChannelMessagePin pins a message within a given channel. -// channelID: The ID of a channel. -// messageID: The ID of a message. -func (s *Session) ChannelMessagePin(channelID, messageID string) (err error) { - - _, err = s.RequestWithBucketID("PUT", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, "")) - return -} - -// ChannelMessageUnpin unpins a message within a given channel. -// channelID: The ID of a channel. -// messageID: The ID of a message. -func (s *Session) ChannelMessageUnpin(channelID, messageID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, "")) - return -} - -// ChannelMessagesPinned returns an array of Message structures for pinned messages -// within a given channel -// channelID : The ID of a Channel. -func (s *Session) ChannelMessagesPinned(channelID string) (st []*Message, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointChannelMessagesPins(channelID), nil, EndpointChannelMessagesPins(channelID)) - - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ChannelFileSend sends a file to the given channel. -// channelID : The ID of a Channel. -// name: The name of the file. -// io.Reader : A reader for the file contents. -func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (*Message, error) { - return s.ChannelMessageSendComplex(channelID, &MessageSend{File: &File{Name: name, Reader: r}}) -} - -// ChannelFileSendWithMessage sends a file to the given channel with an message. -// DEPRECATED. Use ChannelMessageSendComplex instead. -// channelID : The ID of a Channel. -// content: Optional Message content. -// name: The name of the file. -// io.Reader : A reader for the file contents. -func (s *Session) ChannelFileSendWithMessage(channelID, content string, name string, r io.Reader) (*Message, error) { - return s.ChannelMessageSendComplex(channelID, &MessageSend{File: &File{Name: name, Reader: r}, Content: content}) -} - -// ChannelInvites returns an array of Invite structures for the given channel -// channelID : The ID of a Channel -func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointChannelInvites(channelID), nil, EndpointChannelInvites(channelID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ChannelInviteCreate creates a new invite for the given channel. -// channelID : The ID of a Channel -// i : An Invite struct with the values MaxAge, MaxUses and Temporary defined. -func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, err error) { - - data := struct { - MaxAge int `json:"max_age"` - MaxUses int `json:"max_uses"` - Temporary bool `json:"temporary"` - Unique bool `json:"unique"` - }{i.MaxAge, i.MaxUses, i.Temporary, i.Unique} - - body, err := s.RequestWithBucketID("POST", EndpointChannelInvites(channelID), data, EndpointChannelInvites(channelID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// 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, targetType string, allow, deny int) (err error) { - - data := struct { - ID string `json:"id"` - Type string `json:"type"` - Allow int `json:"allow"` - Deny int `json:"deny"` - }{targetID, targetType, allow, deny} - - _, err = s.RequestWithBucketID("PUT", EndpointChannelPermission(channelID, targetID), data, EndpointChannelPermission(channelID, "")) - return -} - -// ChannelPermissionDelete deletes a specific permission override for the given channel. -// NOTE: Name of this func may change. -func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointChannelPermission(channelID, targetID), nil, EndpointChannelPermission(channelID, "")) - return -} - -// ------------------------------------------------------------------------------------------------ -// Functions specific to Discord Invites -// ------------------------------------------------------------------------------------------------ - -// Invite returns an Invite structure of the given invite -// inviteID : The invite code -func (s *Session) Invite(inviteID string) (st *Invite, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID), nil, EndpointInvite("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// InviteWithCounts returns an Invite structure of the given invite including approximate member counts -// inviteID : The invite code -func (s *Session) InviteWithCounts(inviteID string) (st *Invite, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID)+"?with_counts=true", nil, EndpointInvite("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// InviteDelete deletes an existing invite -// inviteID : the code of an invite -func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) { - - body, err := s.RequestWithBucketID("DELETE", EndpointInvite(inviteID), nil, EndpointInvite("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// InviteAccept accepts an Invite to a Guild or Channel -// inviteID : The invite code -func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) { - - body, err := s.RequestWithBucketID("POST", EndpointInvite(inviteID), nil, EndpointInvite("")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ------------------------------------------------------------------------------------------------ -// Functions specific to Discord Voice -// ------------------------------------------------------------------------------------------------ - -// VoiceRegions returns the voice server regions -func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointVoiceRegions, nil, EndpointVoiceRegions) - if err != nil { - return - } - - err = unmarshal(body, &st) - 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 -// ------------------------------------------------------------------------------------------------ - -// Gateway returns the websocket Gateway address -func (s *Session) Gateway() (gateway string, err error) { - - response, err := s.RequestWithBucketID("GET", EndpointGateway, nil, EndpointGateway) - if err != nil { - return - } - - temp := struct { - URL string `json:"url"` - }{} - - err = unmarshal(response, &temp) - if err != nil { - return - } - - gateway = temp.URL - - // Ensure the gateway always has a trailing slash. - // MacOS will fail to connect if we add query params without a trailing slash on the base domain. - if !strings.HasSuffix(gateway, "/") { - gateway += "/" - } - - return -} - -// GatewayBot returns the websocket Gateway address and the recommended number of shards -func (s *Session) GatewayBot() (st *GatewayBotResponse, err error) { - - response, err := s.RequestWithBucketID("GET", EndpointGatewayBot, nil, EndpointGatewayBot) - if err != nil { - return - } - - err = unmarshal(response, &st) - if err != nil { - return - } - - // Ensure the gateway always has a trailing slash. - // MacOS will fail to connect if we add query params without a trailing slash on the base domain. - if !strings.HasSuffix(st.URL, "/") { - st.URL += "/" - } - - return -} - -// Functions specific to Webhooks - -// WebhookCreate returns a new Webhook. -// channelID: The ID of a Channel. -// name : The name of the webhook. -// avatar : The avatar of the webhook. -func (s *Session) WebhookCreate(channelID, name, avatar string) (st *Webhook, err error) { - - data := struct { - Name string `json:"name"` - Avatar string `json:"avatar,omitempty"` - }{name, avatar} - - body, err := s.RequestWithBucketID("POST", EndpointChannelWebhooks(channelID), data, EndpointChannelWebhooks(channelID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// ChannelWebhooks returns all webhooks for a given channel. -// channelID: The ID of a channel. -func (s *Session) ChannelWebhooks(channelID string) (st []*Webhook, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointChannelWebhooks(channelID), nil, EndpointChannelWebhooks(channelID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// GuildWebhooks returns all webhooks for a given guild. -// guildID: The ID of a Guild. -func (s *Session) GuildWebhooks(guildID string) (st []*Webhook, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointGuildWebhooks(guildID), nil, EndpointGuildWebhooks(guildID)) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// Webhook returns a webhook for a given ID -// webhookID: The ID of a webhook. -func (s *Session) Webhook(webhookID string) (st *Webhook, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointWebhook(webhookID), nil, EndpointWebhooks) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// WebhookWithToken returns a webhook for a given ID -// webhookID: The ID of a webhook. -// token : The auth token for the webhook. -func (s *Session) WebhookWithToken(webhookID, token string) (st *Webhook, err error) { - - body, err := s.RequestWithBucketID("GET", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", "")) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// WebhookEdit updates an existing Webhook. -// webhookID: The ID of a webhook. -// name : The name of the webhook. -// avatar : The avatar of the webhook. -func (s *Session) WebhookEdit(webhookID, name, avatar, channelID string) (st *Role, err error) { - - data := struct { - Name string `json:"name,omitempty"` - Avatar string `json:"avatar,omitempty"` - ChannelID string `json:"channel_id,omitempty"` - }{name, avatar, channelID} - - body, err := s.RequestWithBucketID("PATCH", EndpointWebhook(webhookID), data, EndpointWebhooks) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// WebhookEditWithToken updates an existing Webhook with an auth token. -// webhookID: The ID of a webhook. -// token : The auth token for the webhook. -// name : The name of the webhook. -// avatar : The avatar of the webhook. -func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string) (st *Role, err error) { - - data := struct { - Name string `json:"name,omitempty"` - Avatar string `json:"avatar,omitempty"` - }{name, avatar} - - body, err := s.RequestWithBucketID("PATCH", EndpointWebhookToken(webhookID, token), data, EndpointWebhookToken("", "")) - if err != nil { - return - } - - err = unmarshal(body, &st) - - return -} - -// WebhookDelete deletes a webhook for a given ID -// webhookID: The ID of a webhook. -func (s *Session) WebhookDelete(webhookID string) (err error) { - - _, err = s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks) - - return -} - -// WebhookDeleteWithToken deletes a webhook for a given ID with an auth token. -// webhookID: The ID of a webhook. -// token : The auth token for the webhook. -func (s *Session) WebhookDeleteWithToken(webhookID, token string) (st *Webhook, err error) { - - body, err := s.RequestWithBucketID("DELETE", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", "")) - if err != nil { - return - } - - err = unmarshal(body, &st) - - 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) { - uri := EndpointWebhookToken(webhookID, token) - - if wait { - uri += "?wait=true" - } - - 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 - } - - 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) - } else { - response, err = s.RequestWithBucketID("POST", uri, data, EndpointWebhookToken("", "")) - } - if err != nil { - return - } - if !wait { - return - } - - err = unmarshal(response, &st) - return -} - -// MessageReactionAdd creates an emoji reaction to a message. -// channelID : The channel ID. -// messageID : The message ID. -// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier. -func (s *Session) MessageReactionAdd(channelID, messageID, emojiID string) error { - - // emoji such as #⃣ need to have # escaped - emojiID = strings.Replace(emojiID, "#", "%23", -1) - _, err := s.RequestWithBucketID("PUT", EndpointMessageReaction(channelID, messageID, emojiID, "@me"), nil, EndpointMessageReaction(channelID, "", "", "")) - - return err -} - -// MessageReactionRemove deletes an emoji reaction to a message. -// channelID : The channel ID. -// messageID : The message ID. -// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier. -// userID : @me or ID of the user to delete the reaction for. -func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID string) error { - - // emoji such as #⃣ need to have # escaped - emojiID = strings.Replace(emojiID, "#", "%23", -1) - _, err := s.RequestWithBucketID("DELETE", EndpointMessageReaction(channelID, messageID, emojiID, userID), nil, EndpointMessageReaction(channelID, "", "", "")) - - return err -} - -// MessageReactionsRemoveAll deletes all reactions from a message -// channelID : The channel ID -// messageID : The message ID. -func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error { - - _, err := s.RequestWithBucketID("DELETE", EndpointMessageReactionsAll(channelID, messageID), nil, EndpointMessageReactionsAll(channelID, messageID)) - - return err -} - -// MessageReactions gets all the users reactions for a specific emoji. -// channelID : The channel ID. -// messageID : The message ID. -// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier. -// limit : max number of users to return (max 100) -func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int) (st []*User, err error) { - // emoji such as #⃣ need to have # escaped - emojiID = strings.Replace(emojiID, "#", "%23", -1) - uri := EndpointMessageReactions(channelID, messageID, emojiID) - - v := url.Values{} - - if limit > 0 { - v.Set("limit", strconv.Itoa(limit)) - } - - if len(v) > 0 { - uri += "?" + v.Encode() - } - - body, err := s.RequestWithBucketID("GET", uri, nil, EndpointMessageReaction(channelID, "", "", "")) - if err != nil { - return - } - - err = unmarshal(body, &st) - return -} - -// ------------------------------------------------------------------------------------------------ -// Functions specific to user notes -// ------------------------------------------------------------------------------------------------ - -// 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()) - if err != nil { - return - } - - err = unmarshal(body, &r) - 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 -} - -// 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)) - if err != nil { - return - } - - err = unmarshal(body, &mf) - return -} diff --git a/vendor/github.com/bwmarrin/discordgo/state.go b/vendor/github.com/bwmarrin/discordgo/state.go deleted file mode 100644 index 7babc115..00000000 --- a/vendor/github.com/bwmarrin/discordgo/state.go +++ /dev/null @@ -1,1022 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 code related to state tracking. If enabled, state -// tracking will capture the initial READY packet and many other websocket -// events and maintain an in-memory state of of guilds, channels, users, and -// so forth. This information can be accessed through the Session.State struct. - -package discordgo - -import ( - "errors" - "sort" - "sync" -) - -// ErrNilState is returned when the state is nil. -var ErrNilState = errors.New("state not instantiated, please use discordgo.New() or assign Session.State") - -// ErrStateNotFound is returned when the state cache -// requested is not found -var ErrStateNotFound = errors.New("state cache not found") - -// A State contains the current known state. -// As discord sends this in a READY blob, it seems reasonable to simply -// use that struct as the data store. -type State struct { - sync.RWMutex - 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 - - guildMap map[string]*Guild - channelMap map[string]*Channel - memberMap map[string]map[string]*Member -} - -// NewState creates an empty state. -func NewState() *State { - return &State{ - Ready: Ready{ - 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), - } -} - -func (s *State) createMemberMap(guild *Guild) { - members := make(map[string]*Member) - for _, m := range guild.Members { - members[m.User.ID] = m - } - s.memberMap[guild.ID] = members -} - -// GuildAdd adds a guild to the current world state, or -// updates it if it already exists. -func (s *State) GuildAdd(guild *Guild) error { - if s == nil { - return ErrNilState - } - - s.Lock() - defer s.Unlock() - - // Update the channels to point to the right guild, adding them to the channelMap as we go - for _, c := range guild.Channels { - s.channelMap[c.ID] = c - } - - // 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) - } else if _, ok := s.memberMap[guild.ID]; !ok { - // Even if we have no new member slice, we still initialize the member map for this guild if it doesn't exist - s.memberMap[guild.ID] = make(map[string]*Member) - } - - if g, ok := s.guildMap[guild.ID]; ok { - // We are about to replace `g` in the state with `guild`, but first we need to - // make sure we preserve any fields that the `guild` doesn't contain from `g`. - if guild.MemberCount == 0 { - guild.MemberCount = g.MemberCount - } - if guild.Roles == nil { - guild.Roles = g.Roles - } - if guild.Emojis == nil { - guild.Emojis = g.Emojis - } - if guild.Members == nil { - guild.Members = g.Members - } - if guild.Presences == nil { - guild.Presences = g.Presences - } - if guild.Channels == nil { - guild.Channels = g.Channels - } - if guild.VoiceStates == nil { - guild.VoiceStates = g.VoiceStates - } - *g = *guild - return nil - } - - s.Guilds = append(s.Guilds, guild) - s.guildMap[guild.ID] = guild - - return nil -} - -// GuildRemove removes a guild from current world state. -func (s *State) GuildRemove(guild *Guild) error { - if s == nil { - return ErrNilState - } - - _, err := s.Guild(guild.ID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - delete(s.guildMap, guild.ID) - - for i, g := range s.Guilds { - if g.ID == guild.ID { - s.Guilds = append(s.Guilds[:i], s.Guilds[i+1:]...) - return nil - } - } - - return nil -} - -// Guild gets a guild by ID. -// Useful for querying if @me is in a guild: -// _, err := discordgo.Session.State.Guild(guildID) -// isInGuild := err == nil -func (s *State) Guild(guildID string) (*Guild, error) { - if s == nil { - return nil, ErrNilState - } - - s.RLock() - defer s.RUnlock() - - if g, ok := s.guildMap[guildID]; ok { - return g, nil - } - - 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 - } - - 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 - - //Update status - guild.Presences[i].Game = presence.Game - guild.Presences[i].Roles = presence.Roles - if presence.Status != "" { - guild.Presences[i].Status = presence.Status - } - if presence.Nick != "" { - guild.Presences[i].Nick = presence.Nick - } - - //Update the optionally sent user information - //ID Is a mandatory field so you should not need to check if it is empty - guild.Presences[i].User.ID = presence.User.ID - - if presence.User.Avatar != "" { - guild.Presences[i].User.Avatar = presence.User.Avatar - } - if presence.User.Discriminator != "" { - guild.Presences[i].User.Discriminator = presence.User.Discriminator - } - if presence.User.Email != "" { - guild.Presences[i].User.Email = presence.User.Email - } - if presence.User.Token != "" { - guild.Presences[i].User.Token = presence.User.Token - } - if presence.User.Username != "" { - guild.Presences[i].User.Username = presence.User.Username - } - - return nil - } - } - - guild.Presences = append(guild.Presences, presence) - return nil -} - -// PresenceRemove removes a presence from the current world state. -func (s *State) PresenceRemove(guildID string, presence *Presence) error { - if s == nil { - return ErrNilState - } - - 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 = append(guild.Presences[:i], guild.Presences[i+1:]...) - return nil - } - } - - return ErrStateNotFound -} - -// Presence gets a presence by ID from a guild. -func (s *State) Presence(guildID, userID string) (*Presence, error) { - if s == nil { - return nil, ErrNilState - } - - guild, err := s.Guild(guildID) - if err != nil { - return nil, err - } - - for _, p := range guild.Presences { - if p.User.ID == userID { - return p, nil - } - } - - return nil, ErrStateNotFound -} - -// 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 - } - - 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 - } - - m, ok := members[member.User.ID] - if !ok { - members[member.User.ID] = member - guild.Members = append(guild.Members, member) - } 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 == "" { - member.JoinedAt = m.JoinedAt - } - *m = *member - } - - return nil -} - -// MemberRemove removes a member from current world state. -func (s *State) MemberRemove(member *Member) error { - if s == nil { - return ErrNilState - } - - 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 - } - - _, ok = members[member.User.ID] - if !ok { - return ErrStateNotFound - } - delete(members, member.User.ID) - - for i, m := range guild.Members { - if m.User.ID == member.User.ID { - guild.Members = append(guild.Members[:i], guild.Members[i+1:]...) - return nil - } - } - - return ErrStateNotFound -} - -// Member gets a member by ID from a guild. -func (s *State) Member(guildID, userID string) (*Member, error) { - if s == nil { - return nil, ErrNilState - } - - s.RLock() - defer s.RUnlock() - - members, ok := s.memberMap[guildID] - if !ok { - return nil, ErrStateNotFound - } - - m, ok := members[userID] - if ok { - return m, nil - } - - return nil, ErrStateNotFound -} - -// RoleAdd adds a role to the current world state, or -// updates it if it already exists. -func (s *State) RoleAdd(guildID string, role *Role) error { - if s == nil { - return ErrNilState - } - - guild, err := s.Guild(guildID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - for i, r := range guild.Roles { - if r.ID == role.ID { - guild.Roles[i] = role - return nil - } - } - - guild.Roles = append(guild.Roles, role) - return nil -} - -// RoleRemove removes a role from current world state by ID. -func (s *State) RoleRemove(guildID, roleID string) error { - if s == nil { - return ErrNilState - } - - guild, err := s.Guild(guildID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - for i, r := range guild.Roles { - if r.ID == roleID { - guild.Roles = append(guild.Roles[:i], guild.Roles[i+1:]...) - return nil - } - } - - return ErrStateNotFound -} - -// Role gets a role by ID from a guild. -func (s *State) Role(guildID, roleID string) (*Role, error) { - if s == nil { - return nil, ErrNilState - } - - guild, err := s.Guild(guildID) - if err != nil { - return nil, err - } - - s.RLock() - defer s.RUnlock() - - for _, r := range guild.Roles { - if r.ID == roleID { - return r, nil - } - } - - return nil, ErrStateNotFound -} - -// ChannelAdd adds a channel to the current world state, or -// updates it if it already exists. -// Channels may exist either as PrivateChannels or inside -// a guild. -func (s *State) ChannelAdd(channel *Channel) error { - if s == nil { - return ErrNilState - } - - s.Lock() - defer s.Unlock() - - // If the channel exists, replace it - if c, ok := s.channelMap[channel.ID]; ok { - if channel.Messages == nil { - channel.Messages = c.Messages - } - if channel.PermissionOverwrites == nil { - channel.PermissionOverwrites = c.PermissionOverwrites - } - - *c = *channel - return nil - } - - if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM { - s.PrivateChannels = append(s.PrivateChannels, channel) - } else { - guild, ok := s.guildMap[channel.GuildID] - if !ok { - return ErrStateNotFound - } - - guild.Channels = append(guild.Channels, channel) - } - - s.channelMap[channel.ID] = channel - - return nil -} - -// ChannelRemove removes a channel from current world state. -func (s *State) ChannelRemove(channel *Channel) error { - if s == nil { - return ErrNilState - } - - _, err := s.Channel(channel.ID) - if err != nil { - return err - } - - if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM { - s.Lock() - defer s.Unlock() - - for i, c := range s.PrivateChannels { - if c.ID == channel.ID { - s.PrivateChannels = append(s.PrivateChannels[:i], s.PrivateChannels[i+1:]...) - break - } - } - } else { - guild, err := s.Guild(channel.GuildID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - for i, c := range guild.Channels { - if c.ID == channel.ID { - guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...) - break - } - } - } - - delete(s.channelMap, channel.ID) - - 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) { - return s.Channel(channelID) -} - -// PrivateChannel gets a private channel by ID. -// This method is Deprecated, use Channel(channelID) -func (s *State) PrivateChannel(channelID string) (*Channel, error) { - return s.Channel(channelID) -} - -// Channel gets a channel by ID, it will look in all guilds and private channels. -func (s *State) Channel(channelID string) (*Channel, error) { - if s == nil { - return nil, ErrNilState - } - - s.RLock() - defer s.RUnlock() - - if c, ok := s.channelMap[channelID]; ok { - return c, nil - } - - return nil, ErrStateNotFound -} - -// Emoji returns an emoji for a guild and emoji id. -func (s *State) Emoji(guildID, emojiID string) (*Emoji, error) { - if s == nil { - return nil, ErrNilState - } - - guild, err := s.Guild(guildID) - if err != nil { - return nil, err - } - - s.RLock() - defer s.RUnlock() - - for _, e := range guild.Emojis { - if e.ID == emojiID { - return e, nil - } - } - - return nil, ErrStateNotFound -} - -// EmojiAdd adds an emoji to the current world state. -func (s *State) EmojiAdd(guildID string, emoji *Emoji) error { - if s == nil { - return ErrNilState - } - - guild, err := s.Guild(guildID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - for i, e := range guild.Emojis { - if e.ID == emoji.ID { - guild.Emojis[i] = emoji - return nil - } - } - - guild.Emojis = append(guild.Emojis, emoji) - return nil -} - -// EmojisAdd adds multiple emojis to the world state. -func (s *State) EmojisAdd(guildID string, emojis []*Emoji) error { - for _, e := range emojis { - if err := s.EmojiAdd(guildID, e); err != nil { - return err - } - } - return nil -} - -// MessageAdd adds a message to the current world state, or updates it if it exists. -// If the channel cannot be found, the message is discarded. -// Messages are kept in state up to s.MaxMessageCount per channel. -func (s *State) MessageAdd(message *Message) error { - if s == nil { - return ErrNilState - } - - c, err := s.Channel(message.ChannelID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - // If the message exists, merge in the new message contents. - for _, m := range c.Messages { - if m.ID == message.ID { - if message.Content != "" { - m.Content = message.Content - } - if message.EditedTimestamp != "" { - m.EditedTimestamp = message.EditedTimestamp - } - if message.Mentions != nil { - m.Mentions = message.Mentions - } - if message.Embeds != nil { - m.Embeds = message.Embeds - } - if message.Attachments != nil { - m.Attachments = message.Attachments - } - if message.Timestamp != "" { - m.Timestamp = message.Timestamp - } - if message.Author != nil { - m.Author = message.Author - } - - return nil - } - } - - c.Messages = append(c.Messages, message) - - if len(c.Messages) > s.MaxMessageCount { - c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:] - } - return nil -} - -// MessageRemove removes a message from the world state. -func (s *State) MessageRemove(message *Message) error { - if s == nil { - return ErrNilState - } - - return s.messageRemoveByID(message.ChannelID, message.ID) -} - -// messageRemoveByID removes a message by channelID and messageID from the world state. -func (s *State) messageRemoveByID(channelID, messageID string) error { - c, err := s.Channel(channelID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - for i, m := range c.Messages { - if m.ID == messageID { - c.Messages = append(c.Messages[:i], c.Messages[i+1:]...) - return nil - } - } - - return ErrStateNotFound -} - -func (s *State) voiceStateUpdate(update *VoiceStateUpdate) error { - guild, err := s.Guild(update.GuildID) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - // Handle Leaving Channel - if update.ChannelID == "" { - for i, state := range guild.VoiceStates { - if state.UserID == update.UserID { - guild.VoiceStates = append(guild.VoiceStates[:i], guild.VoiceStates[i+1:]...) - return nil - } - } - } else { - for i, state := range guild.VoiceStates { - if state.UserID == update.UserID { - guild.VoiceStates[i] = update.VoiceState - return nil - } - } - - guild.VoiceStates = append(guild.VoiceStates, update.VoiceState) - } - - return nil -} - -// Message gets a message by channel and message ID. -func (s *State) Message(channelID, messageID string) (*Message, error) { - if s == nil { - return nil, ErrNilState - } - - c, err := s.Channel(channelID) - if err != nil { - return nil, err - } - - s.RLock() - defer s.RUnlock() - - for _, m := range c.Messages { - if m.ID == messageID { - return m, nil - } - } - - return nil, ErrStateNotFound -} - -// OnReady takes a Ready event and updates all internal state. -func (s *State) onReady(se *Session, r *Ready) (err error) { - if s == nil { - return ErrNilState - } - - s.Lock() - defer s.Unlock() - - // We must track at least the current user for Voice, even - // if state is disabled, store the bare essentials. - if !se.StateEnabled { - ready := Ready{ - Version: r.Version, - SessionID: r.SessionID, - User: r.User, - } - - s.Ready = ready - - return nil - } - - s.Ready = *r - - for _, g := range s.Guilds { - s.guildMap[g.ID] = g - s.createMemberMap(g) - - for _, c := range g.Channels { - s.channelMap[c.ID] = c - } - } - - for _, c := range s.PrivateChannels { - s.channelMap[c.ID] = c - } - - return nil -} - -// OnInterface handles all events related to states. -func (s *State) OnInterface(se *Session, i interface{}) (err error) { - if s == nil { - return ErrNilState - } - - r, ok := i.(*Ready) - if ok { - return s.onReady(se, r) - } - - if !se.StateEnabled { - return nil - } - - switch t := i.(type) { - case *GuildCreate: - err = s.GuildAdd(t.Guild) - case *GuildUpdate: - err = s.GuildAdd(t.Guild) - case *GuildDelete: - err = s.GuildRemove(t.Guild) - case *GuildMemberAdd: - // Updates the MemberCount of the guild. - guild, err := s.Guild(t.Member.GuildID) - if err != nil { - return err - } - guild.MemberCount++ - - // Caches member if tracking is enabled. - if s.TrackMembers { - err = s.MemberAdd(t.Member) - } - case *GuildMemberUpdate: - if s.TrackMembers { - err = s.MemberAdd(t.Member) - } - case *GuildMemberRemove: - // Updates the MemberCount of the guild. - guild, err := s.Guild(t.Member.GuildID) - if err != nil { - return err - } - guild.MemberCount-- - - // Removes member from the cache if tracking is enabled. - if s.TrackMembers { - err = s.MemberRemove(t.Member) - } - case *GuildMembersChunk: - if s.TrackMembers { - for i := range t.Members { - t.Members[i].GuildID = t.GuildID - err = s.MemberAdd(t.Members[i]) - } - } - case *GuildRoleCreate: - if s.TrackRoles { - err = s.RoleAdd(t.GuildID, t.Role) - } - case *GuildRoleUpdate: - if s.TrackRoles { - err = s.RoleAdd(t.GuildID, t.Role) - } - case *GuildRoleDelete: - if s.TrackRoles { - err = s.RoleRemove(t.GuildID, t.RoleID) - } - case *GuildEmojisUpdate: - if s.TrackEmojis { - err = s.EmojisAdd(t.GuildID, t.Emojis) - } - case *ChannelCreate: - if s.TrackChannels { - err = s.ChannelAdd(t.Channel) - } - case *ChannelUpdate: - if s.TrackChannels { - err = s.ChannelAdd(t.Channel) - } - case *ChannelDelete: - if s.TrackChannels { - err = s.ChannelRemove(t.Channel) - } - case *MessageCreate: - if s.MaxMessageCount != 0 { - err = s.MessageAdd(t.Message) - } - case *MessageUpdate: - if s.MaxMessageCount != 0 { - var old *Message - old, err = s.Message(t.ChannelID, t.ID) - if err == nil { - oldCopy := *old - t.BeforeUpdate = &oldCopy - } - - err = s.MessageAdd(t.Message) - } - case *MessageDelete: - if s.MaxMessageCount != 0 { - err = s.MessageRemove(t.Message) - } - case *MessageDeleteBulk: - if s.MaxMessageCount != 0 { - for _, mID := range t.Messages { - s.messageRemoveByID(t.ChannelID, mID) - } - } - case *VoiceStateUpdate: - if s.TrackVoice { - err = s.voiceStateUpdate(t) - } - case *PresenceUpdate: - if s.TrackPresences { - s.PresenceAdd(t.GuildID, &t.Presence) - } - if s.TrackMembers { - if t.Status == StatusOffline { - return - } - - var m *Member - m, err = s.Member(t.GuildID, t.User.ID) - - if err != nil { - // Member not found; this is a user coming online - m = &Member{ - GuildID: t.GuildID, - Nick: t.Nick, - User: t.User, - Roles: t.Roles, - } - - } else { - - if t.Nick != "" { - m.Nick = t.Nick - } - - if t.User.Username != "" { - m.User.Username = t.User.Username - } - - // PresenceUpdates always contain a list of roles, so there's no need to check for an empty list here - m.Roles = t.Roles - - } - - err = s.MemberAdd(m) - } - - } - - return -} - -// UserChannelPermissions returns the permission of a user in a channel. -// userID : The ID of the user to calculate permissions for. -// channelID : The ID of the channel to calculate permission for. -func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { - if s == nil { - return 0, ErrNilState - } - - channel, err := s.Channel(channelID) - if err != nil { - return - } - - guild, err := s.Guild(channel.GuildID) - if err != nil { - return - } - - if userID == guild.OwnerID { - apermissions = PermissionAll - return - } - - member, err := s.Member(guild.ID, userID) - if err != nil { - return - } - - return memberPermissions(guild, channel, member), nil -} - -// UserColor returns the color of a user in a channel. -// While colors are defined at a Guild level, determining for a channel is more useful in message handlers. -// 0 is returned in cases of error, which is the color of @everyone. -// userID : The ID of the user to calculate the color for. -// channelID : The ID of the channel to calculate the color for. -func (s *State) UserColor(userID, channelID string) int { - if s == nil { - return 0 - } - - channel, err := s.Channel(channelID) - if err != nil { - return 0 - } - - guild, err := s.Guild(channel.GuildID) - if err != nil { - return 0 - } - - member, err := s.Member(guild.ID, userID) - if err != nil { - return 0 - } - - roles := Roles(guild.Roles) - sort.Sort(roles) - - for _, role := range roles { - for _, roleID := range member.Roles { - if role.ID == roleID { - if role.Color != 0 { - return role.Color - } - } - } - } - - return 0 -} diff --git a/vendor/github.com/bwmarrin/discordgo/structs.go b/vendor/github.com/bwmarrin/discordgo/structs.go deleted file mode 100644 index b19d3a67..00000000 --- a/vendor/github.com/bwmarrin/discordgo/structs.go +++ /dev/null @@ -1,1039 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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" - "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 - 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 - - // 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"` - ExpireBehavior int `json:"expire_behavior"` - ExpireGracePeriod int `json:"expire_grace_period"` - User *User `json:"user"` - Account IntegrationAccount `json:"account"` - SyncedAt Timestamp `json:"synced_at"` -} - -// 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"` - - // 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 = 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"` -} - -// 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 PermissionOverwrite holds permission overwrite data for a Channel -type PermissionOverwrite struct { - ID string `json:"id"` - Type string `json:"type"` - Deny int `json:"deny"` - Allow int `json:"allow"` -} - -// Emoji struct holds data related to Emoji's -type Emoji struct { - ID string `json:"id"` - Name string `json:"name"` - Roles []string `json:"roles"` - Managed bool `json:"managed"` - RequireColons bool `json:"require_colons"` - 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 "<a:" + e.APIName() + ">" - } - - 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 ID of the embed channel ID, used for embed widgets. - EmbedChannelID string `json:"embed_channel_id"` - - // The user ID of the owner of the guild. - OwnerID string `json:"owner_id"` - - // 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 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 has embedding enabled. - EmbedEnabled bool `json:"embed_enabled"` - - // 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. - // 0 == all messages, 1 == mentions only. - DefaultMessageNotifications int `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"` - - // 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"` - - // 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 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"` -} - -// 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 int `json:"permissions"` -} - -// 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"` -} - -// 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 int `json:"permissions"` -} - -// 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"` - Game *Game `json:"game"` - Nick string `json:"nick"` - Roles []string `json:"roles"` - Since *int `json:"since"` -} - -// GameType is the type of "game" (see GameType* consts) in the Game struct -type GameType int - -// Valid GameType values -const ( - GameTypeGame GameType = iota - GameTypeStreaming - GameTypeListening - GameTypeWatching -) - -// A Game struct holds the name of the "playing .." game for a user -type Game struct { - Name string `json:"name"` - Type GameType `json:"type"` - URL string `json:"url,omitempty"` - Details string `json:"details,omitempty"` - State string `json:"state,omitempty"` - TimeStamps TimeStamps `json:"timestamps,omitempty"` - Assets Assets `json:"assets,omitempty"` - ApplicationID string `json:"application_id,omitempty"` - Instance int8 `json:"instance,omitempty"` - // TODO: Party and Secrets (unknown structure) -} - -// 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"` -} - -// 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"` -} - -// 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. -type GuildAuditLog struct { - Webhooks []struct { - ChannelID string `json:"channel_id"` - GuildID string `json:"guild_id"` - ID string `json:"id"` - Avatar string `json:"avatar"` - Name string `json:"name"` - } `json:"webhooks,omitempty"` - Users []struct { - Username string `json:"username"` - Discriminator string `json:"discriminator"` - Bot bool `json:"bot"` - ID string `json:"id"` - Avatar string `json:"avatar"` - } `json:"users,omitempty"` - AuditLogEntries []struct { - TargetID string `json:"target_id"` - Changes []struct { - NewValue interface{} `json:"new_value"` - OldValue interface{} `json:"old_value"` - Key string `json:"key"` - } `json:"changes,omitempty"` - UserID string `json:"user_id"` - ID string `json:"id"` - ActionType int `json:"action_type"` - Options struct { - DeleteMembersDay string `json:"delete_member_days"` - MembersRemoved string `json:"members_removed"` - ChannelID string `json:"channel_id"` - Count string `json:"count"` - ID string `json:"id"` - Type string `json:"type"` - RoleName string `json:"role_name"` - } `json:"options,omitempty"` - Reason string `json:"reason"` - } `json:"audit_log_entries"` -} - -// Block contains Discord Audit Log Action Types -const ( - AuditLogActionGuildUpdate = 1 - - AuditLogActionChannelCreate = 10 - AuditLogActionChannelUpdate = 11 - AuditLogActionChannelDelete = 12 - AuditLogActionChannelOverwriteCreate = 13 - AuditLogActionChannelOverwriteUpdate = 14 - AuditLogActionChannelOverwriteDelete = 15 - - AuditLogActionMemberKick = 20 - AuditLogActionMemberPrune = 21 - AuditLogActionMemberBanAdd = 22 - AuditLogActionMemberBanRemove = 23 - AuditLogActionMemberUpdate = 24 - AuditLogActionMemberRoleUpdate = 25 - - AuditLogActionRoleCreate = 30 - AuditLogActionRoleUpdate = 31 - AuditLogActionRoleDelete = 32 - - AuditLogActionInviteCreate = 40 - AuditLogActionInviteUpdate = 41 - AuditLogActionInviteDelete = 42 - - AuditLogActionWebhookCreate = 50 - AuditLogActionWebhookUpdate = 51 - AuditLogActionWebhookDelete = 52 - - AuditLogActionEmojiCreate = 60 - AuditLogActionEmojiUpdate = 61 - AuditLogActionEmojiDelete = 62 - - AuditLogActionMessageDelete = 72 -) - -// 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"` - 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"` -} - -// 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"` -} - -// 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"` -} - -// Constants for the different bit offsets of text channel permissions -const ( - 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 - - PermissionAllText = PermissionReadMessages | - PermissionSendMessages | - PermissionSendTTSMessages | - PermissionManageMessages | - PermissionEmbedLinks | - PermissionAttachFiles | - PermissionReadMessageHistory | - PermissionMentionEveryone - PermissionAllVoice = 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 - - 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 -) diff --git a/vendor/github.com/bwmarrin/discordgo/types.go b/vendor/github.com/bwmarrin/discordgo/types.go deleted file mode 100644 index c0ce0131..00000000 --- a/vendor/github.com/bwmarrin/discordgo/types.go +++ /dev/null @@ -1,57 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 custom types, currently only a timestamp wrapper. - -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. -type RESTError struct { - Request *http.Request - Response *http.Response - ResponseBody []byte - - Message *APIErrorMessage // Message may be nil. -} - -func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTError { - restErr := &RESTError{ - Request: req, - Response: resp, - ResponseBody: body, - } - - // Attempt to decode the error and assume no message was provided if it fails - var msg *APIErrorMessage - err := json.Unmarshal(body, &msg) - if err == nil { - restErr.Message = msg - } - - return restErr -} - -func (r RESTError) Error() string { - return "HTTP " + r.Response.Status + ", " + string(r.ResponseBody) -} diff --git a/vendor/github.com/bwmarrin/discordgo/user.go b/vendor/github.com/bwmarrin/discordgo/user.go deleted file mode 100644 index a9af31a9..00000000 --- a/vendor/github.com/bwmarrin/discordgo/user.go +++ /dev/null @@ -1,69 +0,0 @@ -package discordgo - -import "strings" - -// A User stores all data for an individual Discord user. -type User struct { - // The ID of the user. - ID string `json:"id"` - - // The email of the user. This is only present when - // the application possesses the email scope for the user. - Email string `json:"email"` - - // The user's username. - Username string `json:"username"` - - // The hash of the user's avatar. Use Session.UserAvatar - // to retrieve the avatar itself. - Avatar string `json:"avatar"` - - // The user's chosen language option. - Locale string `json:"locale"` - - // The discriminator of the user (4 numbers after name). - Discriminator string `json:"discriminator"` - - // The token of the user. This is only present for - // the user represented by the current session. - Token string `json:"token"` - - // Whether the user's email is verified. - Verified bool `json:"verified"` - - // Whether the user has multi-factor authentication enabled. - MFAEnabled bool `json:"mfa_enabled"` - - // Whether the user is a bot. - Bot bool `json:"bot"` -} - -// String returns a unique identifier of the form username#discriminator -func (u *User) String() string { - return u.Username + "#" + u.Discriminator -} - -// Mention return a string which mentions the user -func (u *User) Mention() string { - return "<@" + u.ID + ">" -} - -// AvatarURL returns a URL to the user'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 (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 -} diff --git a/vendor/github.com/bwmarrin/discordgo/util.go b/vendor/github.com/bwmarrin/discordgo/util.go deleted file mode 100644 index 02443ca1..00000000 --- a/vendor/github.com/bwmarrin/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(timestamp/1000, 0) - return -} diff --git a/vendor/github.com/bwmarrin/discordgo/voice.go b/vendor/github.com/bwmarrin/discordgo/voice.go deleted file mode 100644 index 51ac16c8..00000000 --- a/vendor/github.com/bwmarrin/discordgo/voice.go +++ /dev/null @@ -1,887 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 code related to Discord voice suppport - -package discordgo - -import ( - "encoding/binary" - "encoding/json" - "fmt" - "net" - "strconv" - "strings" - "sync" - "time" - - "github.com/gorilla/websocket" - "golang.org/x/crypto/nacl/secretbox" -) - -// ------------------------------------------------------------------------------------------------ -// Code related to both VoiceConnection Websocket and UDP connections. -// ------------------------------------------------------------------------------------------------ - -// A VoiceConnection struct holds all the data and functions related to a Discord Voice Connection. -type VoiceConnection struct { - sync.RWMutex - - Debug bool // If true, print extra logging -- DEPRECATED - LogLevel int - Ready bool // If true, voice is ready to send/receive audio - UserID string - GuildID string - ChannelID string - deaf bool - mute bool - speaking bool - reconnecting bool // If true, voice connection is trying to reconnect - - OpusSend chan []byte // Chan for sending opus audio - OpusRecv chan *Packet // Chan for receiving opus audio - - wsConn *websocket.Conn - wsMutex sync.Mutex - udpConn *net.UDPConn - session *Session - - sessionID string - token string - endpoint string - - // Used to send a close signal to goroutines - close chan struct{} - - // Used to allow blocking until connected - connected chan bool - - // Used to pass the sessionid from onVoiceStateUpdate - // sessionRecv chan string UNUSED ATM - - op4 voiceOP4 - op2 voiceOP2 - - voiceSpeakingUpdateHandlers []VoiceSpeakingUpdateHandler -} - -// VoiceSpeakingUpdateHandler type provides a function definition for the -// VoiceSpeakingUpdate event -type VoiceSpeakingUpdateHandler func(vc *VoiceConnection, vs *VoiceSpeakingUpdate) - -// Speaking sends a speaking notification to Discord over the voice websocket. -// This must be sent as true prior to sending audio and should be set to false -// once finished sending audio. -// b : Send true if speaking, false if not. -func (v *VoiceConnection) Speaking(b bool) (err error) { - - v.log(LogDebug, "called (%t)", b) - - type voiceSpeakingData struct { - Speaking bool `json:"speaking"` - Delay int `json:"delay"` - } - - type voiceSpeakingOp struct { - Op int `json:"op"` // Always 5 - Data voiceSpeakingData `json:"d"` - } - - if v.wsConn == nil { - return fmt.Errorf("no VoiceConnection websocket") - } - - data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}} - v.wsMutex.Lock() - err = v.wsConn.WriteJSON(data) - v.wsMutex.Unlock() - - v.Lock() - defer v.Unlock() - if err != nil { - v.speaking = false - v.log(LogError, "Speaking() write json error, %s", err) - return - } - - v.speaking = b - - return -} - -// ChangeChannel sends Discord a request to change channels within a Guild -// !!! NOTE !!! This function may be removed in favour of just using ChannelVoiceJoin -func (v *VoiceConnection) ChangeChannel(channelID string, mute, deaf bool) (err error) { - - v.log(LogInformational, "called") - - data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, &channelID, mute, deaf}} - v.wsMutex.Lock() - err = v.session.wsConn.WriteJSON(data) - v.wsMutex.Unlock() - if err != nil { - return - } - v.ChannelID = channelID - v.deaf = deaf - v.mute = mute - v.speaking = false - - return -} - -// Disconnect disconnects from this voice channel and closes the websocket -// and udp connections to Discord. -func (v *VoiceConnection) Disconnect() (err error) { - - // Send a OP4 with a nil channel to disconnect - if v.sessionID != "" { - data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} - v.session.wsMutex.Lock() - err = v.session.wsConn.WriteJSON(data) - v.session.wsMutex.Unlock() - v.sessionID = "" - } - - // Close websocket and udp connections - v.Close() - - v.log(LogInformational, "Deleting VoiceConnection %s", v.GuildID) - - v.session.Lock() - delete(v.session.VoiceConnections, v.GuildID) - v.session.Unlock() - - return -} - -// Close closes the voice ws and udp connections -func (v *VoiceConnection) Close() { - - v.log(LogInformational, "called") - - v.Lock() - defer v.Unlock() - - v.Ready = false - v.speaking = false - - if v.close != nil { - v.log(LogInformational, "closing v.close") - close(v.close) - v.close = nil - } - - if v.udpConn != nil { - v.log(LogInformational, "closing udp") - err := v.udpConn.Close() - if err != nil { - v.log(LogError, "error closing udp connection, %s", err) - } - v.udpConn = nil - } - - if v.wsConn != nil { - v.log(LogInformational, "sending close frame") - - // To cleanly close a connection, a client should send a close - // frame and wait for the server to close the connection. - v.wsMutex.Lock() - err := v.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - v.wsMutex.Unlock() - if err != nil { - v.log(LogError, "error closing websocket, %s", err) - } - - // TODO: Wait for Discord to actually close the connection. - time.Sleep(1 * time.Second) - - v.log(LogInformational, "closing websocket") - err = v.wsConn.Close() - if err != nil { - v.log(LogError, "error closing websocket, %s", err) - } - - v.wsConn = nil - } -} - -// AddHandler adds a Handler for VoiceSpeakingUpdate events. -func (v *VoiceConnection) AddHandler(h VoiceSpeakingUpdateHandler) { - v.Lock() - defer v.Unlock() - - v.voiceSpeakingUpdateHandlers = append(v.voiceSpeakingUpdateHandlers, h) -} - -// VoiceSpeakingUpdate is a struct for a VoiceSpeakingUpdate event. -type VoiceSpeakingUpdate struct { - UserID string `json:"user_id"` - SSRC int `json:"ssrc"` - Speaking bool `json:"speaking"` -} - -// ------------------------------------------------------------------------------------------------ -// Unexported Internal Functions Below. -// ------------------------------------------------------------------------------------------------ - -// A voiceOP4 stores the data for the voice operation 4 websocket event -// which provides us with the NaCl SecretBox encryption key -type voiceOP4 struct { - SecretKey [32]byte `json:"secret_key"` - Mode string `json:"mode"` -} - -// A voiceOP2 stores the data for the voice operation 2 websocket event -// which is sort of like the voice READY packet -type voiceOP2 struct { - SSRC uint32 `json:"ssrc"` - Port int `json:"port"` - Modes []string `json:"modes"` - HeartbeatInterval time.Duration `json:"heartbeat_interval"` - IP string `json:"ip"` -} - -// WaitUntilConnected waits for the Voice Connection to -// become ready, if it does not become ready it returns an err -func (v *VoiceConnection) waitUntilConnected() error { - - v.log(LogInformational, "called") - - i := 0 - for { - v.RLock() - ready := v.Ready - v.RUnlock() - if ready { - return nil - } - - if i > 10 { - return fmt.Errorf("timeout waiting for voice") - } - - time.Sleep(1 * time.Second) - i++ - } -} - -// Open opens a voice connection. This should be called -// after VoiceChannelJoin is used and the data VOICE websocket events -// are captured. -func (v *VoiceConnection) open() (err error) { - - v.log(LogInformational, "called") - - v.Lock() - defer v.Unlock() - - // Don't open a websocket if one is already open - if v.wsConn != nil { - v.log(LogWarning, "refusing to overwrite non-nil websocket") - return - } - - // TODO temp? loop to wait for the SessionID - i := 0 - for { - if v.sessionID != "" { - break - } - if i > 20 { // only loop for up to 1 second total - return fmt.Errorf("did not receive voice Session ID in time") - } - time.Sleep(50 * time.Millisecond) - i++ - } - - // Connect to VoiceConnection Websocket - vg := "wss://" + strings.TrimSuffix(v.endpoint, ":80") - v.log(LogInformational, "connecting to voice endpoint %s", vg) - v.wsConn, _, err = websocket.DefaultDialer.Dial(vg, nil) - if err != nil { - v.log(LogWarning, "error connecting to voice endpoint %s, %s", vg, err) - v.log(LogDebug, "voice struct: %#v\n", v) - return - } - - type voiceHandshakeData struct { - ServerID string `json:"server_id"` - UserID string `json:"user_id"` - SessionID string `json:"session_id"` - Token string `json:"token"` - } - type voiceHandshakeOp struct { - Op int `json:"op"` // Always 0 - Data voiceHandshakeData `json:"d"` - } - data := voiceHandshakeOp{0, voiceHandshakeData{v.GuildID, v.UserID, v.sessionID, v.token}} - - err = v.wsConn.WriteJSON(data) - if err != nil { - v.log(LogWarning, "error sending init packet, %s", err) - return - } - - v.close = make(chan struct{}) - go v.wsListen(v.wsConn, v.close) - - // add loop/check for Ready bool here? - // then return false if not ready? - // but then wsListen will also err. - - return -} - -// wsListen listens on the voice websocket for messages and passes them -// to the voice event handler. This is automatically called by the Open func -func (v *VoiceConnection) wsListen(wsConn *websocket.Conn, close <-chan struct{}) { - - v.log(LogInformational, "called") - - for { - _, message, err := v.wsConn.ReadMessage() - if err != nil { - // Detect if we have been closed manually. If a Close() has already - // happened, the websocket we are listening on will be different to the - // current session. - v.RLock() - sameConnection := v.wsConn == wsConn - v.RUnlock() - if sameConnection { - - v.log(LogError, "voice endpoint %s websocket closed unexpectantly, %s", v.endpoint, err) - - // Start reconnect goroutine then exit. - go v.reconnect() - } - return - } - - // Pass received message to voice event handler - select { - case <-close: - return - default: - go v.onEvent(message) - } - } -} - -// wsEvent handles any voice websocket events. This is only called by the -// wsListen() function. -func (v *VoiceConnection) onEvent(message []byte) { - - v.log(LogDebug, "received: %s", string(message)) - - var e Event - if err := json.Unmarshal(message, &e); err != nil { - v.log(LogError, "unmarshall error, %s", err) - return - } - - switch e.Operation { - - case 2: // READY - - if err := json.Unmarshal(e.RawData, &v.op2); err != nil { - v.log(LogError, "OP2 unmarshall error, %s, %s", err, string(e.RawData)) - return - } - - // Start the voice websocket heartbeat to keep the connection alive - go v.wsHeartbeat(v.wsConn, v.close, v.op2.HeartbeatInterval) - // TODO monitor a chan/bool to verify this was successful - - // Start the UDP connection - err := v.udpOpen() - if err != nil { - v.log(LogError, "error opening udp connection, %s", err) - return - } - - // Start the opusSender. - // TODO: Should we allow 48000/960 values to be user defined? - if v.OpusSend == nil { - v.OpusSend = make(chan []byte, 2) - } - go v.opusSender(v.udpConn, v.close, v.OpusSend, 48000, 960) - - // Start the opusReceiver - if !v.deaf { - if v.OpusRecv == nil { - v.OpusRecv = make(chan *Packet, 2) - } - - go v.opusReceiver(v.udpConn, v.close, v.OpusRecv) - } - - return - - case 3: // HEARTBEAT response - // add code to use this to track latency? - return - - case 4: // udp encryption secret key - v.Lock() - defer v.Unlock() - - v.op4 = voiceOP4{} - if err := json.Unmarshal(e.RawData, &v.op4); err != nil { - v.log(LogError, "OP4 unmarshall error, %s, %s", err, string(e.RawData)) - return - } - return - - case 5: - if len(v.voiceSpeakingUpdateHandlers) == 0 { - return - } - - voiceSpeakingUpdate := &VoiceSpeakingUpdate{} - if err := json.Unmarshal(e.RawData, voiceSpeakingUpdate); err != nil { - v.log(LogError, "OP5 unmarshall error, %s, %s", err, string(e.RawData)) - return - } - - for _, h := range v.voiceSpeakingUpdateHandlers { - h(v, voiceSpeakingUpdate) - } - - default: - v.log(LogDebug, "unknown voice operation, %d, %s", e.Operation, string(e.RawData)) - } - - return -} - -type voiceHeartbeatOp struct { - Op int `json:"op"` // Always 3 - Data int `json:"d"` -} - -// NOTE :: When a guild voice server changes how do we shut this down -// properly, so a new connection can be setup without fuss? -// -// wsHeartbeat sends regular heartbeats to voice Discord so it knows the client -// is still connected. If you do not send these heartbeats Discord will -// disconnect the websocket connection after a few seconds. -func (v *VoiceConnection) wsHeartbeat(wsConn *websocket.Conn, close <-chan struct{}, i time.Duration) { - - if close == nil || wsConn == nil { - return - } - - var err error - ticker := time.NewTicker(i * time.Millisecond) - defer ticker.Stop() - for { - v.log(LogDebug, "sending heartbeat packet") - v.wsMutex.Lock() - err = wsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())}) - v.wsMutex.Unlock() - if err != nil { - v.log(LogError, "error sending heartbeat to voice endpoint %s, %s", v.endpoint, err) - return - } - - select { - case <-ticker.C: - // continue loop and send heartbeat - case <-close: - return - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Code related to the VoiceConnection UDP connection -// ------------------------------------------------------------------------------------------------ - -type voiceUDPData struct { - Address string `json:"address"` // Public IP of machine running this code - Port uint16 `json:"port"` // UDP Port of machine running this code - Mode string `json:"mode"` // always "xsalsa20_poly1305" -} - -type voiceUDPD struct { - Protocol string `json:"protocol"` // Always "udp" ? - Data voiceUDPData `json:"data"` -} - -type voiceUDPOp struct { - Op int `json:"op"` // Always 1 - Data voiceUDPD `json:"d"` -} - -// udpOpen opens a UDP connection to the voice server and completes the -// initial required handshake. This connection is left open in the session -// and can be used to send or receive audio. This should only be called -// from voice.wsEvent OP2 -func (v *VoiceConnection) udpOpen() (err error) { - - v.Lock() - defer v.Unlock() - - if v.wsConn == nil { - return fmt.Errorf("nil voice websocket") - } - - if v.udpConn != nil { - return fmt.Errorf("udp connection already open") - } - - if v.close == nil { - return fmt.Errorf("nil close channel") - } - - if v.endpoint == "" { - return fmt.Errorf("empty endpoint") - } - - host := v.op2.IP + ":" + strconv.Itoa(v.op2.Port) - addr, err := net.ResolveUDPAddr("udp", host) - if err != nil { - v.log(LogWarning, "error resolving udp host %s, %s", host, err) - return - } - - v.log(LogInformational, "connecting to udp addr %s", addr.String()) - v.udpConn, err = net.DialUDP("udp", nil, addr) - if err != nil { - v.log(LogWarning, "error connecting to udp addr %s, %s", addr.String(), err) - return - } - - // Create a 70 byte array and put the SSRC code from the Op 2 VoiceConnection event - // into it. Then send that over the UDP connection to Discord - sb := make([]byte, 70) - binary.BigEndian.PutUint32(sb, v.op2.SSRC) - _, err = v.udpConn.Write(sb) - if err != nil { - v.log(LogWarning, "udp write error to %s, %s", addr.String(), err) - return - } - - // Create a 70 byte array and listen for the initial handshake response - // from Discord. Once we get it parse the IP and PORT information out - // of the response. This should be our public IP and PORT as Discord - // saw us. - rb := make([]byte, 70) - rlen, _, err := v.udpConn.ReadFromUDP(rb) - if err != nil { - v.log(LogWarning, "udp read error, %s, %s", addr.String(), err) - return - } - - if rlen < 70 { - v.log(LogWarning, "received udp packet too small") - return fmt.Errorf("received udp packet too small") - } - - // Loop over position 4 through 20 to grab the IP address - // Should never be beyond position 20. - var ip string - for i := 4; i < 20; i++ { - if rb[i] == 0 { - break - } - ip += string(rb[i]) - } - - // Grab port from position 68 and 69 - port := binary.BigEndian.Uint16(rb[68:70]) - - // Take the data from above and send it back to Discord to finalize - // the UDP connection handshake. - data := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "xsalsa20_poly1305"}}} - - v.wsMutex.Lock() - err = v.wsConn.WriteJSON(data) - v.wsMutex.Unlock() - if err != nil { - v.log(LogWarning, "udp write error, %#v, %s", data, err) - return - } - - // start udpKeepAlive - go v.udpKeepAlive(v.udpConn, v.close, 5*time.Second) - // TODO: find a way to check that it fired off okay - - return -} - -// udpKeepAlive sends a udp packet to keep the udp connection open -// This is still a bit of a "proof of concept" -func (v *VoiceConnection) udpKeepAlive(udpConn *net.UDPConn, close <-chan struct{}, i time.Duration) { - - if udpConn == nil || close == nil { - return - } - - var err error - var sequence uint64 - - packet := make([]byte, 8) - - ticker := time.NewTicker(i) - defer ticker.Stop() - for { - - binary.LittleEndian.PutUint64(packet, sequence) - sequence++ - - _, err = udpConn.Write(packet) - if err != nil { - v.log(LogError, "write error, %s", err) - return - } - - select { - case <-ticker.C: - // continue loop and send keepalive - case <-close: - return - } - } -} - -// opusSender will listen on the given channel and send any -// pre-encoded opus audio to Discord. Supposedly. -func (v *VoiceConnection) opusSender(udpConn *net.UDPConn, close <-chan struct{}, opus <-chan []byte, rate, size int) { - - if udpConn == nil || close == nil { - return - } - - // VoiceConnection is now ready to receive audio packets - // TODO: this needs reviewed as I think there must be a better way. - v.Lock() - v.Ready = true - v.Unlock() - defer func() { - v.Lock() - v.Ready = false - v.Unlock() - }() - - var sequence uint16 - var timestamp uint32 - var recvbuf []byte - var ok bool - udpHeader := make([]byte, 12) - var nonce [24]byte - - // build the parts that don't change in the udpHeader - udpHeader[0] = 0x80 - udpHeader[1] = 0x78 - binary.BigEndian.PutUint32(udpHeader[8:], v.op2.SSRC) - - // start a send loop that loops until buf chan is closed - ticker := time.NewTicker(time.Millisecond * time.Duration(size/(rate/1000))) - defer ticker.Stop() - for { - - // Get data from chan. If chan is closed, return. - select { - case <-close: - return - case recvbuf, ok = <-opus: - if !ok { - return - } - // else, continue loop - } - - v.RLock() - speaking := v.speaking - v.RUnlock() - if !speaking { - err := v.Speaking(true) - if err != nil { - v.log(LogError, "error sending speaking packet, %s", err) - } - } - - // Add sequence and timestamp to udpPacket - binary.BigEndian.PutUint16(udpHeader[2:], sequence) - binary.BigEndian.PutUint32(udpHeader[4:], timestamp) - - // encrypt the opus data - copy(nonce[:], udpHeader) - v.RLock() - sendbuf := secretbox.Seal(udpHeader, recvbuf, &nonce, &v.op4.SecretKey) - v.RUnlock() - - // block here until we're exactly at the right time :) - // Then send rtp audio packet to Discord over UDP - select { - case <-close: - return - case <-ticker.C: - // continue - } - _, err := udpConn.Write(sendbuf) - - if err != nil { - v.log(LogError, "udp write error, %s", err) - v.log(LogDebug, "voice struct: %#v\n", v) - return - } - - if (sequence) == 0xFFFF { - sequence = 0 - } else { - sequence++ - } - - if (timestamp + uint32(size)) >= 0xFFFFFFFF { - timestamp = 0 - } else { - timestamp += uint32(size) - } - } -} - -// A Packet contains the headers and content of a received voice packet. -type Packet struct { - SSRC uint32 - Sequence uint16 - Timestamp uint32 - Type []byte - Opus []byte - PCM []int16 -} - -// opusReceiver listens on the UDP socket for incoming packets -// and sends them across the given channel -// NOTE :: This function may change names later. -func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct{}, c chan *Packet) { - - if udpConn == nil || close == nil { - return - } - - recvbuf := make([]byte, 1024) - var nonce [24]byte - - for { - rlen, err := udpConn.Read(recvbuf) - if err != nil { - // Detect if we have been closed manually. If a Close() has already - // happened, the udp connection we are listening on will be different - // to the current session. - v.RLock() - sameConnection := v.udpConn == udpConn - v.RUnlock() - if sameConnection { - - v.log(LogError, "udp read error, %s, %s", v.endpoint, err) - v.log(LogDebug, "voice struct: %#v\n", v) - - go v.reconnect() - } - return - } - - select { - case <-close: - return - default: - // continue loop - } - - // For now, skip anything except audio. - if rlen < 12 || (recvbuf[0] != 0x80 && recvbuf[0] != 0x90) { - continue - } - - // build a audio packet struct - p := Packet{} - p.Type = recvbuf[0:2] - p.Sequence = binary.BigEndian.Uint16(recvbuf[2:4]) - p.Timestamp = binary.BigEndian.Uint32(recvbuf[4:8]) - p.SSRC = binary.BigEndian.Uint32(recvbuf[8:12]) - // decrypt opus data - 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:] - } - - if c != nil { - select { - case c <- &p: - case <-close: - return - } - } - } -} - -// Reconnect will close down a voice connection then immediately try to -// reconnect to that session. -// NOTE : This func is messy and a WIP while I find what works. -// It will be cleaned up once a proven stable option is flushed out. -// aka: this is ugly shit code, please don't judge too harshly. -func (v *VoiceConnection) reconnect() { - - v.log(LogInformational, "called") - - v.Lock() - if v.reconnecting { - v.log(LogInformational, "already reconnecting to channel %s, exiting", v.ChannelID) - v.Unlock() - return - } - v.reconnecting = true - v.Unlock() - - defer func() { v.reconnecting = false }() - - // Close any currently open connections - v.Close() - - wait := time.Duration(1) - for { - - <-time.After(wait * time.Second) - wait *= 2 - if wait > 600 { - wait = 600 - } - - if v.session.DataReady == false || v.session.wsConn == nil { - v.log(LogInformational, "cannot reconnect to channel %s with unready session", v.ChannelID) - continue - } - - v.log(LogInformational, "trying to reconnect to channel %s", v.ChannelID) - - _, err := v.session.ChannelVoiceJoin(v.GuildID, v.ChannelID, v.mute, v.deaf) - if err == nil { - v.log(LogInformational, "successfully reconnected to channel %s", v.ChannelID) - return - } - - v.log(LogInformational, "error reconnecting to channel %s, %s", v.ChannelID, err) - - // if the reconnect above didn't work lets just send a disconnect - // packet to reset things. - // Send a OP4 with a nil channel to disconnect - data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} - v.session.wsMutex.Lock() - err = v.session.wsConn.WriteJSON(data) - v.session.wsMutex.Unlock() - if err != nil { - v.log(LogError, "error sending disconnect packet, %s", err) - } - - } -} diff --git a/vendor/github.com/bwmarrin/discordgo/wsapi.go b/vendor/github.com/bwmarrin/discordgo/wsapi.go deleted file mode 100644 index eccbb9f4..00000000 --- a/vendor/github.com/bwmarrin/discordgo/wsapi.go +++ /dev/null @@ -1,884 +0,0 @@ -// Discordgo - Discord bindings for Go -// Available at https://github.com/bwmarrin/discordgo - -// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. 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 low level functions for interacting with the Discord -// data websocket interface. - -package discordgo - -import ( - "bytes" - "compress/zlib" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "runtime" - "sync/atomic" - "time" - - "github.com/gorilla/websocket" -) - -// ErrWSAlreadyOpen is thrown when you attempt to open -// a websocket that already is open. -var ErrWSAlreadyOpen = errors.New("web socket already opened") - -// ErrWSNotFound is thrown when you attempt to use a websocket -// that doesn't exist -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 -var ErrWSShardBounds = errors.New("ShardID must be less than ShardCount") - -type resumePacket struct { - Op int `json:"op"` - Data struct { - Token string `json:"token"` - SessionID string `json:"session_id"` - Sequence int64 `json:"seq"` - } `json:"d"` -} - -// Open creates a websocket connection to Discord. -// See: https://discordapp.com/developers/docs/topics/gateway#connecting -func (s *Session) Open() error { - s.log(LogInformational, "called") - - var err error - - // Prevent Open or other major Session functions from - // being called while Open is still running. - s.Lock() - defer s.Unlock() - - // If the websock is already open, bail out here. - if s.wsConn != nil { - return ErrWSAlreadyOpen - } - - // Get the gateway to use for the Websocket connection - if s.gateway == "" { - s.gateway, err = s.Gateway() - if err != nil { - return err - } - - // Add the version and encoding to the URL - s.gateway = s.gateway + "?v=" + APIVersion + "&encoding=json" - } - - // Connect to the Gateway - s.log(LogInformational, "connecting to gateway %s", s.gateway) - header := http.Header{} - header.Add("accept-encoding", "zlib") - s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header) - if err != nil { - s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err) - s.gateway = "" // clear cached gateway - s.wsConn = nil // Just to be safe. - return err - } - - s.wsConn.SetCloseHandler(func(code int, text string) error { - return nil - }) - - defer func() { - // because of this, all code below must set err to the error - // when exiting with an error :) Maybe someone has a better - // way :) - if err != nil { - s.wsConn.Close() - s.wsConn = nil - } - }() - - // The first response from Discord should be an Op 10 (Hello) Packet. - // When processed by onEvent the heartbeat goroutine will be started. - mt, m, err := s.wsConn.ReadMessage() - if err != nil { - return err - } - e, err := s.onEvent(mt, m) - if err != nil { - return err - } - if e.Operation != 10 { - err = fmt.Errorf("expecting Op 10, got Op %d instead", e.Operation) - return err - } - s.log(LogInformational, "Op 10 Hello Packet received from Discord") - s.LastHeartbeatAck = time.Now().UTC() - var h helloOp - if err = json.Unmarshal(e.RawData, &h); err != nil { - err = fmt.Errorf("error unmarshalling helloOp, %s", err) - return err - } - - // Now we send either an Op 2 Identity if this is a brand new - // connection or Op 6 Resume if we are resuming an existing connection. - sequence := atomic.LoadInt64(s.sequence) - if s.sessionID == "" && sequence == 0 { - - // Send Op 2 Identity Packet - err = s.identify() - if err != nil { - err = fmt.Errorf("error sending identify packet to gateway, %s, %s", s.gateway, err) - return err - } - - } else { - - // Send Op 6 Resume Packet - p := resumePacket{} - p.Op = 6 - p.Data.Token = s.Token - p.Data.SessionID = s.sessionID - p.Data.Sequence = sequence - - s.log(LogInformational, "sending resume packet to gateway") - s.wsMutex.Lock() - err = s.wsConn.WriteJSON(p) - s.wsMutex.Unlock() - if err != nil { - err = fmt.Errorf("error sending gateway resume packet, %s, %s", s.gateway, err) - return err - } - - } - - // A basic state is a hard requirement for Voice. - // We create it here so the below READY/RESUMED packet can populate - // the state :) - // XXX: Move to New() func? - if s.State == nil { - state := NewState() - state.TrackChannels = false - state.TrackEmojis = false - state.TrackMembers = false - state.TrackRoles = false - state.TrackVoice = false - s.State = state - } - - // Now Discord should send us a READY or RESUMED packet. - mt, m, err = s.wsConn.ReadMessage() - if err != nil { - return err - } - e, err = s.onEvent(mt, m) - if err != nil { - return err - } - if e.Type != `READY` && e.Type != `RESUMED` { - // This is not fatal, but it does not follow their API documentation. - s.log(LogWarning, "Expected READY/RESUMED, instead got:\n%#v\n", e) - } - s.log(LogInformational, "First Packet:\n%#v\n", e) - - s.log(LogInformational, "We are now connected to Discord, emitting connect event") - s.handleEvent(connectEventType, &Connect{}) - - // A VoiceConnections map is a hard requirement for Voice. - // XXX: can this be moved to when opening a voice connection? - if s.VoiceConnections == nil { - s.log(LogInformational, "creating new VoiceConnections map") - s.VoiceConnections = make(map[string]*VoiceConnection) - } - - // Create listening chan outside of listen, as it needs to happen inside the - // mutex lock and needs to exist before calling heartbeat and listen - // go rountines. - s.listening = make(chan interface{}) - - // Start sending heartbeats and reading messages from Discord. - go s.heartbeat(s.wsConn, s.listening, h.HeartbeatInterval) - go s.listen(s.wsConn, s.listening) - - s.log(LogInformational, "exiting") - return nil -} - -// listen polls the websocket connection for events, it will stop when the -// listening channel is closed, or an error occurs. -func (s *Session) listen(wsConn *websocket.Conn, listening <-chan interface{}) { - - s.log(LogInformational, "called") - - for { - - messageType, message, err := wsConn.ReadMessage() - - if err != nil { - - // Detect if we have been closed manually. If a Close() has already - // happened, the websocket we are listening on will be different to - // the current session. - s.RLock() - sameConnection := s.wsConn == wsConn - s.RUnlock() - - if sameConnection { - - s.log(LogWarning, "error reading from gateway %s websocket, %s", s.gateway, err) - // There has been an error reading, close the websocket so that - // OnDisconnect event is emitted. - err := s.Close() - if err != nil { - s.log(LogWarning, "error closing session connection, %s", err) - } - - s.log(LogInformational, "calling reconnect() now") - s.reconnect() - } - - return - } - - select { - - case <-listening: - return - - default: - s.onEvent(messageType, message) - - } - } -} - -type heartbeatOp struct { - Op int `json:"op"` - Data int64 `json:"d"` -} - -type helloOp struct { - HeartbeatInterval time.Duration `json:"heartbeat_interval"` -} - -// FailedHeartbeatAcks is the Number of heartbeat intervals to wait until forcing a connection restart. -const FailedHeartbeatAcks time.Duration = 5 * time.Millisecond - -// HeartbeatLatency returns the latency between heartbeat acknowledgement and heartbeat send. -func (s *Session) HeartbeatLatency() time.Duration { - - return s.LastHeartbeatAck.Sub(s.LastHeartbeatSent) - -} - -// heartbeat sends regular heartbeats to Discord so it knows the client -// is still connected. If you do not send these heartbeats Discord will -// disconnect the websocket connection after a few seconds. -func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}, heartbeatIntervalMsec time.Duration) { - - s.log(LogInformational, "called") - - if listening == nil || wsConn == nil { - return - } - - var err error - ticker := time.NewTicker(heartbeatIntervalMsec * time.Millisecond) - defer ticker.Stop() - - for { - s.RLock() - last := s.LastHeartbeatAck - s.RUnlock() - sequence := atomic.LoadInt64(s.sequence) - s.log(LogDebug, "sending gateway websocket heartbeat seq %d", sequence) - s.wsMutex.Lock() - s.LastHeartbeatSent = time.Now().UTC() - err = wsConn.WriteJSON(heartbeatOp{1, sequence}) - s.wsMutex.Unlock() - if err != nil || time.Now().UTC().Sub(last) > (heartbeatIntervalMsec*FailedHeartbeatAcks) { - if err != nil { - s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err) - } else { - s.log(LogError, "haven't gotten a heartbeat ACK in %v, triggering a reconnection", time.Now().UTC().Sub(last)) - } - s.Close() - s.reconnect() - return - } - s.Lock() - s.DataReady = true - s.Unlock() - - select { - case <-ticker.C: - // continue loop and send heartbeat - case <-listening: - return - } - } -} - -// UpdateStatusData ia provided to UpdateStatusComplex() -type UpdateStatusData struct { - IdleSince *int `json:"since"` - Game *Game `json:"game"` - AFK bool `json:"afk"` - Status string `json:"status"` -} - -type updateStatusOp struct { - Op int `json:"op"` - Data UpdateStatusData `json:"d"` -} - -func newUpdateStatusData(idle int, gameType GameType, game, url string) *UpdateStatusData { - usd := &UpdateStatusData{ - Status: "online", - } - - if idle > 0 { - usd.IdleSince = &idle - } - - if game != "" { - usd.Game = &Game{ - Name: game, - Type: gameType, - URL: url, - } - } - - return usd -} - -// UpdateStatus is used to update the user's status. -// If idle>0 then set status to idle. -// If game!="" then set game. -// if otherwise, set status to active, and no game. -func (s *Session) UpdateStatus(idle int, game string) (err error) { - return s.UpdateStatusComplex(*newUpdateStatusData(idle, GameTypeGame, game, "")) -} - -// UpdateStreamingStatus is used to update the user's streaming status. -// If idle>0 then set status to idle. -// If game!="" then set game. -// If game!="" and url!="" then set the status type to streaming with the URL set. -// if otherwise, set status to active, and no game. -func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err error) { - gameType := GameTypeGame - if url != "" { - gameType = GameTypeStreaming - } - return s.UpdateStatusComplex(*newUpdateStatusData(idle, gameType, game, url)) -} - -// UpdateListeningStatus is used to set the user to "Listening to..." -// If game!="" then set to what user is listening to -// Else, set user to active and no game. -func (s *Session) UpdateListeningStatus(game string) (err error) { - return s.UpdateStatusComplex(*newUpdateStatusData(0, GameTypeListening, game, "")) -} - -// UpdateStatusComplex allows for sending the raw status update data untouched by discordgo. -func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) { - - s.RLock() - defer s.RUnlock() - if s.wsConn == nil { - return ErrWSNotFound - } - - s.wsMutex.Lock() - err = s.wsConn.WriteJSON(updateStatusOp{3, usd}) - s.wsMutex.Unlock() - - return -} - -type requestGuildMembersData struct { - GuildID string `json:"guild_id"` - Query string `json:"query"` - Limit int `json:"limit"` -} - -type requestGuildMembersOp struct { - Op int `json:"op"` - Data requestGuildMembersData `json:"d"` -} - -// RequestGuildMembers requests guild members from the gateway -// The gateway responds with GuildMembersChunk events -// guildID : The ID of the guild to request members of -// query : String that username starts with, leave empty to return all members -// limit : Max number of items to return, or 0 to request all members matched -func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err error) { - s.log(LogInformational, "called") - - s.RLock() - defer s.RUnlock() - if s.wsConn == nil { - return ErrWSNotFound - } - - data := requestGuildMembersData{ - GuildID: guildID, - Query: query, - Limit: limit, - } - - s.wsMutex.Lock() - err = s.wsConn.WriteJSON(requestGuildMembersOp{8, data}) - s.wsMutex.Unlock() - - return -} - -// onEvent is the "event handler" for all messages received on the -// Discord Gateway API websocket connection. -// -// If you use the AddHandler() function to register a handler for a -// specific event this function will pass the event along to that handler. -// -// If you use the AddHandler() function to register a handler for the -// "OnEvent" event then all events will be passed to that handler. -func (s *Session) onEvent(messageType int, message []byte) (*Event, error) { - - var err error - var reader io.Reader - reader = bytes.NewBuffer(message) - - // If this is a compressed message, uncompress it. - if messageType == websocket.BinaryMessage { - - z, err2 := zlib.NewReader(reader) - if err2 != nil { - s.log(LogError, "error uncompressing websocket message, %s", err) - return nil, err2 - } - - defer func() { - err3 := z.Close() - if err3 != nil { - s.log(LogWarning, "error closing zlib, %s", err) - } - }() - - reader = z - } - - // Decode the event into an Event struct. - var e *Event - decoder := json.NewDecoder(reader) - if err = decoder.Decode(&e); err != nil { - s.log(LogError, "error decoding websocket message, %s", err) - return e, err - } - - s.log(LogDebug, "Op: %d, Seq: %d, Type: %s, Data: %s\n\n", e.Operation, e.Sequence, e.Type, string(e.RawData)) - - // Ping request. - // Must respond with a heartbeat packet within 5 seconds - if e.Operation == 1 { - s.log(LogInformational, "sending heartbeat in response to Op1") - s.wsMutex.Lock() - err = s.wsConn.WriteJSON(heartbeatOp{1, atomic.LoadInt64(s.sequence)}) - s.wsMutex.Unlock() - if err != nil { - s.log(LogError, "error sending heartbeat in response to Op1") - return e, err - } - - return e, nil - } - - // Reconnect - // Must immediately disconnect from gateway and reconnect to new gateway. - if e.Operation == 7 { - s.log(LogInformational, "Closing and reconnecting in response to Op7") - s.Close() - s.reconnect() - return e, nil - } - - // Invalid Session - // Must respond with a Identify packet. - if e.Operation == 9 { - - s.log(LogInformational, "sending identify packet to gateway in response to Op9") - - err = s.identify() - if err != nil { - s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err) - return e, err - } - - return e, nil - } - - if e.Operation == 10 { - // Op10 is handled by Open() - return e, nil - } - - if e.Operation == 11 { - s.Lock() - s.LastHeartbeatAck = time.Now().UTC() - s.Unlock() - s.log(LogDebug, "got heartbeat ACK") - return e, nil - } - - // Do not try to Dispatch a non-Dispatch Message - if e.Operation != 0 { - // But we probably should be doing something with them. - // TEMP - s.log(LogWarning, "unknown Op: %d, Seq: %d, Type: %s, Data: %s, message: %s", e.Operation, e.Sequence, e.Type, string(e.RawData), string(message)) - return e, nil - } - - // Store the message sequence - atomic.StoreInt64(s.sequence, e.Sequence) - - // Map event to registered event handlers and pass it along to any registered handlers. - if eh, ok := registeredInterfaceProviders[e.Type]; ok { - e.Struct = eh.New() - - // Attempt to unmarshal our event. - if err = json.Unmarshal(e.RawData, e.Struct); err != nil { - s.log(LogError, "error unmarshalling %s event, %s", e.Type, err) - } - - // Send event to any registered event handlers for it's type. - // Because the above doesn't cancel this, in case of an error - // the struct could be partially populated or at default values. - // However, most errors are due to a single field and I feel - // it's better to pass along what we received than nothing at all. - // TODO: Think about that decision :) - // Either way, READY events must fire, even with errors. - s.handleEvent(e.Type, e.Struct) - } else { - s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData)) - } - - // For legacy reasons, we send the raw event also, this could be useful for handling unknown events. - s.handleEvent(eventEventType, e) - - return e, nil -} - -// ------------------------------------------------------------------------------------------------ -// Code related to voice connections that initiate over the data websocket -// ------------------------------------------------------------------------------------------------ - -type voiceChannelJoinData struct { - GuildID *string `json:"guild_id"` - ChannelID *string `json:"channel_id"` - SelfMute bool `json:"self_mute"` - SelfDeaf bool `json:"self_deaf"` -} - -type voiceChannelJoinOp struct { - Op int `json:"op"` - Data voiceChannelJoinData `json:"d"` -} - -// ChannelVoiceJoin joins the session user to a voice channel. -// -// gID : Guild ID of the channel to join. -// cID : Channel ID of the channel to join. -// mute : If true, you will be set to muted upon joining. -// deaf : If true, you will be set to deafened upon joining. -func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *VoiceConnection, err error) { - - s.log(LogInformational, "called") - - s.RLock() - voice, _ = s.VoiceConnections[gID] - s.RUnlock() - - if voice == nil { - voice = &VoiceConnection{} - s.Lock() - s.VoiceConnections[gID] = voice - s.Unlock() - } - - voice.Lock() - voice.GuildID = gID - voice.ChannelID = cID - voice.deaf = deaf - voice.mute = mute - voice.session = s - voice.Unlock() - - err = s.ChannelVoiceJoinManual(gID, cID, mute, deaf) - if err != nil { - return - } - - // doesn't exactly work perfect yet.. TODO - err = voice.waitUntilConnected() - if err != nil { - s.log(LogWarning, "error waiting for voice to connect, %s", err) - voice.Close() - return - } - - return -} - -// ChannelVoiceJoinManual initiates a voice session to a voice channel, but does not complete it. -// -// This should only be used when the VoiceServerUpdate will be intercepted and used elsewhere. -// -// gID : Guild ID of the channel to join. -// cID : Channel ID of the channel to join, leave empty to disconnect. -// mute : If true, you will be set to muted upon joining. -// deaf : If true, you will be set to deafened upon joining. -func (s *Session) ChannelVoiceJoinManual(gID, cID string, mute, deaf bool) (err error) { - - s.log(LogInformational, "called") - - var channelID *string - if cID == "" { - channelID = nil - } else { - channelID = &cID - } - - // Send the request to Discord that we want to join the voice channel - data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, channelID, mute, deaf}} - s.wsMutex.Lock() - err = s.wsConn.WriteJSON(data) - s.wsMutex.Unlock() - return -} - -// onVoiceStateUpdate handles Voice State Update events on the data websocket. -func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) { - - // If we don't have a connection for the channel, don't bother - if st.ChannelID == "" { - return - } - - // Check if we have a voice connection to update - s.RLock() - voice, exists := s.VoiceConnections[st.GuildID] - s.RUnlock() - if !exists { - return - } - - // We only care about events that are about us. - if s.State.User.ID != st.UserID { - return - } - - // Store the SessionID for later use. - voice.Lock() - voice.UserID = st.UserID - voice.sessionID = st.SessionID - voice.ChannelID = st.ChannelID - voice.Unlock() -} - -// onVoiceServerUpdate handles the Voice Server Update data websocket event. -// -// This is also fired if the Guild's voice region changes while connected -// to a voice channel. In that case, need to re-establish connection to -// the new region endpoint. -func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) { - - s.log(LogInformational, "called") - - s.RLock() - voice, exists := s.VoiceConnections[st.GuildID] - s.RUnlock() - - // If no VoiceConnection exists, just skip this - if !exists { - return - } - - // If currently connected to voice ws/udp, then disconnect. - // Has no effect if not connected. - voice.Close() - - // Store values for later use - voice.Lock() - voice.token = st.Token - voice.endpoint = st.Endpoint - voice.GuildID = st.GuildID - voice.Unlock() - - // Open a connection to the voice server - err := voice.open() - if err != nil { - s.log(LogError, "onVoiceServerUpdate voice.open, %s", err) - } -} - -type identifyProperties struct { - OS string `json:"$os"` - Browser string `json:"$browser"` - Device string `json:"$device"` - Referer string `json:"$referer"` - ReferringDomain string `json:"$referring_domain"` -} - -type identifyData struct { - Token string `json:"token"` - Properties identifyProperties `json:"properties"` - LargeThreshold int `json:"large_threshold"` - Compress bool `json:"compress"` - Shard *[2]int `json:"shard,omitempty"` -} - -type identifyOp struct { - Op int `json:"op"` - Data identifyData `json:"d"` -} - -// identify sends the identify packet to the gateway -func (s *Session) identify() error { - - properties := identifyProperties{runtime.GOOS, - "Discordgo v" + VERSION, - "", - "", - "", - } - - data := identifyData{s.Token, - properties, - 250, - s.Compress, - nil, - } - - if s.ShardCount > 1 { - - if s.ShardID >= s.ShardCount { - return ErrWSShardBounds - } - - data.Shard = &[2]int{s.ShardID, s.ShardCount} - } - - op := identifyOp{2, data} - - s.wsMutex.Lock() - err := s.wsConn.WriteJSON(op) - s.wsMutex.Unlock() - - return err -} - -func (s *Session) reconnect() { - - s.log(LogInformational, "called") - - var err error - - if s.ShouldReconnectOnError { - - wait := time.Duration(1) - - for { - s.log(LogInformational, "trying to reconnect to gateway") - - err = s.Open() - if err == nil { - s.log(LogInformational, "successfully reconnected to gateway") - - // I'm not sure if this is actually needed. - // if the gw reconnect works properly, voice should stay alive - // However, there seems to be cases where something "weird" - // happens. So we're doing this for now just to improve - // stability in those edge cases. - s.RLock() - defer s.RUnlock() - for _, v := range s.VoiceConnections { - - s.log(LogInformational, "reconnecting voice connection to guild %s", v.GuildID) - go v.reconnect() - - // This is here just to prevent violently spamming the - // voice reconnects - time.Sleep(1 * time.Second) - - } - return - } - - // Certain race conditions can call reconnect() twice. If this happens, we - // just break out of the reconnect loop - if err == ErrWSAlreadyOpen { - s.log(LogInformational, "Websocket already exists, no need to reconnect") - return - } - - s.log(LogError, "error reconnecting to gateway, %s", err) - - <-time.After(wait * time.Second) - wait *= 2 - if wait > 600 { - wait = 600 - } - } - } -} - -// Close closes a websocket and stops all listening/heartbeat goroutines. -// TODO: Add support for Voice WS/UDP connections -func (s *Session) Close() (err error) { - - s.log(LogInformational, "called") - s.Lock() - - s.DataReady = false - - if s.listening != nil { - s.log(LogInformational, "closing listening channel") - close(s.listening) - s.listening = nil - } - - // TODO: Close all active Voice Connections too - // this should force stop any reconnecting voice channels too - - if s.wsConn != nil { - - s.log(LogInformational, "sending close frame") - // To cleanly close a connection, a client should send a close - // frame and wait for the server to close the connection. - s.wsMutex.Lock() - err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - s.wsMutex.Unlock() - if err != nil { - s.log(LogInformational, "error closing websocket, %s", err) - } - - // TODO: Wait for Discord to actually close the connection. - time.Sleep(1 * time.Second) - - s.log(LogInformational, "closing gateway websocket") - err = s.wsConn.Close() - if err != nil { - s.log(LogInformational, "error closing websocket, %s", err) - } - - s.wsConn = nil - } - - s.Unlock() - - s.log(LogInformational, "emit disconnect event") - s.handleEvent(disconnectEventType, &Disconnect{}) - - return -} |