summaryrefslogtreecommitdiffstats
path: root/vendor/github.com
diff options
context:
space:
mode:
authorWim <wim@42.be>2018-11-13 00:02:07 +0100
committerWim <wim@42.be>2018-11-13 00:02:07 +0100
commitf8dc24bc09fc1981637ac5c4a210780ac5512944 (patch)
tree0df78ce10744dbf3b25accdcb215a9b7b87b7e89 /vendor/github.com
parente9419f10d3d24e24c9cedab93104c418f383782c (diff)
downloadmatterbridge-msglm-f8dc24bc09fc1981637ac5c4a210780ac5512944.tar.gz
matterbridge-msglm-f8dc24bc09fc1981637ac5c4a210780ac5512944.tar.bz2
matterbridge-msglm-f8dc24bc09fc1981637ac5c4a210780ac5512944.zip
Switch back go upstream bwmarrin/discordgo
Commit https://github.com/bwmarrin/discordgo/commit/ffa9956c9b41e8e2a10c26a254389854e016b006 got merged in.
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/bwmarrin/discordgo/.travis.yml (renamed from vendor/github.com/matterbridge/discordgo/.travis.yml)6
-rw-r--r--vendor/github.com/bwmarrin/discordgo/LICENSE (renamed from vendor/github.com/matterbridge/discordgo/LICENSE)0
-rw-r--r--vendor/github.com/bwmarrin/discordgo/README.md (renamed from vendor/github.com/matterbridge/discordgo/README.md)20
-rw-r--r--vendor/github.com/bwmarrin/discordgo/discord.go (renamed from vendor/github.com/matterbridge/discordgo/discord.go)6
-rw-r--r--vendor/github.com/bwmarrin/discordgo/endpoints.go (renamed from vendor/github.com/matterbridge/discordgo/endpoints.go)28
-rw-r--r--vendor/github.com/bwmarrin/discordgo/event.go (renamed from vendor/github.com/matterbridge/discordgo/event.go)11
-rw-r--r--vendor/github.com/bwmarrin/discordgo/eventhandlers.go (renamed from vendor/github.com/matterbridge/discordgo/eventhandlers.go)24
-rw-r--r--vendor/github.com/bwmarrin/discordgo/events.go (renamed from vendor/github.com/matterbridge/discordgo/events.go)9
-rw-r--r--vendor/github.com/bwmarrin/discordgo/go.mod6
-rw-r--r--vendor/github.com/bwmarrin/discordgo/go.sum4
-rw-r--r--vendor/github.com/bwmarrin/discordgo/logging.go (renamed from vendor/github.com/matterbridge/discordgo/logging.go)0
-rw-r--r--vendor/github.com/bwmarrin/discordgo/message.go (renamed from vendor/github.com/matterbridge/discordgo/message.go)67
-rw-r--r--vendor/github.com/bwmarrin/discordgo/mkdocs.yml (renamed from vendor/github.com/matterbridge/discordgo/mkdocs.yml)0
-rw-r--r--vendor/github.com/bwmarrin/discordgo/oauth2.go (renamed from vendor/github.com/matterbridge/discordgo/oauth2.go)0
-rw-r--r--vendor/github.com/bwmarrin/discordgo/ratelimit.go (renamed from vendor/github.com/matterbridge/discordgo/ratelimit.go)0
-rw-r--r--vendor/github.com/bwmarrin/discordgo/restapi.go (renamed from vendor/github.com/matterbridge/discordgo/restapi.go)233
-rw-r--r--vendor/github.com/bwmarrin/discordgo/state.go (renamed from vendor/github.com/matterbridge/discordgo/state.go)29
-rw-r--r--vendor/github.com/bwmarrin/discordgo/structs.go (renamed from vendor/github.com/matterbridge/discordgo/structs.go)439
-rw-r--r--vendor/github.com/bwmarrin/discordgo/types.go (renamed from vendor/github.com/matterbridge/discordgo/types.go)3
-rw-r--r--vendor/github.com/bwmarrin/discordgo/user.go69
-rw-r--r--vendor/github.com/bwmarrin/discordgo/voice.go (renamed from vendor/github.com/matterbridge/discordgo/voice.go)10
-rw-r--r--vendor/github.com/bwmarrin/discordgo/wsapi.go (renamed from vendor/github.com/matterbridge/discordgo/wsapi.go)99
-rw-r--r--vendor/github.com/gorilla/websocket/.gitignore2
-rw-r--r--vendor/github.com/gorilla/websocket/.travis.yml10
-rw-r--r--vendor/github.com/gorilla/websocket/AUTHORS1
-rw-r--r--vendor/github.com/gorilla/websocket/README.md2
-rw-r--r--vendor/github.com/gorilla/websocket/client.go243
-rw-r--r--vendor/github.com/gorilla/websocket/conn.go170
-rw-r--r--vendor/github.com/gorilla/websocket/conn_read_legacy.go21
-rw-r--r--vendor/github.com/gorilla/websocket/conn_write.go (renamed from vendor/github.com/gorilla/websocket/conn_read.go)15
-rw-r--r--vendor/github.com/gorilla/websocket/conn_write_legacy.go18
-rw-r--r--vendor/github.com/gorilla/websocket/doc.go56
-rw-r--r--vendor/github.com/gorilla/websocket/json.go11
-rw-r--r--vendor/github.com/gorilla/websocket/mask.go1
-rw-r--r--vendor/github.com/gorilla/websocket/prepared.go1
-rw-r--r--vendor/github.com/gorilla/websocket/proxy.go77
-rw-r--r--vendor/github.com/gorilla/websocket/server.go136
-rw-r--r--vendor/github.com/gorilla/websocket/trace.go19
-rw-r--r--vendor/github.com/gorilla/websocket/trace_17.go12
-rw-r--r--vendor/github.com/gorilla/websocket/util.go35
-rw-r--r--vendor/github.com/gorilla/websocket/x_net_proxy.go473
-rw-r--r--vendor/github.com/matterbridge/discordgo/user.go47
42 files changed, 1879 insertions, 534 deletions
diff --git a/vendor/github.com/matterbridge/discordgo/.travis.yml b/vendor/github.com/bwmarrin/discordgo/.travis.yml
index fe626fcf..2656ae53 100644
--- a/vendor/github.com/matterbridge/discordgo/.travis.yml
+++ b/vendor/github.com/bwmarrin/discordgo/.travis.yml
@@ -1,12 +1,12 @@
language: go
go:
- - 1.7.x
- - 1.8.x
- 1.9.x
+ - 1.10.x
+ - 1.11.x
install:
- go get github.com/bwmarrin/discordgo
- go get -v .
- - go get -v github.com/golang/lint/golint
+ - go get -v golang.org/x/lint/golint
script:
- diff <(gofmt -d .) <(echo -n)
- go vet -x ./...
diff --git a/vendor/github.com/matterbridge/discordgo/LICENSE b/vendor/github.com/bwmarrin/discordgo/LICENSE
index 8d062ea5..8d062ea5 100644
--- a/vendor/github.com/matterbridge/discordgo/LICENSE
+++ b/vendor/github.com/bwmarrin/discordgo/LICENSE
diff --git a/vendor/github.com/matterbridge/discordgo/README.md b/vendor/github.com/bwmarrin/discordgo/README.md
index acc72bf1..cb5a6659 100644
--- a/vendor/github.com/matterbridge/discordgo/README.md
+++ b/vendor/github.com/bwmarrin/discordgo/README.md
@@ -1,4 +1,4 @@
-# DiscordGo
+# DiscordGo
[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discord.gg/0SBTUU1wZTWT6sqd)
@@ -15,11 +15,11 @@ 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
+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)
+Discord (and DiscordGo).
**For help with this package or general Go discussion, please join the [Discord
Gophers](https://discord.gg/0f1SbxBZjYq9jLBk) chat server.**
@@ -39,9 +39,9 @@ the breaking changes get documented before pushing to master.
*So, what should you use?*
-If you can accept the constant changing nature of *develop* then it is the
+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 then use *master*
+slightly and have a more stable package with documented releases, use *master*.
### Installing
@@ -96,10 +96,10 @@ that information in a nice format.
## 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
+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
+- [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
@@ -114,7 +114,7 @@ Contributions are very welcomed, however please follow the below guidelines.
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
+- 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.
@@ -127,4 +127,4 @@ comparison and list of other Discord API libraries.
## Special Thanks
-[Chris Rhodes](https://github.com/iopred) - For the DiscordGo logo and tons of PRs
+[Chris Rhodes](https://github.com/iopred) - For the DiscordGo logo and tons of PRs.
diff --git a/vendor/github.com/matterbridge/discordgo/discord.go b/vendor/github.com/bwmarrin/discordgo/discord.go
index 99fda30b..cdac67fe 100644
--- a/vendor/github.com/matterbridge/discordgo/discord.go
+++ b/vendor/github.com/bwmarrin/discordgo/discord.go
@@ -6,8 +6,8 @@
// 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 beling developed and are very
-// experimental at this point. They will most likley change so please use 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
@@ -21,7 +21,7 @@ import (
)
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
-const VERSION = "0.18.0"
+const VERSION = "0.19.0"
// ErrMFA will be risen by New when the user has 2FA.
var ErrMFA = errors.New("account has 2FA enabled")
diff --git a/vendor/github.com/matterbridge/discordgo/endpoints.go b/vendor/github.com/bwmarrin/discordgo/endpoints.go
index 335e224d..b9619089 100644
--- a/vendor/github.com/matterbridge/discordgo/endpoints.go
+++ b/vendor/github.com/bwmarrin/discordgo/endpoints.go
@@ -11,6 +11,8 @@
package discordgo
+import "strconv"
+
// APIVersion is the Discord API version used for the REST and Websocket API.
var APIVersion = "6"
@@ -61,14 +63,18 @@ var (
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" }
- 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 }
+ 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" }
@@ -88,6 +94,9 @@ var (
EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" }
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 }
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
@@ -127,7 +136,8 @@ var (
EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
- EndpointEmoji = func(eID string) string { return EndpointAPI + "emojis/" + eID + ".png" }
+ 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"
diff --git a/vendor/github.com/matterbridge/discordgo/event.go b/vendor/github.com/bwmarrin/discordgo/event.go
index bba396cb..97cc00a2 100644
--- a/vendor/github.com/matterbridge/discordgo/event.go
+++ b/vendor/github.com/bwmarrin/discordgo/event.go
@@ -98,7 +98,9 @@ func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the function fires.
-// events.go contains all the Discord WSAPI events that can be fired.
+// 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) {
// })
@@ -106,6 +108,13 @@ func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
// 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() {
diff --git a/vendor/github.com/matterbridge/discordgo/eventhandlers.go b/vendor/github.com/bwmarrin/discordgo/eventhandlers.go
index 5cc157de..d2b9a98b 100644
--- a/vendor/github.com/matterbridge/discordgo/eventhandlers.go
+++ b/vendor/github.com/bwmarrin/discordgo/eventhandlers.go
@@ -50,6 +50,7 @@ const (
userUpdateEventType = "USER_UPDATE"
voiceServerUpdateEventType = "VOICE_SERVER_UPDATE"
voiceStateUpdateEventType = "VOICE_STATE_UPDATE"
+ webhooksUpdateEventType = "WEBHOOKS_UPDATE"
)
// channelCreateEventHandler is an event handler for ChannelCreate events.
@@ -892,6 +893,26 @@ func (eh voiceStateUpdateEventHandler) Handle(s *Session, i interface{}) {
}
}
+// 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{}):
@@ -982,6 +1003,8 @@ func handlerForInterface(handler interface{}) EventHandler {
return voiceServerUpdateEventHandler(v)
case func(*Session, *VoiceStateUpdate):
return voiceStateUpdateEventHandler(v)
+ case func(*Session, *WebhooksUpdate):
+ return webhooksUpdateEventHandler(v)
}
return nil
@@ -1027,4 +1050,5 @@ func init() {
registerInterfaceProvider(userUpdateEventHandler(nil))
registerInterfaceProvider(voiceServerUpdateEventHandler(nil))
registerInterfaceProvider(voiceStateUpdateEventHandler(nil))
+ registerInterfaceProvider(webhooksUpdateEventHandler(nil))
}
diff --git a/vendor/github.com/matterbridge/discordgo/events.go b/vendor/github.com/bwmarrin/discordgo/events.go
index c78fbdd2..c4fb5205 100644
--- a/vendor/github.com/matterbridge/discordgo/events.go
+++ b/vendor/github.com/bwmarrin/discordgo/events.go
@@ -70,6 +70,7 @@ type ChannelDelete struct {
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.
@@ -212,6 +213,7 @@ type RelationshipRemove struct {
type TypingStart struct {
UserID string `json:"user_id"`
ChannelID string `json:"channel_id"`
+ GuildID string `json:"guild_id,omitempty"`
Timestamp int `json:"timestamp"`
}
@@ -250,4 +252,11 @@ type VoiceStateUpdate struct {
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
new file mode 100644
index 00000000..2ff88680
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/go.mod
@@ -0,0 +1,6 @@
+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
new file mode 100644
index 00000000..a86b0501
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/go.sum
@@ -0,0 +1,4 @@
+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/matterbridge/discordgo/logging.go b/vendor/github.com/bwmarrin/discordgo/logging.go
index 6460b35b..6460b35b 100644
--- a/vendor/github.com/matterbridge/discordgo/logging.go
+++ b/vendor/github.com/bwmarrin/discordgo/logging.go
diff --git a/vendor/github.com/matterbridge/discordgo/message.go b/vendor/github.com/bwmarrin/discordgo/message.go
index 4fd468fd..2b609920 100644
--- a/vendor/github.com/matterbridge/discordgo/message.go
+++ b/vendor/github.com/bwmarrin/discordgo/message.go
@@ -32,20 +32,59 @@ const (
// A Message stores all data related to a specific Discord message.
type Message struct {
- ID string `json:"id"`
- ChannelID string `json:"channel_id"`
- Content string `json:"content"`
- Timestamp Timestamp `json:"timestamp"`
- EditedTimestamp Timestamp `json:"edited_timestamp"`
- MentionRoles []string `json:"mention_roles"`
- Tts bool `json:"tts"`
- MentionEveryone bool `json:"mention_everyone"`
- Author *User `json:"author"`
- Attachments []*MessageAttachment `json:"attachments"`
- Embeds []*MessageEmbed `json:"embeds"`
- Mentions []*User `json:"mentions"`
- Reactions []*MessageReactions `json:"reactions"`
- Type MessageType `json:"type"`
+ // 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"`
+
+ // 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"`
}
// File stores info about files you e.g. send in messages.
diff --git a/vendor/github.com/matterbridge/discordgo/mkdocs.yml b/vendor/github.com/bwmarrin/discordgo/mkdocs.yml
index 3ee8eb37..3ee8eb37 100644
--- a/vendor/github.com/matterbridge/discordgo/mkdocs.yml
+++ b/vendor/github.com/bwmarrin/discordgo/mkdocs.yml
diff --git a/vendor/github.com/matterbridge/discordgo/oauth2.go b/vendor/github.com/bwmarrin/discordgo/oauth2.go
index 108b32fe..108b32fe 100644
--- a/vendor/github.com/matterbridge/discordgo/oauth2.go
+++ b/vendor/github.com/bwmarrin/discordgo/oauth2.go
diff --git a/vendor/github.com/matterbridge/discordgo/ratelimit.go b/vendor/github.com/bwmarrin/discordgo/ratelimit.go
index dc48c924..dc48c924 100644
--- a/vendor/github.com/matterbridge/discordgo/ratelimit.go
+++ b/vendor/github.com/bwmarrin/discordgo/ratelimit.go
diff --git a/vendor/github.com/matterbridge/discordgo/restapi.go b/vendor/github.com/bwmarrin/discordgo/restapi.go
index 5dc0467f..84a2a31e 100644
--- a/vendor/github.com/matterbridge/discordgo/restapi.go
+++ b/vendor/github.com/bwmarrin/discordgo/restapi.go
@@ -38,6 +38,7 @@ var (
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
@@ -89,7 +90,7 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
req.Header.Set("Content-Type", contentType)
// TODO: Make a configurable static variable.
- req.Header.Set("User-Agent", fmt.Sprintf("DiscordBot (https://github.com/bwmarrin/discordgo, v%s)", VERSION))
+ req.Header.Set("User-Agent", "DiscordBot (https://github.com/bwmarrin/discordgo, v"+VERSION+")")
if s.Debug {
for k, v := range req.Header {
@@ -129,13 +130,9 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
}
switch resp.StatusCode {
-
case http.StatusOK:
case http.StatusCreated:
case http.StatusNoContent:
-
- // TODO check for 401 response, invalidate token if we get one.
-
case http.StatusBadGateway:
// Retry sending request if possible
if sequence < s.MaxRestRetries {
@@ -145,7 +142,6 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
} 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)
@@ -161,7 +157,12 @@ func (s *Session) RequestWithLockedBucket(method, urlStr, contentType string, b
// 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)
}
@@ -249,7 +250,7 @@ func (s *Session) Register(username string) (token string, err error) {
// even use.
func (s *Session) Logout() (err error) {
- // _, err = s.Request("POST", LOGOUT, fmt.Sprintf(`{"token": "%s"}`, s.Token))
+ // _, err = s.Request("POST", LOGOUT, `{"token": "` + s.Token + `"}`)
if s.Token == "" {
return
@@ -361,6 +362,21 @@ func (s *Session) UserUpdateStatus(status Status) (st *Settings, err error) {
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) {
@@ -412,7 +428,7 @@ func (s *Session) UserGuilds(limit int, beforeID, afterID string) (st []*UserGui
uri := EndpointUserGuilds("@me")
if len(v) > 0 {
- uri = fmt.Sprintf("%s?%s", uri, v.Encode())
+ uri += "?" + v.Encode()
}
body, err := s.RequestWithBucketID("GET", uri, nil, EndpointUserGuilds(""))
@@ -565,7 +581,7 @@ 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 {
+ if err == nil && !st.Unavailable {
return
}
}
@@ -735,7 +751,7 @@ func (s *Session) GuildMembers(guildID string, after string, limit int) (st []*M
}
if len(v) > 0 {
- uri = fmt.Sprintf("%s?%s", uri, v.Encode())
+ uri += "?" + v.Encode()
}
body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildMembers(guildID))
@@ -761,6 +777,32 @@ func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) {
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
@@ -877,17 +919,22 @@ func (s *Session) GuildChannels(guildID string) (st []*Channel, err error) {
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 : Tpye of the channel (voice or text)
-func (s *Session) GuildChannelCreate(guildID, name, ctype string) (st *Channel, err error) {
-
- data := struct {
- Name string `json:"name"`
- Type string `json:"type"`
- }{name, ctype}
+// 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
@@ -897,12 +944,33 @@ func (s *Session) GuildChannelCreate(guildID, name, ctype string) (st *Channel,
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) {
- _, err = s.RequestWithBucketID("PATCH", EndpointGuildChannels(guildID), channels, EndpointGuildChannels(guildID))
+ 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
}
@@ -1021,7 +1089,7 @@ func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, er
Pruned uint32 `json:"pruned"`
}{}
- uri := EndpointGuildPrune(guildID) + fmt.Sprintf("?days=%d", days)
+ uri := EndpointGuildPrune(guildID) + "?days=" + strconv.FormatUint(uint64(days), 10)
body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID))
if err != nil {
return
@@ -1075,7 +1143,7 @@ func (s *Session) GuildPrune(guildID string, days uint32) (count uint32, err err
// GuildIntegrations returns an array of Integrations for a guild.
// guildID : The ID of a Guild.
-func (s *Session) GuildIntegrations(guildID string) (st []*GuildIntegration, err error) {
+func (s *Session) GuildIntegrations(guildID string) (st []*Integration, err error) {
body, err := s.RequestWithBucketID("GET", EndpointGuildIntegrations(guildID), nil, EndpointGuildIntegrations(guildID))
if err != nil {
@@ -1206,6 +1274,94 @@ func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string)
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
// ------------------------------------------------------------------------------------------------
@@ -1291,7 +1447,7 @@ func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID
v.Set("around", aroundID)
}
if len(v) > 0 {
- uri = fmt.Sprintf("%s?%s", uri, v.Encode())
+ uri += "?" + v.Encode()
}
body, err := s.RequestWithBucketID("GET", uri, nil, EndpointChannelMessages(channelID))
@@ -1586,7 +1742,8 @@ func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, e
MaxAge int `json:"max_age"`
MaxUses int `json:"max_uses"`
Temporary bool `json:"temporary"`
- }{i.MaxAge, i.MaxUses, i.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 {
@@ -1638,6 +1795,19 @@ func (s *Session) Invite(inviteID string) (st *Invite, err error) {
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) {
@@ -1830,12 +2000,13 @@ func (s *Session) WebhookWithToken(webhookID, token string) (st *Webhook, err er
// 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 string) (st *Role, err error) {
+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"`
- }{name, avatar}
+ 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 {
@@ -1956,7 +2127,7 @@ func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit i
}
if len(v) > 0 {
- uri = fmt.Sprintf("%s?%s", uri, v.Encode())
+ uri += "?" + v.Encode()
}
body, err := s.RequestWithBucketID("GET", uri, nil, EndpointMessageReaction(channelID, "", "", ""))
diff --git a/vendor/github.com/matterbridge/discordgo/state.go b/vendor/github.com/bwmarrin/discordgo/state.go
index 8158708b..e6f08c73 100644
--- a/vendor/github.com/matterbridge/discordgo/state.go
+++ b/vendor/github.com/bwmarrin/discordgo/state.go
@@ -32,6 +32,7 @@ type State struct {
sync.RWMutex
Ready
+ // MaxMessageCount represents how many messages per channel the state will store.
MaxMessageCount int
TrackChannels bool
TrackEmojis bool
@@ -98,6 +99,9 @@ func (s *State) GuildAdd(guild *Guild) error {
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
}
@@ -299,7 +303,12 @@ func (s *State) MemberAdd(member *Member) error {
members[member.User.ID] = member
guild.Members = append(guild.Members, member)
} else {
- *m = *member // Update the actual data, which will also update the member pointer in the slice
+ // 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
@@ -607,7 +616,7 @@ func (s *State) EmojisAdd(guildID string, emojis []*Emoji) error {
// 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
+// Messages are kept in state up to s.MaxMessageCount per channel.
func (s *State) MessageAdd(message *Message) error {
if s == nil {
return ErrNilState
@@ -805,6 +814,14 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
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)
}
@@ -813,6 +830,14 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
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)
}
diff --git a/vendor/github.com/matterbridge/discordgo/structs.go b/vendor/github.com/bwmarrin/discordgo/structs.go
index 19d2bad7..4465ec52 100644
--- a/vendor/github.com/matterbridge/discordgo/structs.go
+++ b/vendor/github.com/bwmarrin/discordgo/structs.go
@@ -13,6 +13,7 @@ package discordgo
import (
"encoding/json"
+ "fmt"
"net/http"
"sync"
"time"
@@ -84,6 +85,9 @@ type Session struct {
// 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
@@ -111,6 +115,37 @@ type Session struct {
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"`
@@ -145,6 +180,10 @@ type Invite struct {
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
@@ -161,22 +200,61 @@ const (
// A Channel holds all data related to an individual Discord channel.
type Channel struct {
- ID string `json:"id"`
- GuildID string `json:"guild_id"`
- Name string `json:"name"`
- Topic string `json:"topic"`
- Type ChannelType `json:"type"`
- LastMessageID string `json:"last_message_id"`
- NSFW bool `json:"nsfw"`
- Position int `json:"position"`
- Bitrate int `json:"bitrate"`
- Recipients []*User `json:"recipients"`
- Messages []*Message `json:"-"`
+ // 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"`
+
+ // 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"`
- ParentID string `json:"parent_id"`
+
+ // 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"`
+}
+
+// Mention returns a string which mentions the channel
+func (c *Channel) Mention() string {
+ return fmt.Sprintf("<#%s>", c.ID)
}
-// A ChannelEdit holds Channel Feild data for a channel edit.
+// A ChannelEdit holds Channel Field data for a channel edit.
type ChannelEdit struct {
Name string `json:"name,omitempty"`
Topic string `json:"topic,omitempty"`
@@ -186,6 +264,7 @@ type ChannelEdit struct {
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
@@ -206,6 +285,19 @@ type Emoji struct {
Animated bool `json:"animated"`
}
+// 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 != "" {
@@ -228,31 +320,129 @@ const (
VerificationLevelHigh
)
+// 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
+)
+
// 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 {
- ID string `json:"id"`
- Name string `json:"name"`
- Icon string `json:"icon"`
- Region string `json:"region"`
- AfkChannelID string `json:"afk_channel_id"`
- EmbedChannelID string `json:"embed_channel_id"`
- OwnerID string `json:"owner_id"`
- JoinedAt Timestamp `json:"joined_at"`
- Splash string `json:"splash"`
- AfkTimeout int `json:"afk_timeout"`
- MemberCount int `json:"member_count"`
- VerificationLevel VerificationLevel `json:"verification_level"`
- EmbedEnabled bool `json:"embed_enabled"`
- Large bool `json:"large"` // ??
- DefaultMessageNotifications int `json:"default_message_notifications"`
- Roles []*Role `json:"roles"`
- Emojis []*Emoji `json:"emojis"`
- Members []*Member `json:"members"`
- Presences []*Presence `json:"presences"`
- Channels []*Channel `json:"channels"`
- VoiceStates []*VoiceState `json:"voice_states"`
- Unavailable bool `json:"unavailable"`
+ // 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"`
}
// A UserGuild holds a brief version of a Guild
@@ -279,14 +469,37 @@ type GuildParams struct {
// A Role stores information about Discord guild member roles.
type Role struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Managed bool `json:"managed"`
- Mentionable bool `json:"mentionable"`
- Hoist bool `json:"hoist"`
- Color int `json:"color"`
- Position int `json:"position"`
- Permissions int `json:"permissions"`
+ // 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
@@ -334,6 +547,8 @@ type GameType int
const (
GameTypeGame GameType = iota
GameTypeStreaming
+ GameTypeListening
+ GameTypeWatching
)
// A Game struct holds the name of the "playing .." game for a user
@@ -379,15 +594,34 @@ type Assets struct {
SmallText string `json:"small_text,omitempty"`
}
-// A Member stores user information for Guild members.
+// A Member stores user information for Guild members. A guild
+// member represents a certain user's presence in a guild.
type Member struct {
- GuildID string `json:"guild_id"`
- JoinedAt string `json:"joined_at"`
- Nick string `json:"nick"`
- Deaf bool `json:"deaf"`
- Mute bool `json:"mute"`
- User *User `json:"user"`
- Roles []string `json:"roles"`
+ // 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"`
+}
+
+// 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.
@@ -467,33 +701,88 @@ type GuildBan struct {
User *User `json:"user"`
}
-// A GuildIntegration stores data for a guild integration.
-type GuildIntegration 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 *GuildIntegrationAccount `json:"account"`
- SyncedAt int `json:"synced_at"`
-}
-
-// A GuildIntegrationAccount stores data for a guild integration account.
-type GuildIntegrationAccount struct {
- ID string `json:"id"`
- Name string `json:"name"`
-}
-
// 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"`
@@ -553,6 +842,7 @@ type MessageReaction struct {
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
@@ -629,7 +919,9 @@ const (
PermissionKickMembers |
PermissionBanMembers |
PermissionManageServer |
- PermissionAdministrator
+ PermissionAdministrator |
+ PermissionManageWebhooks |
+ PermissionManageEmojis
)
// Block contains Discord JSON Error Response codes
@@ -648,6 +940,7 @@ const (
ErrCodeUnknownToken = 10012
ErrCodeUnknownUser = 10013
ErrCodeUnknownEmoji = 10014
+ ErrCodeUnknownWebhook = 10015
ErrCodeBotsCannotUseEndpoint = 20001
ErrCodeOnlyBotsCanUseEndpoint = 20002
diff --git a/vendor/github.com/matterbridge/discordgo/types.go b/vendor/github.com/bwmarrin/discordgo/types.go
index 780b6bb9..c0ce0131 100644
--- a/vendor/github.com/matterbridge/discordgo/types.go
+++ b/vendor/github.com/bwmarrin/discordgo/types.go
@@ -11,7 +11,6 @@ package discordgo
import (
"encoding/json"
- "fmt"
"net/http"
"time"
)
@@ -54,5 +53,5 @@ func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTErro
}
func (r RESTError) Error() string {
- return fmt.Sprintf("HTTP %s, %s", r.Response.Status, r.ResponseBody)
+ 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
new file mode 100644
index 00000000..a9af31a9
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/user.go
@@ -0,0 +1,69 @@
+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/matterbridge/discordgo/voice.go b/vendor/github.com/bwmarrin/discordgo/voice.go
index 3bbf6212..aa630b12 100644
--- a/vendor/github.com/matterbridge/discordgo/voice.go
+++ b/vendor/github.com/bwmarrin/discordgo/voice.go
@@ -14,6 +14,7 @@ import (
"encoding/json"
"fmt"
"net"
+ "strconv"
"strings"
"sync"
"time"
@@ -103,7 +104,7 @@ func (v *VoiceConnection) Speaking(b bool) (err error) {
defer v.Unlock()
if err != nil {
v.speaking = false
- v.log(LogError, "Speaking() write json error:", err)
+ v.log(LogError, "Speaking() write json error, %s", err)
return
}
@@ -135,7 +136,6 @@ func (v *VoiceConnection) ChangeChannel(channelID string, mute, deaf bool) (err
// Disconnect disconnects from this voice channel and closes the websocket
// and udp connections to Discord.
-// !!! NOTE !!! this function may be removed in favour of ChannelVoiceLeave
func (v *VoiceConnection) Disconnect() (err error) {
// Send a OP4 with a nil channel to disconnect
@@ -180,7 +180,7 @@ func (v *VoiceConnection) Close() {
v.log(LogInformational, "closing udp")
err := v.udpConn.Close()
if err != nil {
- v.log(LogError, "error closing udp connection: ", err)
+ v.log(LogError, "error closing udp connection, %s", err)
}
v.udpConn = nil
}
@@ -299,7 +299,7 @@ func (v *VoiceConnection) open() (err error) {
}
// Connect to VoiceConnection Websocket
- vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(v.endpoint, ":80"))
+ 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 {
@@ -542,7 +542,7 @@ func (v *VoiceConnection) udpOpen() (err error) {
return fmt.Errorf("empty endpoint")
}
- host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.op2.Port)
+ host := strings.TrimSuffix(v.endpoint, ":80") + ":" + 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)
diff --git a/vendor/github.com/matterbridge/discordgo/wsapi.go b/vendor/github.com/bwmarrin/discordgo/wsapi.go
index de66f693..8ecaaa77 100644
--- a/vendor/github.com/matterbridge/discordgo/wsapi.go
+++ b/vendor/github.com/bwmarrin/discordgo/wsapi.go
@@ -86,6 +86,10 @@ func (s *Session) Open() error {
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
@@ -263,6 +267,13 @@ type helloOp struct {
// 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.
@@ -283,8 +294,9 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
last := s.LastHeartbeatAck
s.RUnlock()
sequence := atomic.LoadInt64(s.sequence)
- s.log(LogInformational, "sending gateway websocket heartbeat seq %d", 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) {
@@ -323,16 +335,8 @@ type updateStatusOp struct {
Data UpdateStatusData `json:"d"`
}
-// 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) {
-
- s.log(LogInformational, "called")
-
- usd := UpdateStatusData{
+func newUpdateStatusData(idle int, gameType GameType, game, url string) *UpdateStatusData {
+ usd := &UpdateStatusData{
Status: "online",
}
@@ -341,10 +345,6 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
}
if game != "" {
- gameType := GameTypeGame
- if url != "" {
- gameType = GameTypeStreaming
- }
usd.Game = &Game{
Name: game,
Type: gameType,
@@ -352,7 +352,35 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
}
}
- return s.UpdateStatusComplex(usd)
+ 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.
@@ -371,14 +399,6 @@ func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) {
return
}
-// 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.UpdateStreamingStatus(idle, game, "")
-}
-
type requestGuildMembersData struct {
GuildID string `json:"guild_id"`
Query string `json:"query"`
@@ -508,7 +528,7 @@ func (s *Session) onEvent(messageType int, message []byte) (*Event, error) {
s.Lock()
s.LastHeartbeatAck = time.Now().UTC()
s.Unlock()
- s.log(LogInformational, "got heartbeat ACK")
+ s.log(LogDebug, "got heartbeat ACK")
return e, nil
}
@@ -615,6 +635,30 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi
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.
+// 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")
+
+ // Send the request to Discord that we want to join the voice channel
+ data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}}
+ s.wsMutex.Lock()
+ err = s.wsConn.WriteJSON(data)
+ s.wsMutex.Unlock()
+ if err != nil {
+ return
+ }
+
+ return
+}
+
// onVoiceStateUpdate handles Voice State Update events on the data websocket.
func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) {
@@ -732,11 +776,8 @@ func (s *Session) identify() error {
s.wsMutex.Lock()
err := s.wsConn.WriteJSON(op)
s.wsMutex.Unlock()
- if err != nil {
- return err
- }
- return nil
+ return err
}
func (s *Session) reconnect() {
diff --git a/vendor/github.com/gorilla/websocket/.gitignore b/vendor/github.com/gorilla/websocket/.gitignore
index ac710204..cd3fcd1e 100644
--- a/vendor/github.com/gorilla/websocket/.gitignore
+++ b/vendor/github.com/gorilla/websocket/.gitignore
@@ -22,4 +22,4 @@ _testmain.go
*.exe
.idea/
-*.iml \ No newline at end of file
+*.iml
diff --git a/vendor/github.com/gorilla/websocket/.travis.yml b/vendor/github.com/gorilla/websocket/.travis.yml
index 3d8d29cf..a49db51c 100644
--- a/vendor/github.com/gorilla/websocket/.travis.yml
+++ b/vendor/github.com/gorilla/websocket/.travis.yml
@@ -3,11 +3,11 @@ sudo: false
matrix:
include:
- - go: 1.4
- - go: 1.5
- - go: 1.6
- - go: 1.7
- - go: 1.8
+ - go: 1.7.x
+ - go: 1.8.x
+ - go: 1.9.x
+ - go: 1.10.x
+ - go: 1.11.x
- go: tip
allow_failures:
- go: tip
diff --git a/vendor/github.com/gorilla/websocket/AUTHORS b/vendor/github.com/gorilla/websocket/AUTHORS
index b003eca0..1931f400 100644
--- a/vendor/github.com/gorilla/websocket/AUTHORS
+++ b/vendor/github.com/gorilla/websocket/AUTHORS
@@ -4,5 +4,6 @@
# Please keep the list sorted.
Gary Burd <gary@beagledreams.com>
+Google LLC (https://opensource.google.com/)
Joachim Bauch <mail@joachim-bauch.de>
diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md
index 33c3d2be..20e391f8 100644
--- a/vendor/github.com/gorilla/websocket/README.md
+++ b/vendor/github.com/gorilla/websocket/README.md
@@ -51,7 +51,7 @@ subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
</table>
-Notes:
+Notes:
1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).
2. The application can get the type of a received data message by implementing
diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go
index 43a87c75..2e32fd50 100644
--- a/vendor/github.com/gorilla/websocket/client.go
+++ b/vendor/github.com/gorilla/websocket/client.go
@@ -5,15 +5,15 @@
package websocket
import (
- "bufio"
"bytes"
+ "context"
"crypto/tls"
- "encoding/base64"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
+ "net/http/httptrace"
"net/url"
"strings"
"time"
@@ -53,6 +53,10 @@ type Dialer struct {
// NetDial is nil, net.Dial is used.
NetDial func(network, addr string) (net.Conn, error)
+ // NetDialContext specifies the dial function for creating TCP connections. If
+ // NetDialContext is nil, net.DialContext is used.
+ NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
+
// Proxy specifies a function to return a proxy for a given
// Request. If the function returns a non-nil error, the
// request is aborted with the provided error.
@@ -71,6 +75,17 @@ type Dialer struct {
// do not limit the size of the messages that can be sent or received.
ReadBufferSize, WriteBufferSize int
+ // WriteBufferPool is a pool of buffers for write operations. If the value
+ // is not set, then write buffers are allocated to the connection for the
+ // lifetime of the connection.
+ //
+ // A pool is most useful when the application has a modest volume of writes
+ // across a large number of connections.
+ //
+ // Applications should use a single pool for each unique value of
+ // WriteBufferSize.
+ WriteBufferPool BufferPool
+
// Subprotocols specifies the client's requested subprotocols.
Subprotocols []string
@@ -86,52 +101,13 @@ type Dialer struct {
Jar http.CookieJar
}
-var errMalformedURL = errors.New("malformed ws or wss URL")
-
-// parseURL parses the URL.
-//
-// This function is a replacement for the standard library url.Parse function.
-// In Go 1.4 and earlier, url.Parse loses information from the path.
-func parseURL(s string) (*url.URL, error) {
- // From the RFC:
- //
- // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
- // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
- var u url.URL
- switch {
- case strings.HasPrefix(s, "ws://"):
- u.Scheme = "ws"
- s = s[len("ws://"):]
- case strings.HasPrefix(s, "wss://"):
- u.Scheme = "wss"
- s = s[len("wss://"):]
- default:
- return nil, errMalformedURL
- }
-
- if i := strings.Index(s, "?"); i >= 0 {
- u.RawQuery = s[i+1:]
- s = s[:i]
- }
-
- if i := strings.Index(s, "/"); i >= 0 {
- u.Opaque = s[i:]
- s = s[:i]
- } else {
- u.Opaque = "/"
- }
-
- u.Host = s
-
- if strings.Contains(u.Host, "@") {
- // Don't bother parsing user information because user information is
- // not allowed in websocket URIs.
- return nil, errMalformedURL
- }
-
- return &u, nil
+// Dial creates a new client connection by calling DialContext with a background context.
+func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
+ return d.DialContext(context.Background(), urlStr, requestHeader)
}
+var errMalformedURL = errors.New("malformed ws or wss URL")
+
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
hostPort = u.Host
hostNoPort = u.Host
@@ -150,26 +126,29 @@ func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
return hostPort, hostNoPort
}
-// DefaultDialer is a dialer with all fields set to the default zero values.
+// DefaultDialer is a dialer with all fields set to the default values.
var DefaultDialer = &Dialer{
- Proxy: http.ProxyFromEnvironment,
+ Proxy: http.ProxyFromEnvironment,
+ HandshakeTimeout: 45 * time.Second,
}
-// Dial creates a new client connection. Use requestHeader to specify the
+// nilDialer is dialer to use when receiver is nil.
+var nilDialer = *DefaultDialer
+
+// DialContext creates a new client connection. Use requestHeader to specify the
// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
// Use the response.Header to get the selected subprotocol
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
//
+// The context will be used in the request and in the Dialer
+//
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
// non-nil *http.Response so that callers can handle redirects, authentication,
// etcetera. The response body may not contain the entire response and does not
// need to be closed by the application.
-func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
-
+func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
if d == nil {
- d = &Dialer{
- Proxy: http.ProxyFromEnvironment,
- }
+ d = &nilDialer
}
challengeKey, err := generateChallengeKey()
@@ -177,7 +156,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
return nil, nil, err
}
- u, err := parseURL(urlStr)
+ u, err := url.Parse(urlStr)
if err != nil {
return nil, nil, err
}
@@ -205,6 +184,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
Header: make(http.Header),
Host: u.Host,
}
+ req = req.WithContext(ctx)
// Set the cookies present in the cookie jar of the dialer
if d.Jar != nil {
@@ -237,45 +217,83 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
k == "Sec-Websocket-Extensions" ||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
+ case k == "Sec-Websocket-Protocol":
+ req.Header["Sec-WebSocket-Protocol"] = vs
default:
req.Header[k] = vs
}
}
if d.EnableCompression {
- req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
+ req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"}
}
- hostPort, hostNoPort := hostPortNoPort(u)
-
- var proxyURL *url.URL
- // Check wether the proxy method has been configured
- if d.Proxy != nil {
- proxyURL, err = d.Proxy(req)
- }
- if err != nil {
- return nil, nil, err
+ if d.HandshakeTimeout != 0 {
+ var cancel func()
+ ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)
+ defer cancel()
}
- var targetHostPort string
- if proxyURL != nil {
- targetHostPort, _ = hostPortNoPort(proxyURL)
+ // Get network dial function.
+ var netDial func(network, add string) (net.Conn, error)
+
+ if d.NetDialContext != nil {
+ netDial = func(network, addr string) (net.Conn, error) {
+ return d.NetDialContext(ctx, network, addr)
+ }
+ } else if d.NetDial != nil {
+ netDial = d.NetDial
} else {
- targetHostPort = hostPort
+ netDialer := &net.Dialer{}
+ netDial = func(network, addr string) (net.Conn, error) {
+ return netDialer.DialContext(ctx, network, addr)
+ }
}
- var deadline time.Time
- if d.HandshakeTimeout != 0 {
- deadline = time.Now().Add(d.HandshakeTimeout)
+ // If needed, wrap the dial function to set the connection deadline.
+ if deadline, ok := ctx.Deadline(); ok {
+ forwardDial := netDial
+ netDial = func(network, addr string) (net.Conn, error) {
+ c, err := forwardDial(network, addr)
+ if err != nil {
+ return nil, err
+ }
+ err = c.SetDeadline(deadline)
+ if err != nil {
+ c.Close()
+ return nil, err
+ }
+ return c, nil
+ }
}
- netDial := d.NetDial
- if netDial == nil {
- netDialer := &net.Dialer{Deadline: deadline}
- netDial = netDialer.Dial
+ // If needed, wrap the dial function to connect through a proxy.
+ if d.Proxy != nil {
+ proxyURL, err := d.Proxy(req)
+ if err != nil {
+ return nil, nil, err
+ }
+ if proxyURL != nil {
+ dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
+ if err != nil {
+ return nil, nil, err
+ }
+ netDial = dialer.Dial
+ }
+ }
+
+ hostPort, hostNoPort := hostPortNoPort(u)
+ trace := httptrace.ContextClientTrace(ctx)
+ if trace != nil && trace.GetConn != nil {
+ trace.GetConn(hostPort)
}
- netConn, err := netDial("tcp", targetHostPort)
+ netConn, err := netDial("tcp", hostPort)
+ if trace != nil && trace.GotConn != nil {
+ trace.GotConn(httptrace.GotConnInfo{
+ Conn: netConn,
+ })
+ }
if err != nil {
return nil, nil, err
}
@@ -286,42 +304,6 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
}
}()
- if err := netConn.SetDeadline(deadline); err != nil {
- return nil, nil, err
- }
-
- if proxyURL != nil {
- connectHeader := make(http.Header)
- if user := proxyURL.User; user != nil {
- proxyUser := user.Username()
- if proxyPassword, passwordSet := user.Password(); passwordSet {
- credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
- connectHeader.Set("Proxy-Authorization", "Basic "+credential)
- }
- }
- connectReq := &http.Request{
- Method: "CONNECT",
- URL: &url.URL{Opaque: hostPort},
- Host: hostPort,
- Header: connectHeader,
- }
-
- connectReq.Write(netConn)
-
- // Read response.
- // Okay to use and discard buffered reader here, because
- // TLS server will not speak until spoken to.
- br := bufio.NewReader(netConn)
- resp, err := http.ReadResponse(br, connectReq)
- if err != nil {
- return nil, nil, err
- }
- if resp.StatusCode != 200 {
- f := strings.SplitN(resp.Status, " ", 2)
- return nil, nil, errors.New(f[1])
- }
- }
-
if u.Scheme == "https" {
cfg := cloneTLSConfig(d.TLSClientConfig)
if cfg.ServerName == "" {
@@ -329,22 +311,31 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
}
tlsConn := tls.Client(netConn, cfg)
netConn = tlsConn
- if err := tlsConn.Handshake(); err != nil {
- return nil, nil, err
+
+ var err error
+ if trace != nil {
+ err = doHandshakeWithTrace(trace, tlsConn, cfg)
+ } else {
+ err = doHandshake(tlsConn, cfg)
}
- if !cfg.InsecureSkipVerify {
- if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
- return nil, nil, err
- }
+
+ if err != nil {
+ return nil, nil, err
}
}
- conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize)
+ conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)
if err := req.Write(netConn); err != nil {
return nil, nil, err
}
+ if trace != nil && trace.GotFirstResponseByte != nil {
+ if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {
+ trace.GotFirstResponseByte()
+ }
+ }
+
resp, err := http.ReadResponse(conn.br, req)
if err != nil {
return nil, nil, err
@@ -390,3 +381,15 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
netConn = nil // to avoid close in defer.
return conn, resp, nil
}
+
+func doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error {
+ if err := tlsConn.Handshake(); err != nil {
+ return err
+ }
+ if !cfg.InsecureSkipVerify {
+ if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go
index 97e1dbac..d2a21c14 100644
--- a/vendor/github.com/gorilla/websocket/conn.go
+++ b/vendor/github.com/gorilla/websocket/conn.go
@@ -76,7 +76,7 @@ const (
// is UTF-8 encoded text.
PingMessage = 9
- // PongMessage denotes a ping control message. The optional message payload
+ // PongMessage denotes a pong control message. The optional message payload
// is UTF-8 encoded text.
PongMessage = 10
)
@@ -100,9 +100,8 @@ func (e *netError) Error() string { return e.msg }
func (e *netError) Temporary() bool { return e.temporary }
func (e *netError) Timeout() bool { return e.timeout }
-// CloseError represents close frame.
+// CloseError represents a close message.
type CloseError struct {
-
// Code is defined in RFC 6455, section 11.7.
Code int
@@ -224,6 +223,20 @@ func isValidReceivedCloseCode(code int) bool {
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
}
+// BufferPool represents a pool of buffers. The *sync.Pool type satisfies this
+// interface. The type of the value stored in a pool is not specified.
+type BufferPool interface {
+ // Get gets a value from the pool or returns nil if the pool is empty.
+ Get() interface{}
+ // Put adds a value to the pool.
+ Put(interface{})
+}
+
+// writePoolData is the type added to the write buffer pool. This wrapper is
+// used to prevent applications from peeking at and depending on the values
+// added to the pool.
+type writePoolData struct{ buf []byte }
+
// The Conn type represents a WebSocket connection.
type Conn struct {
conn net.Conn
@@ -233,6 +246,8 @@ type Conn struct {
// Write fields
mu chan bool // used as mutex to protect write to conn
writeBuf []byte // frame is constructed in this buffer.
+ writePool BufferPool
+ writeBufSize int
writeDeadline time.Time
writer io.WriteCloser // the current writer returned to the application
isWriting bool // for best-effort concurrent write detection
@@ -264,64 +279,29 @@ type Conn struct {
newDecompressionReader func(io.Reader) io.ReadCloser
}
-func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
- return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil)
-}
-
-type writeHook struct {
- p []byte
-}
-
-func (wh *writeHook) Write(p []byte) (int, error) {
- wh.p = p
- return len(p), nil
-}
-
-func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn {
- mu := make(chan bool, 1)
- mu <- true
+func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn {
- var br *bufio.Reader
- if readBufferSize == 0 && brw != nil && brw.Reader != nil {
- // Reuse the supplied bufio.Reader if the buffer has a useful size.
- // This code assumes that peek on a reader returns
- // bufio.Reader.buf[:0].
- brw.Reader.Reset(conn)
- if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 {
- br = brw.Reader
- }
- }
if br == nil {
if readBufferSize == 0 {
readBufferSize = defaultReadBufferSize
- }
- if readBufferSize < maxControlFramePayloadSize {
+ } else if readBufferSize < maxControlFramePayloadSize {
+ // must be large enough for control frame
readBufferSize = maxControlFramePayloadSize
}
br = bufio.NewReaderSize(conn, readBufferSize)
}
- var writeBuf []byte
- if writeBufferSize == 0 && brw != nil && brw.Writer != nil {
- // Use the bufio.Writer's buffer if the buffer has a useful size. This
- // code assumes that bufio.Writer.buf[:1] is passed to the
- // bufio.Writer's underlying writer.
- var wh writeHook
- brw.Writer.Reset(&wh)
- brw.Writer.WriteByte(0)
- brw.Flush()
- if cap(wh.p) >= maxFrameHeaderSize+256 {
- writeBuf = wh.p[:cap(wh.p)]
- }
+ if writeBufferSize <= 0 {
+ writeBufferSize = defaultWriteBufferSize
}
+ writeBufferSize += maxFrameHeaderSize
- if writeBuf == nil {
- if writeBufferSize == 0 {
- writeBufferSize = defaultWriteBufferSize
- }
- writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize)
+ if writeBuf == nil && writeBufferPool == nil {
+ writeBuf = make([]byte, writeBufferSize)
}
+ mu := make(chan bool, 1)
+ mu <- true
c := &Conn{
isServer: isServer,
br: br,
@@ -329,6 +309,8 @@ func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize in
mu: mu,
readFinal: true,
writeBuf: writeBuf,
+ writePool: writeBufferPool,
+ writeBufSize: writeBufferSize,
enableWriteCompression: true,
compressionLevel: defaultCompressionLevel,
}
@@ -343,7 +325,8 @@ func (c *Conn) Subprotocol() string {
return c.subprotocol
}
-// Close closes the underlying network connection without sending or waiting for a close frame.
+// Close closes the underlying network connection without sending or waiting
+// for a close message.
func (c *Conn) Close() error {
return c.conn.Close()
}
@@ -370,7 +353,16 @@ func (c *Conn) writeFatal(err error) error {
return err
}
-func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
+func (c *Conn) read(n int) ([]byte, error) {
+ p, err := c.br.Peek(n)
+ if err == io.EOF {
+ err = errUnexpectedEOF
+ }
+ c.br.Discard(len(p))
+ return p, err
+}
+
+func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error {
<-c.mu
defer func() { c.mu <- true }()
@@ -382,15 +374,14 @@ func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
}
c.conn.SetWriteDeadline(deadline)
- for _, buf := range bufs {
- if len(buf) > 0 {
- _, err := c.conn.Write(buf)
- if err != nil {
- return c.writeFatal(err)
- }
- }
+ if len(buf1) == 0 {
+ _, err = c.conn.Write(buf0)
+ } else {
+ err = c.writeBufs(buf0, buf1)
+ }
+ if err != nil {
+ return c.writeFatal(err)
}
-
if frameType == CloseMessage {
c.writeFatal(ErrCloseSent)
}
@@ -476,7 +467,19 @@ func (c *Conn) prepWrite(messageType int) error {
c.writeErrMu.Lock()
err := c.writeErr
c.writeErrMu.Unlock()
- return err
+ if err != nil {
+ return err
+ }
+
+ if c.writeBuf == nil {
+ wpd, ok := c.writePool.Get().(writePoolData)
+ if ok {
+ c.writeBuf = wpd.buf
+ } else {
+ c.writeBuf = make([]byte, c.writeBufSize)
+ }
+ }
+ return nil
}
// NextWriter returns a writer for the next message to send. The writer's Close
@@ -484,6 +487,9 @@ func (c *Conn) prepWrite(messageType int) error {
//
// There can be at most one open writer on a connection. NextWriter closes the
// previous writer if the application has not already done so.
+//
+// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and
+// PongMessage) are supported.
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
if err := c.prepWrite(messageType); err != nil {
return nil, err
@@ -599,6 +605,10 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error {
if final {
c.writer = nil
+ if c.writePool != nil {
+ c.writePool.Put(writePoolData{buf: c.writeBuf})
+ c.writeBuf = nil
+ }
return nil
}
@@ -764,7 +774,6 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
// Read methods
func (c *Conn) advanceFrame() (int, error) {
-
// 1. Skip remainder of previous frame.
if c.readRemaining > 0 {
@@ -1033,7 +1042,7 @@ func (c *Conn) SetReadDeadline(t time.Time) error {
}
// SetReadLimit sets the maximum size for a message read from the peer. If a
-// message exceeds the limit, the connection sends a close frame to the peer
+// message exceeds the limit, the connection sends a close message to the peer
// and returns ErrReadLimit to the application.
func (c *Conn) SetReadLimit(limit int64) {
c.readLimit = limit
@@ -1046,24 +1055,22 @@ func (c *Conn) CloseHandler() func(code int, text string) error {
// SetCloseHandler sets the handler for close messages received from the peer.
// The code argument to h is the received close code or CloseNoStatusReceived
-// if the close message is empty. The default close handler sends a close frame
-// back to the peer.
+// if the close message is empty. The default close handler sends a close
+// message back to the peer.
//
-// The application must read the connection to process close messages as
-// described in the section on Control Frames above.
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// close messages as described in the section on Control Messages above.
//
-// The connection read methods return a CloseError when a close frame is
+// The connection read methods return a CloseError when a close message is
// received. Most applications should handle close messages as part of their
// normal error handling. Applications should only set a close handler when the
-// application must perform some action before sending a close frame back to
+// application must perform some action before sending a close message back to
// the peer.
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
if h == nil {
h = func(code int, text string) error {
- message := []byte{}
- if code != CloseNoStatusReceived {
- message = FormatCloseMessage(code, "")
- }
+ message := FormatCloseMessage(code, "")
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
return nil
}
@@ -1077,11 +1084,12 @@ func (c *Conn) PingHandler() func(appData string) error {
}
// SetPingHandler sets the handler for ping messages received from the peer.
-// The appData argument to h is the PING frame application data. The default
+// The appData argument to h is the PING message application data. The default
// ping handler sends a pong to the peer.
//
-// The application must read the connection to process ping messages as
-// described in the section on Control Frames above.
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// ping messages as described in the section on Control Messages above.
func (c *Conn) SetPingHandler(h func(appData string) error) {
if h == nil {
h = func(message string) error {
@@ -1103,11 +1111,12 @@ func (c *Conn) PongHandler() func(appData string) error {
}
// SetPongHandler sets the handler for pong messages received from the peer.
-// The appData argument to h is the PONG frame application data. The default
+// The appData argument to h is the PONG message application data. The default
// pong handler does nothing.
//
-// The application must read the connection to process ping messages as
-// described in the section on Control Frames above.
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// pong messages as described in the section on Control Messages above.
func (c *Conn) SetPongHandler(h func(appData string) error) {
if h == nil {
h = func(string) error { return nil }
@@ -1141,7 +1150,14 @@ func (c *Conn) SetCompressionLevel(level int) error {
}
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
+// An empty message is returned for code CloseNoStatusReceived.
func FormatCloseMessage(closeCode int, text string) []byte {
+ if closeCode == CloseNoStatusReceived {
+ // Return empty message because it's illegal to send
+ // CloseNoStatusReceived. Return non-nil value in case application
+ // checks for nil.
+ return []byte{}
+ }
buf := make([]byte, 2+len(text))
binary.BigEndian.PutUint16(buf, uint16(closeCode))
copy(buf[2:], text)
diff --git a/vendor/github.com/gorilla/websocket/conn_read_legacy.go b/vendor/github.com/gorilla/websocket/conn_read_legacy.go
deleted file mode 100644
index 018541cf..00000000
--- a/vendor/github.com/gorilla/websocket/conn_read_legacy.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !go1.5
-
-package websocket
-
-import "io"
-
-func (c *Conn) read(n int) ([]byte, error) {
- p, err := c.br.Peek(n)
- if err == io.EOF {
- err = errUnexpectedEOF
- }
- if len(p) > 0 {
- // advance over the bytes just read
- io.ReadFull(c.br, p)
- }
- return p, err
-}
diff --git a/vendor/github.com/gorilla/websocket/conn_read.go b/vendor/github.com/gorilla/websocket/conn_write.go
index 1ea15059..a509a21f 100644
--- a/vendor/github.com/gorilla/websocket/conn_read.go
+++ b/vendor/github.com/gorilla/websocket/conn_write.go
@@ -2,17 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build go1.5
+// +build go1.8
package websocket
-import "io"
+import "net"
-func (c *Conn) read(n int) ([]byte, error) {
- p, err := c.br.Peek(n)
- if err == io.EOF {
- err = errUnexpectedEOF
- }
- c.br.Discard(len(p))
- return p, err
+func (c *Conn) writeBufs(bufs ...[]byte) error {
+ b := net.Buffers(bufs)
+ _, err := b.WriteTo(c.conn)
+ return err
}
diff --git a/vendor/github.com/gorilla/websocket/conn_write_legacy.go b/vendor/github.com/gorilla/websocket/conn_write_legacy.go
new file mode 100644
index 00000000..37edaff5
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/conn_write_legacy.go
@@ -0,0 +1,18 @@
+// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.8
+
+package websocket
+
+func (c *Conn) writeBufs(bufs ...[]byte) error {
+ for _, buf := range bufs {
+ if len(buf) > 0 {
+ if _, err := c.conn.Write(buf); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go
index e291a952..dcce1a63 100644
--- a/vendor/github.com/gorilla/websocket/doc.go
+++ b/vendor/github.com/gorilla/websocket/doc.go
@@ -6,9 +6,8 @@
//
// Overview
//
-// The Conn type represents a WebSocket connection. A server application uses
-// the Upgrade function from an Upgrader object with a HTTP request handler
-// to get a pointer to a Conn:
+// The Conn type represents a WebSocket connection. A server application calls
+// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:
//
// var upgrader = websocket.Upgrader{
// ReadBufferSize: 1024,
@@ -31,10 +30,12 @@
// for {
// messageType, p, err := conn.ReadMessage()
// if err != nil {
+// log.Println(err)
// return
// }
-// if err = conn.WriteMessage(messageType, p); err != nil {
-// return err
+// if err := conn.WriteMessage(messageType, p); err != nil {
+// log.Println(err)
+// return
// }
// }
//
@@ -85,20 +86,26 @@
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
// methods to send a control message to the peer.
//
-// Connections handle received close messages by sending a close message to the
-// peer and returning a *CloseError from the the NextReader, ReadMessage or the
-// message Read method.
+// Connections handle received close messages by calling the handler function
+// set with the SetCloseHandler method and by returning a *CloseError from the
+// NextReader, ReadMessage or the message Read method. The default close
+// handler sends a close message to the peer.
//
-// Connections handle received ping and pong messages by invoking callback
-// functions set with SetPingHandler and SetPongHandler methods. The callback
-// functions are called from the NextReader, ReadMessage and the message Read
-// methods.
+// Connections handle received ping messages by calling the handler function
+// set with the SetPingHandler method. The default ping handler sends a pong
+// message to the peer.
//
-// The default ping handler sends a pong to the peer. The application's reading
-// goroutine can block for a short time while the handler writes the pong data
-// to the connection.
+// Connections handle received pong messages by calling the handler function
+// set with the SetPongHandler method. The default pong handler does nothing.
+// If an application sends ping messages, then the application should set a
+// pong handler to receive the corresponding pong.
//
-// The application must read the connection to process ping, pong and close
+// The control message handler functions are called from the NextReader,
+// ReadMessage and message reader Read methods. The default close and ping
+// handlers can block these methods for a short time when the handler writes to
+// the connection.
+//
+// The application must read the connection to process close, ping and pong
// messages sent from the peer. If the application is not otherwise interested
// in messages from the peer, then the application should start a goroutine to
// read and discard messages from the peer. A simple example is:
@@ -137,19 +144,12 @@
// method fails the WebSocket handshake with HTTP status 403.
//
// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
-// the handshake if the Origin request header is present and not equal to the
-// Host request header.
-//
-// An application can allow connections from any origin by specifying a
-// function that always returns true:
-//
-// var upgrader = websocket.Upgrader{
-// CheckOrigin: func(r *http.Request) bool { return true },
-// }
+// the handshake if the Origin request header is present and the Origin host is
+// not equal to the Host request header.
//
-// The deprecated Upgrade function does not enforce an origin policy. It's the
-// application's responsibility to check the Origin header before calling
-// Upgrade.
+// The deprecated package-level Upgrade function does not perform origin
+// checking. The application is responsible for checking the Origin header
+// before calling the Upgrade function.
//
// Compression EXPERIMENTAL
//
diff --git a/vendor/github.com/gorilla/websocket/json.go b/vendor/github.com/gorilla/websocket/json.go
index 4f0e3687..dc2c1f64 100644
--- a/vendor/github.com/gorilla/websocket/json.go
+++ b/vendor/github.com/gorilla/websocket/json.go
@@ -9,12 +9,14 @@ import (
"io"
)
-// WriteJSON is deprecated, use c.WriteJSON instead.
+// WriteJSON writes the JSON encoding of v as a message.
+//
+// Deprecated: Use c.WriteJSON instead.
func WriteJSON(c *Conn, v interface{}) error {
return c.WriteJSON(v)
}
-// WriteJSON writes the JSON encoding of v to the connection.
+// WriteJSON writes the JSON encoding of v as a message.
//
// See the documentation for encoding/json Marshal for details about the
// conversion of Go values to JSON.
@@ -31,7 +33,10 @@ func (c *Conn) WriteJSON(v interface{}) error {
return err2
}
-// ReadJSON is deprecated, use c.ReadJSON instead.
+// ReadJSON reads the next JSON-encoded message from the connection and stores
+// it in the value pointed to by v.
+//
+// Deprecated: Use c.ReadJSON instead.
func ReadJSON(c *Conn, v interface{}) error {
return c.ReadJSON(v)
}
diff --git a/vendor/github.com/gorilla/websocket/mask.go b/vendor/github.com/gorilla/websocket/mask.go
index 6a88bbc7..577fce9e 100644
--- a/vendor/github.com/gorilla/websocket/mask.go
+++ b/vendor/github.com/gorilla/websocket/mask.go
@@ -11,7 +11,6 @@ import "unsafe"
const wordSize = int(unsafe.Sizeof(uintptr(0)))
func maskBytes(key [4]byte, pos int, b []byte) int {
-
// Mask one byte at a time for small buffers.
if len(b) < 2*wordSize {
for i := range b {
diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go
index 1efffbd1..74ec565d 100644
--- a/vendor/github.com/gorilla/websocket/prepared.go
+++ b/vendor/github.com/gorilla/websocket/prepared.go
@@ -19,7 +19,6 @@ import (
type PreparedMessage struct {
messageType int
data []byte
- err error
mu sync.Mutex
frames map[prepareKey]*preparedFrame
}
diff --git a/vendor/github.com/gorilla/websocket/proxy.go b/vendor/github.com/gorilla/websocket/proxy.go
new file mode 100644
index 00000000..bf2478e4
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/proxy.go
@@ -0,0 +1,77 @@
+// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "bufio"
+ "encoding/base64"
+ "errors"
+ "net"
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+type netDialerFunc func(network, addr string) (net.Conn, error)
+
+func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
+ return fn(network, addr)
+}
+
+func init() {
+ proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
+ return &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil
+ })
+}
+
+type httpProxyDialer struct {
+ proxyURL *url.URL
+ fowardDial func(network, addr string) (net.Conn, error)
+}
+
+func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
+ hostPort, _ := hostPortNoPort(hpd.proxyURL)
+ conn, err := hpd.fowardDial(network, hostPort)
+ if err != nil {
+ return nil, err
+ }
+
+ connectHeader := make(http.Header)
+ if user := hpd.proxyURL.User; user != nil {
+ proxyUser := user.Username()
+ if proxyPassword, passwordSet := user.Password(); passwordSet {
+ credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
+ connectHeader.Set("Proxy-Authorization", "Basic "+credential)
+ }
+ }
+
+ connectReq := &http.Request{
+ Method: "CONNECT",
+ URL: &url.URL{Opaque: addr},
+ Host: addr,
+ Header: connectHeader,
+ }
+
+ if err := connectReq.Write(conn); err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ // Read response. It's OK to use and discard buffered reader here becaue
+ // the remote server does not speak until spoken to.
+ br := bufio.NewReader(conn)
+ resp, err := http.ReadResponse(br, connectReq)
+ if err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ if resp.StatusCode != 200 {
+ conn.Close()
+ f := strings.SplitN(resp.Status, " ", 2)
+ return nil, errors.New(f[1])
+ }
+ return conn, nil
+}
diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go
index 3495e0f1..a761824b 100644
--- a/vendor/github.com/gorilla/websocket/server.go
+++ b/vendor/github.com/gorilla/websocket/server.go
@@ -7,7 +7,7 @@ package websocket
import (
"bufio"
"errors"
- "net"
+ "io"
"net/http"
"net/url"
"strings"
@@ -33,10 +33,23 @@ type Upgrader struct {
// or received.
ReadBufferSize, WriteBufferSize int
+ // WriteBufferPool is a pool of buffers for write operations. If the value
+ // is not set, then write buffers are allocated to the connection for the
+ // lifetime of the connection.
+ //
+ // A pool is most useful when the application has a modest volume of writes
+ // across a large number of connections.
+ //
+ // Applications should use a single pool for each unique value of
+ // WriteBufferSize.
+ WriteBufferPool BufferPool
+
// Subprotocols specifies the server's supported protocols in order of
- // preference. If this field is set, then the Upgrade method negotiates a
+ // preference. If this field is not nil, then the Upgrade method negotiates a
// subprotocol by selecting the first match in this list with a protocol
- // requested by the client.
+ // requested by the client. If there's no match, then no protocol is
+ // negotiated (the Sec-Websocket-Protocol header is not included in the
+ // handshake response).
Subprotocols []string
// Error specifies the function for generating HTTP error responses. If Error
@@ -44,8 +57,12 @@ type Upgrader struct {
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
// CheckOrigin returns true if the request Origin header is acceptable. If
- // CheckOrigin is nil, the host in the Origin header must not be set or
- // must match the host of the request.
+ // CheckOrigin is nil, then a safe default is used: return false if the
+ // Origin request header is present and the origin host is not equal to
+ // request Host header.
+ //
+ // A CheckOrigin function should carefully validate the request origin to
+ // prevent cross-site request forgery.
CheckOrigin func(r *http.Request) bool
// EnableCompression specify if the server should attempt to negotiate per
@@ -76,7 +93,7 @@ func checkSameOrigin(r *http.Request) bool {
if err != nil {
return false
}
- return u.Host == r.Host
+ return equalASCIIFold(u.Host, r.Host)
}
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
@@ -99,42 +116,44 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
//
// The responseHeader is included in the response to the client's upgrade
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
-// application negotiated subprotocol (Sec-Websocket-Protocol).
+// application negotiated subprotocol (Sec-WebSocket-Protocol).
//
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
// response.
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
- if r.Method != "GET" {
- return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
- }
-
- if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
- return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
- }
+ const badHandshake = "websocket: the client is not using the websocket protocol: "
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
- return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
+ return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header")
}
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
- return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
+ return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
+ }
+
+ if r.Method != "GET" {
+ return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
}
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
}
+ if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
+ return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported")
+ }
+
checkOrigin := u.CheckOrigin
if checkOrigin == nil {
checkOrigin = checkSameOrigin
}
if !checkOrigin(r) {
- return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
+ return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin")
}
challengeKey := r.Header.Get("Sec-Websocket-Key")
if challengeKey == "" {
- return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank")
+ return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-WebSocket-Key' header is missing or blank")
}
subprotocol := u.selectSubprotocol(r, responseHeader)
@@ -151,17 +170,12 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
}
}
- var (
- netConn net.Conn
- err error
- )
-
h, ok := w.(http.Hijacker)
if !ok {
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
}
var brw *bufio.ReadWriter
- netConn, brw, err = h.Hijack()
+ netConn, brw, err := h.Hijack()
if err != nil {
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
}
@@ -171,7 +185,21 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
return nil, errors.New("websocket: client sent data before handshake is complete")
}
- c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw)
+ var br *bufio.Reader
+ if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 {
+ // Reuse hijacked buffered reader as connection reader.
+ br = brw.Reader
+ }
+
+ buf := bufioWriterBuffer(netConn, brw.Writer)
+
+ var writeBuf []byte
+ if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {
+ // Reuse hijacked write buffer as connection buffer.
+ writeBuf = buf
+ }
+
+ c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)
c.subprotocol = subprotocol
if compress {
@@ -179,17 +207,23 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
c.newDecompressionReader = decompressNoContextTakeover
}
- p := c.writeBuf[:0]
+ // Use larger of hijacked buffer and connection write buffer for header.
+ p := buf
+ if len(c.writeBuf) > len(p) {
+ p = c.writeBuf
+ }
+ p = p[:0]
+
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
p = append(p, computeAcceptKey(challengeKey)...)
p = append(p, "\r\n"...)
if c.subprotocol != "" {
- p = append(p, "Sec-Websocket-Protocol: "...)
+ p = append(p, "Sec-WebSocket-Protocol: "...)
p = append(p, c.subprotocol...)
p = append(p, "\r\n"...)
}
if compress {
- p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
+ p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
}
for k, vs := range responseHeader {
if k == "Sec-Websocket-Protocol" {
@@ -230,13 +264,14 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
//
-// This function is deprecated, use websocket.Upgrader instead.
+// Deprecated: Use websocket.Upgrader instead.
//
-// The application is responsible for checking the request origin before
-// calling Upgrade. An example implementation of the same origin policy is:
+// Upgrade does not perform origin checking. The application is responsible for
+// checking the Origin header before calling Upgrade. An example implementation
+// of the same origin policy check is:
//
// if req.Header.Get("Origin") != "http://"+req.Host {
-// http.Error(w, "Origin not allowed", 403)
+// http.Error(w, "Origin not allowed", http.StatusForbidden)
// return
// }
//
@@ -289,3 +324,40 @@ func IsWebSocketUpgrade(r *http.Request) bool {
return tokenListContainsValue(r.Header, "Connection", "upgrade") &&
tokenListContainsValue(r.Header, "Upgrade", "websocket")
}
+
+// bufioReaderSize size returns the size of a bufio.Reader.
+func bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int {
+ // This code assumes that peek on a reset reader returns
+ // bufio.Reader.buf[:0].
+ // TODO: Use bufio.Reader.Size() after Go 1.10
+ br.Reset(originalReader)
+ if p, err := br.Peek(0); err == nil {
+ return cap(p)
+ }
+ return 0
+}
+
+// writeHook is an io.Writer that records the last slice passed to it vio
+// io.Writer.Write.
+type writeHook struct {
+ p []byte
+}
+
+func (wh *writeHook) Write(p []byte) (int, error) {
+ wh.p = p
+ return len(p), nil
+}
+
+// bufioWriterBuffer grabs the buffer from a bufio.Writer.
+func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {
+ // This code assumes that bufio.Writer.buf[:1] is passed to the
+ // bufio.Writer's underlying writer.
+ var wh writeHook
+ bw.Reset(&wh)
+ bw.WriteByte(0)
+ bw.Flush()
+
+ bw.Reset(originalWriter)
+
+ return wh.p[:cap(wh.p)]
+}
diff --git a/vendor/github.com/gorilla/websocket/trace.go b/vendor/github.com/gorilla/websocket/trace.go
new file mode 100644
index 00000000..834f122a
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/trace.go
@@ -0,0 +1,19 @@
+// +build go1.8
+
+package websocket
+
+import (
+ "crypto/tls"
+ "net/http/httptrace"
+)
+
+func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {
+ if trace.TLSHandshakeStart != nil {
+ trace.TLSHandshakeStart()
+ }
+ err := doHandshake(tlsConn, cfg)
+ if trace.TLSHandshakeDone != nil {
+ trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
+ }
+ return err
+}
diff --git a/vendor/github.com/gorilla/websocket/trace_17.go b/vendor/github.com/gorilla/websocket/trace_17.go
new file mode 100644
index 00000000..77d05a0b
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/trace_17.go
@@ -0,0 +1,12 @@
+// +build !go1.8
+
+package websocket
+
+import (
+ "crypto/tls"
+ "net/http/httptrace"
+)
+
+func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {
+ return doHandshake(tlsConn, cfg)
+}
diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go
index 9a4908df..354001e1 100644
--- a/vendor/github.com/gorilla/websocket/util.go
+++ b/vendor/github.com/gorilla/websocket/util.go
@@ -11,6 +11,7 @@ import (
"io"
"net/http"
"strings"
+ "unicode/utf8"
)
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
@@ -111,14 +112,14 @@ func nextTokenOrQuoted(s string) (value string, rest string) {
case escape:
escape = false
p[j] = b
- j += 1
+ j++
case b == '\\':
escape = true
case b == '"':
return string(p[:j]), s[i+1:]
default:
p[j] = b
- j += 1
+ j++
}
}
return "", ""
@@ -127,8 +128,31 @@ func nextTokenOrQuoted(s string) (value string, rest string) {
return "", ""
}
+// equalASCIIFold returns true if s is equal to t with ASCII case folding.
+func equalASCIIFold(s, t string) bool {
+ for s != "" && t != "" {
+ sr, size := utf8.DecodeRuneInString(s)
+ s = s[size:]
+ tr, size := utf8.DecodeRuneInString(t)
+ t = t[size:]
+ if sr == tr {
+ continue
+ }
+ if 'A' <= sr && sr <= 'Z' {
+ sr = sr + 'a' - 'A'
+ }
+ if 'A' <= tr && tr <= 'Z' {
+ tr = tr + 'a' - 'A'
+ }
+ if sr != tr {
+ return false
+ }
+ }
+ return s == t
+}
+
// tokenListContainsValue returns true if the 1#token header with the given
-// name contains token.
+// name contains a token equal to value with ASCII case folding.
func tokenListContainsValue(header http.Header, name string, value string) bool {
headers:
for _, s := range header[name] {
@@ -142,7 +166,7 @@ headers:
if s != "" && s[0] != ',' {
continue headers
}
- if strings.EqualFold(t, value) {
+ if equalASCIIFold(t, value) {
return true
}
if s == "" {
@@ -154,9 +178,8 @@ headers:
return false
}
-// parseExtensiosn parses WebSocket extensions from a header.
+// parseExtensions parses WebSocket extensions from a header.
func parseExtensions(header http.Header) []map[string]string {
-
// From RFC 6455:
//
// Sec-WebSocket-Extensions = extension-list
diff --git a/vendor/github.com/gorilla/websocket/x_net_proxy.go b/vendor/github.com/gorilla/websocket/x_net_proxy.go
new file mode 100644
index 00000000..2e668f6b
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/x_net_proxy.go
@@ -0,0 +1,473 @@
+// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
+//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
+
+// Package proxy provides support for a variety of protocols to proxy network
+// data.
+//
+
+package websocket
+
+import (
+ "errors"
+ "io"
+ "net"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+type proxy_direct struct{}
+
+// Direct is a direct proxy: one that makes network connections directly.
+var proxy_Direct = proxy_direct{}
+
+func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
+ return net.Dial(network, addr)
+}
+
+// A PerHost directs connections to a default Dialer unless the host name
+// requested matches one of a number of exceptions.
+type proxy_PerHost struct {
+ def, bypass proxy_Dialer
+
+ bypassNetworks []*net.IPNet
+ bypassIPs []net.IP
+ bypassZones []string
+ bypassHosts []string
+}
+
+// NewPerHost returns a PerHost Dialer that directs connections to either
+// defaultDialer or bypass, depending on whether the connection matches one of
+// the configured rules.
+func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
+ return &proxy_PerHost{
+ def: defaultDialer,
+ bypass: bypass,
+ }
+}
+
+// Dial connects to the address addr on the given network through either
+// defaultDialer or bypass.
+func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
+ host, _, err := net.SplitHostPort(addr)
+ if err != nil {
+ return nil, err
+ }
+
+ return p.dialerForRequest(host).Dial(network, addr)
+}
+
+func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
+ if ip := net.ParseIP(host); ip != nil {
+ for _, net := range p.bypassNetworks {
+ if net.Contains(ip) {
+ return p.bypass
+ }
+ }
+ for _, bypassIP := range p.bypassIPs {
+ if bypassIP.Equal(ip) {
+ return p.bypass
+ }
+ }
+ return p.def
+ }
+
+ for _, zone := range p.bypassZones {
+ if strings.HasSuffix(host, zone) {
+ return p.bypass
+ }
+ if host == zone[1:] {
+ // For a zone ".example.com", we match "example.com"
+ // too.
+ return p.bypass
+ }
+ }
+ for _, bypassHost := range p.bypassHosts {
+ if bypassHost == host {
+ return p.bypass
+ }
+ }
+ return p.def
+}
+
+// AddFromString parses a string that contains comma-separated values
+// specifying hosts that should use the bypass proxy. Each value is either an
+// IP address, a CIDR range, a zone (*.example.com) or a host name
+// (localhost). A best effort is made to parse the string and errors are
+// ignored.
+func (p *proxy_PerHost) AddFromString(s string) {
+ hosts := strings.Split(s, ",")
+ for _, host := range hosts {
+ host = strings.TrimSpace(host)
+ if len(host) == 0 {
+ continue
+ }
+ if strings.Contains(host, "/") {
+ // We assume that it's a CIDR address like 127.0.0.0/8
+ if _, net, err := net.ParseCIDR(host); err == nil {
+ p.AddNetwork(net)
+ }
+ continue
+ }
+ if ip := net.ParseIP(host); ip != nil {
+ p.AddIP(ip)
+ continue
+ }
+ if strings.HasPrefix(host, "*.") {
+ p.AddZone(host[1:])
+ continue
+ }
+ p.AddHost(host)
+ }
+}
+
+// AddIP specifies an IP address that will use the bypass proxy. Note that
+// this will only take effect if a literal IP address is dialed. A connection
+// to a named host will never match an IP.
+func (p *proxy_PerHost) AddIP(ip net.IP) {
+ p.bypassIPs = append(p.bypassIPs, ip)
+}
+
+// AddNetwork specifies an IP range that will use the bypass proxy. Note that
+// this will only take effect if a literal IP address is dialed. A connection
+// to a named host will never match.
+func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
+ p.bypassNetworks = append(p.bypassNetworks, net)
+}
+
+// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
+// "example.com" matches "example.com" and all of its subdomains.
+func (p *proxy_PerHost) AddZone(zone string) {
+ if strings.HasSuffix(zone, ".") {
+ zone = zone[:len(zone)-1]
+ }
+ if !strings.HasPrefix(zone, ".") {
+ zone = "." + zone
+ }
+ p.bypassZones = append(p.bypassZones, zone)
+}
+
+// AddHost specifies a host name that will use the bypass proxy.
+func (p *proxy_PerHost) AddHost(host string) {
+ if strings.HasSuffix(host, ".") {
+ host = host[:len(host)-1]
+ }
+ p.bypassHosts = append(p.bypassHosts, host)
+}
+
+// A Dialer is a means to establish a connection.
+type proxy_Dialer interface {
+ // Dial connects to the given address via the proxy.
+ Dial(network, addr string) (c net.Conn, err error)
+}
+
+// Auth contains authentication parameters that specific Dialers may require.
+type proxy_Auth struct {
+ User, Password string
+}
+
+// FromEnvironment returns the dialer specified by the proxy related variables in
+// the environment.
+func proxy_FromEnvironment() proxy_Dialer {
+ allProxy := proxy_allProxyEnv.Get()
+ if len(allProxy) == 0 {
+ return proxy_Direct
+ }
+
+ proxyURL, err := url.Parse(allProxy)
+ if err != nil {
+ return proxy_Direct
+ }
+ proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
+ if err != nil {
+ return proxy_Direct
+ }
+
+ noProxy := proxy_noProxyEnv.Get()
+ if len(noProxy) == 0 {
+ return proxy
+ }
+
+ perHost := proxy_NewPerHost(proxy, proxy_Direct)
+ perHost.AddFromString(noProxy)
+ return perHost
+}
+
+// proxySchemes is a map from URL schemes to a function that creates a Dialer
+// from a URL with such a scheme.
+var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
+
+// RegisterDialerType takes a URL scheme and a function to generate Dialers from
+// a URL with that scheme and a forwarding Dialer. Registered schemes are used
+// by FromURL.
+func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
+ if proxy_proxySchemes == nil {
+ proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
+ }
+ proxy_proxySchemes[scheme] = f
+}
+
+// FromURL returns a Dialer given a URL specification and an underlying
+// Dialer for it to make network requests.
+func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
+ var auth *proxy_Auth
+ if u.User != nil {
+ auth = new(proxy_Auth)
+ auth.User = u.User.Username()
+ if p, ok := u.User.Password(); ok {
+ auth.Password = p
+ }
+ }
+
+ switch u.Scheme {
+ case "socks5":
+ return proxy_SOCKS5("tcp", u.Host, auth, forward)
+ }
+
+ // If the scheme doesn't match any of the built-in schemes, see if it
+ // was registered by another package.
+ if proxy_proxySchemes != nil {
+ if f, ok := proxy_proxySchemes[u.Scheme]; ok {
+ return f(u, forward)
+ }
+ }
+
+ return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
+}
+
+var (
+ proxy_allProxyEnv = &proxy_envOnce{
+ names: []string{"ALL_PROXY", "all_proxy"},
+ }
+ proxy_noProxyEnv = &proxy_envOnce{
+ names: []string{"NO_PROXY", "no_proxy"},
+ }
+)
+
+// envOnce looks up an environment variable (optionally by multiple
+// names) once. It mitigates expensive lookups on some platforms
+// (e.g. Windows).
+// (Borrowed from net/http/transport.go)
+type proxy_envOnce struct {
+ names []string
+ once sync.Once
+ val string
+}
+
+func (e *proxy_envOnce) Get() string {
+ e.once.Do(e.init)
+ return e.val
+}
+
+func (e *proxy_envOnce) init() {
+ for _, n := range e.names {
+ e.val = os.Getenv(n)
+ if e.val != "" {
+ return
+ }
+ }
+}
+
+// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
+// with an optional username and password. See RFC 1928 and RFC 1929.
+func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
+ s := &proxy_socks5{
+ network: network,
+ addr: addr,
+ forward: forward,
+ }
+ if auth != nil {
+ s.user = auth.User
+ s.password = auth.Password
+ }
+
+ return s, nil
+}
+
+type proxy_socks5 struct {
+ user, password string
+ network, addr string
+ forward proxy_Dialer
+}
+
+const proxy_socks5Version = 5
+
+const (
+ proxy_socks5AuthNone = 0
+ proxy_socks5AuthPassword = 2
+)
+
+const proxy_socks5Connect = 1
+
+const (
+ proxy_socks5IP4 = 1
+ proxy_socks5Domain = 3
+ proxy_socks5IP6 = 4
+)
+
+var proxy_socks5Errors = []string{
+ "",
+ "general failure",
+ "connection forbidden",
+ "network unreachable",
+ "host unreachable",
+ "connection refused",
+ "TTL expired",
+ "command not supported",
+ "address type not supported",
+}
+
+// Dial connects to the address addr on the given network via the SOCKS5 proxy.
+func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
+ switch network {
+ case "tcp", "tcp6", "tcp4":
+ default:
+ return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
+ }
+
+ conn, err := s.forward.Dial(s.network, s.addr)
+ if err != nil {
+ return nil, err
+ }
+ if err := s.connect(conn, addr); err != nil {
+ conn.Close()
+ return nil, err
+ }
+ return conn, nil
+}
+
+// connect takes an existing connection to a socks5 proxy server,
+// and commands the server to extend that connection to target,
+// which must be a canonical address with a host and port.
+func (s *proxy_socks5) connect(conn net.Conn, target string) error {
+ host, portStr, err := net.SplitHostPort(target)
+ if err != nil {
+ return err
+ }
+
+ port, err := strconv.Atoi(portStr)
+ if err != nil {
+ return errors.New("proxy: failed to parse port number: " + portStr)
+ }
+ if port < 1 || port > 0xffff {
+ return errors.New("proxy: port number out of range: " + portStr)
+ }
+
+ // the size here is just an estimate
+ buf := make([]byte, 0, 6+len(host))
+
+ buf = append(buf, proxy_socks5Version)
+ if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
+ buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
+ } else {
+ buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
+ }
+
+ if _, err := conn.Write(buf); err != nil {
+ return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+ if buf[0] != 5 {
+ return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
+ }
+ if buf[1] == 0xff {
+ return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
+ }
+
+ // See RFC 1929
+ if buf[1] == proxy_socks5AuthPassword {
+ buf = buf[:0]
+ buf = append(buf, 1 /* password protocol version */)
+ buf = append(buf, uint8(len(s.user)))
+ buf = append(buf, s.user...)
+ buf = append(buf, uint8(len(s.password)))
+ buf = append(buf, s.password...)
+
+ if _, err := conn.Write(buf); err != nil {
+ return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if buf[1] != 0 {
+ return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
+ }
+ }
+
+ buf = buf[:0]
+ buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
+
+ if ip := net.ParseIP(host); ip != nil {
+ if ip4 := ip.To4(); ip4 != nil {
+ buf = append(buf, proxy_socks5IP4)
+ ip = ip4
+ } else {
+ buf = append(buf, proxy_socks5IP6)
+ }
+ buf = append(buf, ip...)
+ } else {
+ if len(host) > 255 {
+ return errors.New("proxy: destination host name too long: " + host)
+ }
+ buf = append(buf, proxy_socks5Domain)
+ buf = append(buf, byte(len(host)))
+ buf = append(buf, host...)
+ }
+ buf = append(buf, byte(port>>8), byte(port))
+
+ if _, err := conn.Write(buf); err != nil {
+ return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:4]); err != nil {
+ return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ failure := "unknown error"
+ if int(buf[1]) < len(proxy_socks5Errors) {
+ failure = proxy_socks5Errors[buf[1]]
+ }
+
+ if len(failure) > 0 {
+ return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
+ }
+
+ bytesToDiscard := 0
+ switch buf[3] {
+ case proxy_socks5IP4:
+ bytesToDiscard = net.IPv4len
+ case proxy_socks5IP6:
+ bytesToDiscard = net.IPv6len
+ case proxy_socks5Domain:
+ _, err := io.ReadFull(conn, buf[:1])
+ if err != nil {
+ return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+ bytesToDiscard = int(buf[0])
+ default:
+ return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
+ }
+
+ if cap(buf) < bytesToDiscard {
+ buf = make([]byte, bytesToDiscard)
+ } else {
+ buf = buf[:bytesToDiscard]
+ }
+ if _, err := io.ReadFull(conn, buf); err != nil {
+ return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ // Also need to discard the port number
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/matterbridge/discordgo/user.go b/vendor/github.com/matterbridge/discordgo/user.go
deleted file mode 100644
index a710f286..00000000
--- a/vendor/github.com/matterbridge/discordgo/user.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package discordgo
-
-import (
- "fmt"
- "strings"
-)
-
-// A User stores all data for an individual Discord user.
-type User struct {
- ID string `json:"id"`
- Email string `json:"email"`
- Username string `json:"username"`
- Avatar string `json:"avatar"`
- Discriminator string `json:"discriminator"`
- Token string `json:"token"`
- Verified bool `json:"verified"`
- MFAEnabled bool `json:"mfa_enabled"`
- Bot bool `json:"bot"`
-}
-
-// String returns a unique identifier of the form username#discriminator
-func (u *User) String() string {
- return fmt.Sprintf("%s#%s", u.Username, u.Discriminator)
-}
-
-// Mention return a string which mentions the user
-func (u *User) Mention() string {
- return fmt.Sprintf("<@%s>", 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 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
-}