summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--bridge/bridge.go3
-rw-r--r--bridge/config/config.go6
-rw-r--r--changelog.md1
-rw-r--r--gateway/gateway.go2
-rw-r--r--matterbridge.toml.sample29
-rw-r--r--vendor/github.com/bwmarrin/discordgo/LICENSE28
-rw-r--r--vendor/github.com/bwmarrin/discordgo/discord.go257
-rw-r--r--vendor/github.com/bwmarrin/discordgo/endpoints.go99
-rw-r--r--vendor/github.com/bwmarrin/discordgo/events.go159
-rw-r--r--vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go186
-rw-r--r--vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go98
-rw-r--r--vendor/github.com/bwmarrin/discordgo/examples/avatar/localfile/main.go73
-rw-r--r--vendor/github.com/bwmarrin/discordgo/examples/avatar/url/main.go86
-rw-r--r--vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go33
-rw-r--r--vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go58
-rw-r--r--vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go78
-rw-r--r--vendor/github.com/bwmarrin/discordgo/logging.go95
-rw-r--r--vendor/github.com/bwmarrin/discordgo/message.go82
-rw-r--r--vendor/github.com/bwmarrin/discordgo/oauth2.go120
-rw-r--r--vendor/github.com/bwmarrin/discordgo/restapi.go1403
-rw-r--r--vendor/github.com/bwmarrin/discordgo/state.go746
-rw-r--r--vendor/github.com/bwmarrin/discordgo/structs.go521
-rw-r--r--vendor/github.com/bwmarrin/discordgo/voice.go853
-rw-r--r--vendor/github.com/bwmarrin/discordgo/wsapi.go679
-rw-r--r--vendor/golang.org/x/crypto/poly1305/LICENSE27
-rw-r--r--vendor/golang.org/x/crypto/poly1305/const_amd64.s45
-rw-r--r--vendor/golang.org/x/crypto/poly1305/poly1305.go32
-rw-r--r--vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s497
-rw-r--r--vendor/golang.org/x/crypto/poly1305/poly1305_arm.s379
-rw-r--r--vendor/golang.org/x/crypto/poly1305/sum_amd64.go24
-rw-r--r--vendor/golang.org/x/crypto/poly1305/sum_arm.go24
-rw-r--r--vendor/golang.org/x/crypto/poly1305/sum_ref.go1531
-rw-r--r--vendor/golang.org/x/crypto/salsa20/salsa/LICENSE27
-rw-r--r--vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go144
-rw-r--r--vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s902
-rw-r--r--vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go199
-rw-r--r--vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go23
-rw-r--r--vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go234
-rw-r--r--vendor/manifest35
40 files changed, 9819 insertions, 4 deletions
diff --git a/README.md b/README.md
index c9a9fa56..8d147902 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,9 @@
:warning: Look at [README-0.6.md] (https://github.com/42wim/matterbridge/blob/master/README-0.6.md) for the documentation of the current stable.
The information below is about the develop version.
-Simple bridge between mattermost, IRC, XMPP, Gitter and Slack
+Simple bridge between mattermost, IRC, XMPP, Gitter, Slack and Discord
-* Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter and Slack. Pick and mix.
+* Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter, Slack and Discord. Pick and mix.
* Supports multiple channels.
* Matterbridge can also work with private groups on your mattermost.
* Allow for bridging the same bridges, which means you can eg bridge between multiple mattermosts.
@@ -24,6 +24,7 @@ Accounts to one of the supported bridges
* [XMPP] (https://jabber.org)
* [Gitter] (https://gitter.im)
* [Slack] (https://slack.com)
+* [Discord] (https://discordapp.com)
## Docker
Create your matterbridge.toml file locally eg in ```/tmp/matterbridge.toml```
diff --git a/bridge/bridge.go b/bridge/bridge.go
index 719af4fb..7f08c609 100644
--- a/bridge/bridge.go
+++ b/bridge/bridge.go
@@ -2,6 +2,7 @@ package bridge
import (
"github.com/42wim/matterbridge/bridge/config"
+ "github.com/42wim/matterbridge/bridge/discord"
"github.com/42wim/matterbridge/bridge/gitter"
"github.com/42wim/matterbridge/bridge/irc"
"github.com/42wim/matterbridge/bridge/mattermost"
@@ -35,6 +36,8 @@ func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) Bridg
return bslack.New(cfg.Slack[name], name, c)
case "xmpp":
return bxmpp.New(cfg.Xmpp[name], name, c)
+ case "discord":
+ return bdiscord.New(cfg.Discord[name], name, c)
}
return nil
}
diff --git a/bridge/config/config.go b/bridge/config/config.go
index ad8a523e..e3b7b52f 100644
--- a/bridge/config/config.go
+++ b/bridge/config/config.go
@@ -16,6 +16,7 @@ type Message struct {
type Protocol struct {
BindAddress string // mattermost, slack
+ Guild string // discord
IconURL string // mattermost, slack
IgnoreNicks string // all protocols
Jid string // xmpp
@@ -32,11 +33,11 @@ type Protocol struct {
PrefixMessagesWithNick bool // mattemost, slack
Protocol string //all protocols
RemoteNickFormat string // all protocols
- Server string // IRC,mattermost,XMPP
+ Server string // IRC,mattermost,XMPP,discord
ShowJoinPart bool // all protocols
SkipTLSVerify bool // IRC, mattermost
Team string // mattermost
- Token string // gitter, slack
+ Token string // gitter, slack, discord
URL string // mattermost, slack
UseAPI bool // mattermost, slack
UseSASL bool // IRC
@@ -61,6 +62,7 @@ type Config struct {
Slack map[string]Protocol
Gitter map[string]Protocol
Xmpp map[string]Protocol
+ Discord map[string]Protocol
Gateway []Gateway
}
diff --git a/changelog.md b/changelog.md
index e01b8aaf..28eff277 100644
--- a/changelog.md
+++ b/changelog.md
@@ -6,6 +6,7 @@ See matterbridge.toml.sample for an example
## New features
* Allow for bridging the same type of bridge, which means you can eg bridge between multiple mattermosts.
* The bridge is now a gateway which has support multiple in and out bridges. (and supports multiple gateways).
+* Discord support added. See matterbridge.toml.sample for more information
# v0.6.1
## New features
diff --git a/gateway/gateway.go b/gateway/gateway.go
index 54440d69..6ac36d40 100644
--- a/gateway/gateway.go
+++ b/gateway/gateway.go
@@ -131,5 +131,7 @@ func (gw *Gateway) modifyMessage(msg *config.Message, dest bridge.Bridge) {
setNickFormat(msg, gw.Config.Mattermost[dest.Origin()].RemoteNickFormat)
case "slack":
setNickFormat(msg, gw.Config.Slack[dest.Origin()].RemoteNickFormat)
+ case "discord":
+ setNickFormat(msg, gw.Config.Discord[dest.Origin()].RemoteNickFormat)
}
}
diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample
index 5315b412..c83225ca 100644
--- a/matterbridge.toml.sample
+++ b/matterbridge.toml.sample
@@ -262,6 +262,35 @@ NicksPerRow=4
#OPTIONAL
IgnoreNicks="mmbot spammer2"
+###################################################################
+#discord section
+###################################################################
+[discord]
+
+#You can configure multiple servers "[discord.name]" or "[discord.name2]"
+#In this example we use [discord.game]
+#REQUIRED
+[discord.game]
+#Token to connect with Discord API
+#You can get your token by following the instructions on
+#https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token
+#REQUIRED
+Token="Yourtokenhere"
+
+#REQUIRED
+Guild="yourguildname"
+
+#Nicks you want to ignore. Messages of those users will not be bridged.
+#OPTIONAL
+IgnoreNicks="spammer1 spammer2"
+
+#RemoteNickFormat defines how remote users appear on this bridge
+#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
+#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
+#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
+#OPTIONAL (default {BRIDGE}-{NICK})
+RemoteNickFormat="[{BRIDGE}] <{NICK}>
+
###################################################################
#Gateway configuration
diff --git a/vendor/github.com/bwmarrin/discordgo/LICENSE b/vendor/github.com/bwmarrin/discordgo/LICENSE
new file mode 100644
index 00000000..8d062ea5
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2015, Bruce Marriner
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of discordgo nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/vendor/github.com/bwmarrin/discordgo/discord.go b/vendor/github.com/bwmarrin/discordgo/discord.go
new file mode 100644
index 00000000..d1cfddf5
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/discord.go
@@ -0,0 +1,257 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains high level helper functions and easy entry points for the
+// entire discordgo package. These functions are beling developed and are very
+// experimental at this point. They will most likley change so please use the
+// low level functions if that's a problem.
+
+// Package discordgo provides Discord binding for Go
+package discordgo
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
+const VERSION = "0.13.0"
+
+// New creates a new Discord session and will automate some startup
+// tasks if given enough information to do so. Currently you can pass zero
+// arguments and it will return an empty Discord session.
+// There are 3 ways to call New:
+// With a single auth token - All requests will use the token blindly,
+// no verification of the token will be done and requests may fail.
+// With an email and password - Discord will sign in with the provided
+// credentials.
+// With an email, password and auth token - Discord will verify the auth
+// token, if it is invalid it will sign in with the provided
+// credentials. This is the Discord recommended way to sign in.
+func New(args ...interface{}) (s *Session, err error) {
+
+ // Create an empty Session interface.
+ s = &Session{
+ State: NewState(),
+ StateEnabled: true,
+ Compress: true,
+ ShouldReconnectOnError: true,
+ ShardID: 0,
+ ShardCount: 1,
+ }
+
+ // If no arguments are passed return the empty Session interface.
+ if args == nil {
+ return
+ }
+
+ // Variables used below when parsing func arguments
+ var auth, pass string
+
+ // Parse passed arguments
+ for _, arg := range args {
+
+ switch v := arg.(type) {
+
+ case []string:
+ if len(v) > 3 {
+ err = fmt.Errorf("Too many string parameters provided.")
+ return
+ }
+
+ // First string is either token or username
+ if len(v) > 0 {
+ auth = v[0]
+ }
+
+ // If second string exists, it must be a password.
+ if len(v) > 1 {
+ pass = v[1]
+ }
+
+ // If third string exists, it must be an auth token.
+ if len(v) > 2 {
+ s.Token = v[2]
+ }
+
+ case string:
+ // First string must be either auth token or username.
+ // Second string must be a password.
+ // Only 2 input strings are supported.
+
+ if auth == "" {
+ auth = v
+ } else if pass == "" {
+ pass = v
+ } else if s.Token == "" {
+ s.Token = v
+ } else {
+ err = fmt.Errorf("Too many string parameters provided.")
+ return
+ }
+
+ // case Config:
+ // TODO: Parse configuration struct
+
+ default:
+ err = fmt.Errorf("Unsupported parameter type provided.")
+ return
+ }
+ }
+
+ // If only one string was provided, assume it is an auth token.
+ // Otherwise get auth token from Discord, if a token was specified
+ // Discord will verify it for free, or log the user in if it is
+ // invalid.
+ if pass == "" {
+ s.Token = auth
+ } else {
+ err = s.Login(auth, pass)
+ if err != nil || s.Token == "" {
+ err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
+ return
+ }
+ }
+
+ // The Session is now able to have RestAPI methods called on it.
+ // It is recommended that you now call Open() so that events will trigger.
+
+ return
+}
+
+// validateHandler takes an event handler func, and returns the type of event.
+// eg.
+// Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate))
+// will return the reflect.Type of *discordgo.MessageCreate
+func (s *Session) validateHandler(handler interface{}) reflect.Type {
+
+ handlerType := reflect.TypeOf(handler)
+
+ if handlerType.NumIn() != 2 {
+ panic("Unable to add event handler, handler must be of the type func(*discordgo.Session, *discordgo.EventType).")
+ }
+
+ if handlerType.In(0) != reflect.TypeOf(s) {
+ panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
+ }
+
+ eventType := handlerType.In(1)
+
+ // Support handlers of type interface{}, this is a special handler, which is triggered on every event.
+ if eventType.Kind() == reflect.Interface {
+ eventType = nil
+ }
+
+ return eventType
+}
+
+// AddHandler allows you to add an event handler that will be fired anytime
+// the Discord WSAPI event that matches the interface fires.
+// eventToInterface in events.go has a list of all the Discord WSAPI events
+// and their respective interface.
+// eg:
+// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
+// })
+//
+// or:
+// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
+// })
+// The return value of this method is a function, that when called will remove the
+// event handler.
+func (s *Session) AddHandler(handler interface{}) func() {
+
+ s.initialize()
+
+ eventType := s.validateHandler(handler)
+
+ s.handlersMu.Lock()
+ defer s.handlersMu.Unlock()
+
+ h := reflect.ValueOf(handler)
+
+ s.handlers[eventType] = append(s.handlers[eventType], h)
+
+ // This must be done as we need a consistent reference to the
+ // reflected value, otherwise a RemoveHandler method would have
+ // been nice.
+ return func() {
+ s.handlersMu.Lock()
+ defer s.handlersMu.Unlock()
+
+ handlers := s.handlers[eventType]
+ for i, v := range handlers {
+ if h == v {
+ s.handlers[eventType] = append(handlers[:i], handlers[i+1:]...)
+ return
+ }
+ }
+ }
+}
+
+// handle calls any handlers that match the event type and any handlers of
+// interface{}.
+func (s *Session) handle(event interface{}) {
+
+ s.handlersMu.RLock()
+ defer s.handlersMu.RUnlock()
+
+ if s.handlers == nil {
+ return
+ }
+
+ handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}
+
+ if handlers, ok := s.handlers[nil]; ok {
+ for _, handler := range handlers {
+ go handler.Call(handlerParameters)
+ }
+ }
+
+ if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok {
+ for _, handler := range handlers {
+ go handler.Call(handlerParameters)
+ }
+ }
+}
+
+// initialize adds all internal handlers and state tracking handlers.
+func (s *Session) initialize() {
+
+ s.log(LogInformational, "called")
+
+ s.handlersMu.Lock()
+ if s.handlers != nil {
+ s.handlersMu.Unlock()
+ return
+ }
+
+ s.handlers = map[interface{}][]reflect.Value{}
+ s.handlersMu.Unlock()
+
+ s.AddHandler(s.onReady)
+ s.AddHandler(s.onResumed)
+ s.AddHandler(s.onVoiceServerUpdate)
+ s.AddHandler(s.onVoiceStateUpdate)
+ s.AddHandler(s.State.onInterface)
+}
+
+// onReady handles the ready event.
+func (s *Session) onReady(se *Session, r *Ready) {
+
+ // Store the SessionID within the Session struct.
+ s.sessionID = r.SessionID
+
+ // Start the heartbeat to keep the connection alive.
+ go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
+}
+
+// onResumed handles the resumed event.
+func (s *Session) onResumed(se *Session, r *Resumed) {
+
+ // Start the heartbeat to keep the connection alive.
+ go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/endpoints.go b/vendor/github.com/bwmarrin/discordgo/endpoints.go
new file mode 100644
index 00000000..682433d6
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/endpoints.go
@@ -0,0 +1,99 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains variables for all known Discord end points. All functions
+// throughout the Discordgo package use these variables for all connections
+// to Discord. These are all exported and you may modify them if needed.
+
+package discordgo
+
+// Known Discord API Endpoints.
+var (
+ EndpointStatus = "https://status.discordapp.com/api/v2/"
+ EndpointSm = EndpointStatus + "scheduled-maintenances/"
+ EndpointSmActive = EndpointSm + "active.json"
+ EndpointSmUpcoming = EndpointSm + "upcoming.json"
+
+ EndpointDiscord = "https://discordapp.com/"
+ EndpointAPI = EndpointDiscord + "api/"
+ EndpointGuilds = EndpointAPI + "guilds/"
+ EndpointChannels = EndpointAPI + "channels/"
+ EndpointUsers = EndpointAPI + "users/"
+ EndpointGateway = EndpointAPI + "gateway"
+
+ EndpointAuth = EndpointAPI + "auth/"
+ EndpointLogin = EndpointAuth + "login"
+ EndpointLogout = EndpointAuth + "logout"
+ EndpointVerify = EndpointAuth + "verify"
+ EndpointVerifyResend = EndpointAuth + "verify/resend"
+ EndpointForgotPassword = EndpointAuth + "forgot"
+ EndpointResetPassword = EndpointAuth + "reset"
+ EndpointRegister = EndpointAuth + "register"
+
+ EndpointVoice = EndpointAPI + "/voice/"
+ EndpointVoiceRegions = EndpointVoice + "regions"
+ EndpointVoiceIce = EndpointVoice + "ice"
+
+ EndpointTutorial = EndpointAPI + "tutorial/"
+ EndpointTutorialIndicators = EndpointTutorial + "indicators"
+
+ EndpointTrack = EndpointAPI + "track"
+ EndpointSso = EndpointAPI + "sso"
+ EndpointReport = EndpointAPI + "report"
+ EndpointIntegrations = EndpointAPI + "integrations"
+
+ EndpointUser = func(uID string) string { return EndpointUsers + uID }
+ EndpointUserAvatar = func(uID, aID string) string { return EndpointUsers + uID + "/avatars/" + aID + ".jpg" }
+ 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" }
+
+ EndpointGuild = func(gID string) string { return EndpointGuilds + gID }
+ EndpointGuildInivtes = func(gID string) string { return EndpointGuilds + gID + "/invites" }
+ EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
+ EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
+ EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
+ EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" }
+ EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID }
+ EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" }
+ EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID }
+ EndpointGuildIntegrationSync = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID + "/sync" }
+ EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" }
+ EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID }
+ EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" }
+ EndpointGuildEmbed = func(gID string) string { return EndpointGuilds + gID + "/embed" }
+ EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" }
+ EndpointGuildIcon = func(gID, hash string) string { return EndpointGuilds + gID + "/icons/" + hash + ".jpg" }
+ EndpointGuildSplash = func(gID, hash string) string { return EndpointGuilds + gID + "/splashes/" + hash + ".jpg" }
+
+ EndpointChannel = func(cID string) string { return EndpointChannels + cID }
+ EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
+ EndpointChannelPermission = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID }
+ EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" }
+ EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" }
+ EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" }
+ EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
+ EndpointChannelMessageAck = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" }
+ EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk_delete" }
+ EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
+ EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
+
+ EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
+
+ EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
+
+ EndpointEmoji = func(eID string) string { return EndpointAPI + "emojis/" + eID + ".png" }
+
+ EndpointOauth2 = EndpointAPI + "oauth2/"
+ EndpointApplications = EndpointOauth2 + "applications"
+ EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID }
+ EndpointApplicationsBot = func(aID string) string { return EndpointApplications + "/" + aID + "/bot" }
+)
diff --git a/vendor/github.com/bwmarrin/discordgo/events.go b/vendor/github.com/bwmarrin/discordgo/events.go
new file mode 100644
index 00000000..72aabf67
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/events.go
@@ -0,0 +1,159 @@
+package discordgo
+
+// eventToInterface is a mapping of Discord WSAPI events to their
+// DiscordGo event container.
+// Each Discord WSAPI event maps to a unique interface.
+// Use Session.AddHandler with one of these types to handle that
+// type of event.
+// eg:
+// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
+// })
+//
+// or:
+// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
+// })
+var eventToInterface = map[string]interface{}{
+ "CHANNEL_CREATE": ChannelCreate{},
+ "CHANNEL_UPDATE": ChannelUpdate{},
+ "CHANNEL_DELETE": ChannelDelete{},
+ "GUILD_CREATE": GuildCreate{},
+ "GUILD_UPDATE": GuildUpdate{},
+ "GUILD_DELETE": GuildDelete{},
+ "GUILD_BAN_ADD": GuildBanAdd{},
+ "GUILD_BAN_REMOVE": GuildBanRemove{},
+ "GUILD_MEMBER_ADD": GuildMemberAdd{},
+ "GUILD_MEMBER_UPDATE": GuildMemberUpdate{},
+ "GUILD_MEMBER_REMOVE": GuildMemberRemove{},
+ "GUILD_ROLE_CREATE": GuildRoleCreate{},
+ "GUILD_ROLE_UPDATE": GuildRoleUpdate{},
+ "GUILD_ROLE_DELETE": GuildRoleDelete{},
+ "GUILD_INTEGRATIONS_UPDATE": GuildIntegrationsUpdate{},
+ "GUILD_EMOJIS_UPDATE": GuildEmojisUpdate{},
+ "MESSAGE_ACK": MessageAck{},
+ "MESSAGE_CREATE": MessageCreate{},
+ "MESSAGE_UPDATE": MessageUpdate{},
+ "MESSAGE_DELETE": MessageDelete{},
+ "PRESENCE_UPDATE": PresenceUpdate{},
+ "PRESENCES_REPLACE": PresencesReplace{},
+ "READY": Ready{},
+ "USER_UPDATE": UserUpdate{},
+ "USER_SETTINGS_UPDATE": UserSettingsUpdate{},
+ "USER_GUILD_SETTINGS_UPDATE": UserGuildSettingsUpdate{},
+ "TYPING_START": TypingStart{},
+ "VOICE_SERVER_UPDATE": VoiceServerUpdate{},
+ "VOICE_STATE_UPDATE": VoiceStateUpdate{},
+ "RESUMED": Resumed{},
+}
+
+// Connect is an empty struct for an event.
+type Connect struct{}
+
+// Disconnect is an empty struct for an event.
+type Disconnect struct{}
+
+// RateLimit is a struct for the RateLimited event
+type RateLimit struct {
+ *TooManyRequests
+ URL string
+}
+
+// MessageCreate is a wrapper struct for an event.
+type MessageCreate struct {
+ *Message
+}
+
+// MessageUpdate is a wrapper struct for an event.
+type MessageUpdate struct {
+ *Message
+}
+
+// MessageDelete is a wrapper struct for an event.
+type MessageDelete struct {
+ *Message
+}
+
+// ChannelCreate is a wrapper struct for an event.
+type ChannelCreate struct {
+ *Channel
+}
+
+// ChannelUpdate is a wrapper struct for an event.
+type ChannelUpdate struct {
+ *Channel
+}
+
+// ChannelDelete is a wrapper struct for an event.
+type ChannelDelete struct {
+ *Channel
+}
+
+// GuildCreate is a wrapper struct for an event.
+type GuildCreate struct {
+ *Guild
+}
+
+// GuildUpdate is a wrapper struct for an event.
+type GuildUpdate struct {
+ *Guild
+}
+
+// GuildDelete is a wrapper struct for an event.
+type GuildDelete struct {
+ *Guild
+}
+
+// GuildBanAdd is a wrapper struct for an event.
+type GuildBanAdd struct {
+ *GuildBan
+}
+
+// GuildBanRemove is a wrapper struct for an event.
+type GuildBanRemove struct {
+ *GuildBan
+}
+
+// GuildMemberAdd is a wrapper struct for an event.
+type GuildMemberAdd struct {
+ *Member
+}
+
+// GuildMemberUpdate is a wrapper struct for an event.
+type GuildMemberUpdate struct {
+ *Member
+}
+
+// GuildMemberRemove is a wrapper struct for an event.
+type GuildMemberRemove struct {
+ *Member
+}
+
+// GuildRoleCreate is a wrapper struct for an event.
+type GuildRoleCreate struct {
+ *GuildRole
+}
+
+// GuildRoleUpdate is a wrapper struct for an event.
+type GuildRoleUpdate struct {
+ *GuildRole
+}
+
+// PresencesReplace is an array of Presences for an event.
+type PresencesReplace []*Presence
+
+// VoiceStateUpdate is a wrapper struct for an event.
+type VoiceStateUpdate struct {
+ *VoiceState
+}
+
+// UserUpdate is a wrapper struct for an event.
+type UserUpdate struct {
+ *User
+}
+
+// UserSettingsUpdate is a map for an event.
+type UserSettingsUpdate map[string]interface{}
+
+// UserGuildSettingsUpdate is a map for an event.
+type UserGuildSettingsUpdate struct {
+ *UserGuildSettings
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go b/vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go
new file mode 100644
index 00000000..cc61301c
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go
@@ -0,0 +1,186 @@
+package main
+
+import (
+ "encoding/binary"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+func init() {
+ flag.StringVar(&token, "t", "", "Account Token")
+ flag.Parse()
+}
+
+var token string
+var buffer = make([][]byte, 0)
+
+func main() {
+ if token == "" {
+ fmt.Println("No token provided. Please run: airhorn -t <bot token>")
+ return
+ }
+
+ // Load the sound file.
+ err := loadSound()
+ if err != nil {
+ fmt.Println("Error loading sound: ", err)
+ fmt.Println("Please copy $GOPATH/src/github.com/bwmarrin/examples/airhorn/airhorn.dca to this directory.")
+ return
+ }
+
+ // Create a new Discord session using the provided token.
+ dg, err := discordgo.New(token)
+ if err != nil {
+ fmt.Println("Error creating Discord session: ", err)
+ return
+ }
+
+ // Register ready as a callback for the ready events.
+ dg.AddHandler(ready)
+
+ // Register messageCreate as a callback for the messageCreate events.
+ dg.AddHandler(messageCreate)
+
+ // Register guildCreate as a callback for the guildCreate events.
+ dg.AddHandler(guildCreate)
+
+ // Open the websocket and begin listening.
+ err = dg.Open()
+ if err != nil {
+ fmt.Println("Error opening Discord session: ", err)
+ }
+
+ fmt.Println("Airhorn is now running. Press CTRL-C to exit.")
+ // Simple way to keep program running until CTRL-C is pressed.
+ <-make(chan struct{})
+ return
+}
+
+func ready(s *discordgo.Session, event *discordgo.Ready) {
+ // Set the playing status.
+ _ = s.UpdateStatus(0, "!airhorn")
+}
+
+// This function will be called (due to AddHandler above) every time a new
+// message is created on any channel that the autenticated bot has access to.
+func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
+ if strings.HasPrefix(m.Content, "!airhorn") {
+ // Find the channel that the message came from.
+ c, err := s.State.Channel(m.ChannelID)
+ if err != nil {
+ // Could not find channel.
+ return
+ }
+
+ // Find the guild for that channel.
+ g, err := s.State.Guild(c.GuildID)
+ if err != nil {
+ // Could not find guild.
+ return
+ }
+
+ // Look for the message sender in that guilds current voice states.
+ for _, vs := range g.VoiceStates {
+ if vs.UserID == m.Author.ID {
+ err = playSound(s, g.ID, vs.ChannelID)
+ if err != nil {
+ fmt.Println("Error playing sound:", err)
+ }
+
+ return
+ }
+ }
+ }
+}
+
+// This function will be called (due to AddHandler above) every time a new
+// guild is joined.
+func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
+ if event.Guild.Unavailable != nil {
+ return
+ }
+
+ for _, channel := range event.Guild.Channels {
+ if channel.ID == event.Guild.ID {
+ _, _ = s.ChannelMessageSend(channel.ID, "Airhorn is ready! Type !airhorn while in a voice channel to play a sound.")
+ return
+ }
+ }
+}
+
+// loadSound attempts to load an encoded sound file from disk.
+func loadSound() error {
+ file, err := os.Open("airhorn.dca")
+
+ if err != nil {
+ fmt.Println("Error opening dca file :", err)
+ return err
+ }
+
+ var opuslen int16
+
+ for {
+ // Read opus frame length from dca file.
+ err = binary.Read(file, binary.LittleEndian, &opuslen)
+
+ // If this is the end of the file, just return.
+ if err == io.EOF || err == io.ErrUnexpectedEOF {
+ return nil
+ }
+
+ if err != nil {
+ fmt.Println("Error reading from dca file :", err)
+ return err
+ }
+
+ // Read encoded pcm from dca file.
+ InBuf := make([]byte, opuslen)
+ err = binary.Read(file, binary.LittleEndian, &InBuf)
+
+ // Should not be any end of file errors
+ if err != nil {
+ fmt.Println("Error reading from dca file :", err)
+ return err
+ }
+
+ // Append encoded pcm data to the buffer.
+ buffer = append(buffer, InBuf)
+ }
+}
+
+// playSound plays the current buffer to the provided channel.
+func playSound(s *discordgo.Session, guildID, channelID string) (err error) {
+ // Join the provided voice channel.
+ vc, err := s.ChannelVoiceJoin(guildID, channelID, false, true)
+ if err != nil {
+ return err
+ }
+
+ // Sleep for a specified amount of time before playing the sound
+ time.Sleep(250 * time.Millisecond)
+
+ // Start speaking.
+ _ = vc.Speaking(true)
+
+ // Send the buffer data.
+ for _, buff := range buffer {
+ vc.OpusSend <- buff
+ }
+
+ // Stop speaking
+ _ = vc.Speaking(false)
+
+ // Sleep for a specificed amount of time before ending.
+ time.Sleep(250 * time.Millisecond)
+
+ // Disconnect from the provided voice channel.
+ _ = vc.Disconnect()
+
+ return nil
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go b/vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go
new file mode 100644
index 00000000..bd0e3b88
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go
@@ -0,0 +1,98 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+// Variables used for command line options
+var (
+ Email string
+ Password string
+ Token string
+ AppName string
+ DeleteID string
+ ListOnly bool
+)
+
+func init() {
+
+ flag.StringVar(&Email, "e", "", "Account Email")
+ flag.StringVar(&Password, "p", "", "Account Password")
+ flag.StringVar(&Token, "t", "", "Account Token")
+ flag.StringVar(&DeleteID, "d", "", "Application ID to delete")
+ flag.BoolVar(&ListOnly, "l", false, "List Applications Only")
+ flag.StringVar(&AppName, "a", "", "App/Bot Name")
+ flag.Parse()
+}
+
+func main() {
+
+ var err error
+ // Create a new Discord session using the provided login information.
+ dg, err := discordgo.New(Email, Password, Token)
+ if err != nil {
+ fmt.Println("error creating Discord session,", err)
+ return
+ }
+
+ // If -l set, only display a list of existing applications
+ // for the given account.
+ if ListOnly {
+ aps, err2 := dg.Applications()
+ if err2 != nil {
+ fmt.Println("error fetching applications,", err)
+ return
+ }
+
+ for k, v := range aps {
+ fmt.Printf("%d : --------------------------------------\n", k)
+ fmt.Printf("ID: %s\n", v.ID)
+ fmt.Printf("Name: %s\n", v.Name)
+ fmt.Printf("Secret: %s\n", v.Secret)
+ fmt.Printf("Description: %s\n", v.Description)
+ }
+ return
+ }
+
+ // if -d set, delete the given Application
+ if DeleteID != "" {
+ err = dg.ApplicationDelete(DeleteID)
+ if err != nil {
+ fmt.Println("error deleting application,", err)
+ }
+ return
+ }
+
+ // Create a new application.
+ ap := &discordgo.Application{}
+ ap.Name = AppName
+ ap, err = dg.ApplicationCreate(ap)
+ if err != nil {
+ fmt.Println("error creating new applicaiton,", err)
+ return
+ }
+
+ fmt.Printf("Application created successfully:\n")
+ fmt.Printf("ID: %s\n", ap.ID)
+ fmt.Printf("Name: %s\n", ap.Name)
+ fmt.Printf("Secret: %s\n\n", ap.Secret)
+
+ // Create the bot account under the application we just created
+ bot, err := dg.ApplicationBotCreate(ap.ID)
+ if err != nil {
+ fmt.Println("error creating bot account,", err)
+ return
+ }
+
+ fmt.Printf("Bot account created successfully.\n")
+ fmt.Printf("ID: %s\n", bot.ID)
+ fmt.Printf("Username: %s\n", bot.Username)
+ fmt.Printf("Token: %s\n\n", bot.Token)
+ fmt.Println("Please save the above posted info in a secure place.")
+ fmt.Println("You will need that information to login with your bot account.")
+
+ return
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/examples/avatar/localfile/main.go b/vendor/github.com/bwmarrin/discordgo/examples/avatar/localfile/main.go
new file mode 100644
index 00000000..adfe0b1d
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/examples/avatar/localfile/main.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+ "encoding/base64"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+// Variables used for command line parameters
+var (
+ Email string
+ Password string
+ Token string
+ Avatar string
+ BotID string
+ BotUsername string
+)
+
+func init() {
+
+ flag.StringVar(&Email, "e", "", "Account Email")
+ flag.StringVar(&Password, "p", "", "Account Password")
+ flag.StringVar(&Token, "t", "", "Account Token")
+ flag.StringVar(&Avatar, "f", "./avatar.jpg", "Avatar File Name")
+ flag.Parse()
+}
+
+func main() {
+
+ // Create a new Discord session using the provided login information.
+ // Use discordgo.New(Token) to just use a token for login.
+ dg, err := discordgo.New(Email, Password, Token)
+ if err != nil {
+ fmt.Println("error creating Discord session,", err)
+ return
+ }
+
+ bot, err := dg.User("@me")
+ if err != nil {
+ fmt.Println("error fetching the bot details,", err)
+ return
+ }
+
+ BotID = bot.ID
+ BotUsername = bot.Username
+ changeAvatar(dg)
+
+ fmt.Println("Bot is now running. Press CTRL-C to exit.")
+ // Simple way to keep program running until CTRL-C is pressed.
+ <-make(chan struct{})
+ return
+}
+
+// Helper function to change the avatar
+func changeAvatar(s *discordgo.Session) {
+ img, err := ioutil.ReadFile(Avatar)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ base64 := base64.StdEncoding.EncodeToString(img)
+
+ avatar := fmt.Sprintf("data:%s;base64,%s", http.DetectContentType(img), base64)
+
+ _, err = s.UserUpdate("", "", BotUsername, avatar, "")
+ if err != nil {
+ fmt.Println(err)
+ }
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/examples/avatar/url/main.go b/vendor/github.com/bwmarrin/discordgo/examples/avatar/url/main.go
new file mode 100644
index 00000000..26170df5
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/examples/avatar/url/main.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+ "encoding/base64"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+// Variables used for command line parameters
+var (
+ Email string
+ Password string
+ Token string
+ URL string
+ BotID string
+ BotUsername string
+)
+
+func init() {
+
+ flag.StringVar(&Email, "e", "", "Account Email")
+ flag.StringVar(&Password, "p", "", "Account Password")
+ flag.StringVar(&Token, "t", "", "Account Token")
+ flag.StringVar(&URL, "l", "http://bwmarrin.github.io/discordgo/img/discordgo.png", "Link to the avatar image")
+ flag.Parse()
+}
+
+func main() {
+
+ // Create a new Discord session using the provided login information.
+ // Use discordgo.New(Token) to just use a token for login.
+ dg, err := discordgo.New(Email, Password, Token)
+ if err != nil {
+ fmt.Println("error creating Discord session,", err)
+ return
+ }
+
+ bot, err := dg.User("@me")
+ if err != nil {
+ fmt.Println("error fetching the bot details,", err)
+ return
+ }
+
+ BotID = bot.ID
+ BotUsername = bot.Username
+ changeAvatar(dg)
+
+ fmt.Println("Bot is now running. Press CTRL-C to exit.")
+ // Simple way to keep program running until CTRL-C is pressed.
+ <-make(chan struct{})
+ return
+}
+
+// Helper function to change the avatar
+func changeAvatar(s *discordgo.Session) {
+
+ resp, err := http.Get(URL)
+ if err != nil {
+ fmt.Println("Error retrieving the file, ", err)
+ return
+ }
+
+ defer func() {
+ _ = resp.Body.Close()
+ }()
+
+ img, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ fmt.Println("Error reading the response, ", err)
+ return
+ }
+
+ base64 := base64.StdEncoding.EncodeToString(img)
+
+ avatar := fmt.Sprintf("data:%s;base64,%s", http.DetectContentType(img), base64)
+
+ _, err = s.UserUpdate("", "", BotUsername, avatar, "")
+ if err != nil {
+ fmt.Println("Error setting the avatar, ", err)
+ }
+
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go b/vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go
new file mode 100644
index 00000000..5914fc8a
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go
@@ -0,0 +1,33 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+// Variables used for command line parameters
+var (
+ Email string
+ Password string
+)
+
+func init() {
+
+ flag.StringVar(&Email, "e", "", "Account Email")
+ flag.StringVar(&Password, "p", "", "Account Password")
+ flag.Parse()
+}
+
+func main() {
+
+ // Create a new Discord session using the provided login information.
+ dg, err := discordgo.New(Email, Password)
+ if err != nil {
+ fmt.Println("error creating Discord session,", err)
+ return
+ }
+
+ fmt.Printf("Your Authentication Token is:\n\n%s\n", dg.Token)
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go b/vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go
new file mode 100644
index 00000000..c3861ac0
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go
@@ -0,0 +1,58 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "time"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+// Variables used for command line parameters
+var (
+ Email string
+ Password string
+ Token string
+)
+
+func init() {
+
+ flag.StringVar(&Email, "e", "", "Account Email")
+ flag.StringVar(&Password, "p", "", "Account Password")
+ flag.StringVar(&Token, "t", "", "Account Token")
+ flag.Parse()
+}
+
+func main() {
+
+ // Create a new Discord session using the provided login information.
+ // Use discordgo.New(Token) to just use a token for login.
+ dg, err := discordgo.New(Email, Password, Token)
+ if err != nil {
+ fmt.Println("error creating Discord session,", err)
+ return
+ }
+
+ // Register messageCreate as a callback for the messageCreate events.
+ dg.AddHandler(messageCreate)
+
+ // Open the websocket and begin listening.
+ err = dg.Open()
+ if err != nil {
+ fmt.Println("error opening connection,", err)
+ return
+ }
+
+ fmt.Println("Bot is now running. Press CTRL-C to exit.")
+ // Simple way to keep program running until CTRL-C is pressed.
+ <-make(chan struct{})
+ return
+}
+
+// This function will be called (due to AddHandler above) every time a new
+// message is created on any channel that the autenticated bot has access to.
+func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
+
+ // Print message to stdout.
+ fmt.Printf("%20s %20s %20s > %s\n", m.ChannelID, time.Now().Format(time.Stamp), m.Author.Username, m.Content)
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go b/vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go
new file mode 100644
index 00000000..e6893ca1
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go
@@ -0,0 +1,78 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+// Variables used for command line parameters
+var (
+ Email string
+ Password string
+ Token string
+ BotID string
+)
+
+func init() {
+
+ flag.StringVar(&Email, "e", "", "Account Email")
+ flag.StringVar(&Password, "p", "", "Account Password")
+ flag.StringVar(&Token, "t", "", "Account Token")
+ flag.Parse()
+}
+
+func main() {
+
+ // Create a new Discord session using the provided login information.
+ dg, err := discordgo.New(Email, Password, Token)
+ if err != nil {
+ fmt.Println("error creating Discord session,", err)
+ return
+ }
+
+ // Get the account information.
+ u, err := dg.User("@me")
+ if err != nil {
+ fmt.Println("error obtaining account details,", err)
+ }
+
+ // Store the account ID for later use.
+ BotID = u.ID
+
+ // Register messageCreate as a callback for the messageCreate events.
+ dg.AddHandler(messageCreate)
+
+ // Open the websocket and begin listening.
+ err = dg.Open()
+ if err != nil {
+ fmt.Println("error opening connection,", err)
+ return
+ }
+
+ fmt.Println("Bot is now running. Press CTRL-C to exit.")
+ // Simple way to keep program running until CTRL-C is pressed.
+ <-make(chan struct{})
+ return
+}
+
+// This function will be called (due to AddHandler above) every time a new
+// message is created on any channel that the autenticated bot has access to.
+func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
+
+ // Ignore all messages created by the bot itself
+ if m.Author.ID == BotID {
+ return
+ }
+
+ // If the message is "ping" reply with "Pong!"
+ if m.Content == "ping" {
+ _, _ = s.ChannelMessageSend(m.ChannelID, "Pong!")
+ }
+
+ // If the message is "pong" reply with "Ping!"
+ if m.Content == "pong" {
+ _, _ = s.ChannelMessageSend(m.ChannelID, "Ping!")
+ }
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/logging.go b/vendor/github.com/bwmarrin/discordgo/logging.go
new file mode 100644
index 00000000..70d78d60
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/logging.go
@@ -0,0 +1,95 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains code related to discordgo package logging
+
+package discordgo
+
+import (
+ "fmt"
+ "log"
+ "runtime"
+ "strings"
+)
+
+const (
+
+ // LogError level is used for critical errors that could lead to data loss
+ // or panic that would not be returned to a calling function.
+ LogError int = iota
+
+ // LogWarning level is used for very abnormal events and errors that are
+ // also returend to a calling function.
+ LogWarning
+
+ // LogInformational level is used for normal non-error activity
+ LogInformational
+
+ // LogDebug level is for very detailed non-error activity. This is
+ // very spammy and will impact performance.
+ LogDebug
+)
+
+// msglog provides package wide logging consistancy for discordgo
+// the format, a... portion this command follows that of fmt.Printf
+// msgL : LogLevel of the message
+// caller : 1 + the number of callers away from the message source
+// format : Printf style message format
+// a ... : comma seperated list of values to pass
+func msglog(msgL, caller int, format string, a ...interface{}) {
+
+ pc, file, line, _ := runtime.Caller(caller)
+
+ files := strings.Split(file, "/")
+ file = files[len(files)-1]
+
+ name := runtime.FuncForPC(pc).Name()
+ fns := strings.Split(name, ".")
+ name = fns[len(fns)-1]
+
+ msg := fmt.Sprintf(format, a...)
+
+ log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
+}
+
+// helper function that wraps msglog for the Session struct
+// This adds a check to insure the message is only logged
+// if the session log level is equal or higher than the
+// message log level
+func (s *Session) log(msgL int, format string, a ...interface{}) {
+
+ if msgL > s.LogLevel {
+ return
+ }
+
+ msglog(msgL, 2, format, a...)
+}
+
+// helper function that wraps msglog for the VoiceConnection struct
+// This adds a check to insure the message is only logged
+// if the voice connection log level is equal or higher than the
+// message log level
+func (v *VoiceConnection) log(msgL int, format string, a ...interface{}) {
+
+ if msgL > v.LogLevel {
+ return
+ }
+
+ msglog(msgL, 2, format, a...)
+}
+
+// printJSON is a helper function to display JSON data in a easy to read format.
+/* NOT USED ATM
+func printJSON(body []byte) {
+ var prettyJSON bytes.Buffer
+ error := json.Indent(&prettyJSON, body, "", "\t")
+ if error != nil {
+ log.Print("JSON parse error: ", error)
+ }
+ log.Println(string(prettyJSON.Bytes()))
+}
+*/
diff --git a/vendor/github.com/bwmarrin/discordgo/message.go b/vendor/github.com/bwmarrin/discordgo/message.go
new file mode 100644
index 00000000..8966c161
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/message.go
@@ -0,0 +1,82 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains code related to the Message struct
+
+package discordgo
+
+import (
+ "fmt"
+ "regexp"
+)
+
+// 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 string `json:"timestamp"`
+ EditedTimestamp string `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"`
+}
+
+// A MessageAttachment stores data for message attachments.
+type MessageAttachment struct {
+ ID string `json:"id"`
+ URL string `json:"url"`
+ ProxyURL string `json:"proxy_url"`
+ Filename string `json:"filename"`
+ Width int `json:"width"`
+ Height int `json:"height"`
+ Size int `json:"size"`
+}
+
+// An MessageEmbed stores data for message embeds.
+type MessageEmbed struct {
+ URL string `json:"url"`
+ Type string `json:"type"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ Thumbnail *struct {
+ URL string `json:"url"`
+ ProxyURL string `json:"proxy_url"`
+ Width int `json:"width"`
+ Height int `json:"height"`
+ } `json:"thumbnail"`
+ Provider *struct {
+ URL string `json:"url"`
+ Name string `json:"name"`
+ } `json:"provider"`
+ Author *struct {
+ URL string `json:"url"`
+ Name string `json:"name"`
+ } `json:"author"`
+ Video *struct {
+ URL string `json:"url"`
+ Width int `json:"width"`
+ Height int `json:"height"`
+ } `json:"video"`
+}
+
+// ContentWithMentionsReplaced will replace all @<id> mentions with the
+// username of the mention.
+func (m *Message) ContentWithMentionsReplaced() string {
+ if m.Mentions == nil {
+ return m.Content
+ }
+ content := m.Content
+ for _, user := range m.Mentions {
+ content = regexp.MustCompile(fmt.Sprintf("<@!?(%s)>", user.ID)).ReplaceAllString(content, "@"+user.Username)
+ }
+ return content
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/oauth2.go b/vendor/github.com/bwmarrin/discordgo/oauth2.go
new file mode 100644
index 00000000..de2848db
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/oauth2.go
@@ -0,0 +1,120 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains functions related to Discord OAuth2 endpoints
+
+package discordgo
+
+// ------------------------------------------------------------------------------------------------
+// Code specific to Discord OAuth2 Applications
+// ------------------------------------------------------------------------------------------------
+
+// An Application struct stores values for a Discord OAuth2 Application
+type Application struct {
+ ID string `json:"id,omitempty"`
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ Icon string `json:"icon,omitempty"`
+ Secret string `json:"secret,omitempty"`
+ RedirectURIs *[]string `json:"redirect_uris,omitempty"`
+}
+
+// Application returns an Application structure of a specific Application
+// appID : The ID of an Application
+func (s *Session) Application(appID string) (st *Application, err error) {
+
+ body, err := s.Request("GET", EndpointApplication(appID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// Applications returns all applications for the authenticated user
+func (s *Session) Applications() (st []*Application, err error) {
+
+ body, err := s.Request("GET", EndpointApplications, nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ApplicationCreate creates a new Application
+// name : Name of Application / Bot
+// uris : Redirect URIs (Not required)
+func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error) {
+
+ data := struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ RedirectURIs *[]string `json:"redirect_uris,omitempty"`
+ }{ap.Name, ap.Description, ap.RedirectURIs}
+
+ body, err := s.Request("POST", EndpointApplications, data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ApplicationUpdate updates an existing Application
+// var : desc
+func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Application, err error) {
+
+ data := struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ RedirectURIs *[]string `json:"redirect_uris,omitempty"`
+ }{ap.Name, ap.Description, ap.RedirectURIs}
+
+ body, err := s.Request("PUT", EndpointApplication(appID), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ApplicationDelete deletes an existing Application
+// appID : The ID of an Application
+func (s *Session) ApplicationDelete(appID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointApplication(appID), nil)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+// ------------------------------------------------------------------------------------------------
+// Code specific to Discord OAuth2 Application Bots
+// ------------------------------------------------------------------------------------------------
+
+// ApplicationBotCreate creates an Application Bot Account
+//
+// appID : The ID of an Application
+//
+// NOTE: func name may change, if I can think up something better.
+func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) {
+
+ body, err := s.Request("POST", EndpointApplicationsBot(appID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/restapi.go b/vendor/github.com/bwmarrin/discordgo/restapi.go
new file mode 100644
index 00000000..337a7f82
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/restapi.go
@@ -0,0 +1,1403 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains functions for interacting with the Discord REST/JSON API
+// at the lowest level.
+
+package discordgo
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "image"
+ _ "image/jpeg" // For JPEG decoding
+ _ "image/png" // For PNG decoding
+ "io"
+ "io/ioutil"
+ "log"
+ "mime/multipart"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+// ErrJSONUnmarshal is returned for JSON Unmarshall errors.
+var ErrJSONUnmarshal = errors.New("json unmarshal")
+
+// Request makes a (GET/POST/...) Requests to Discord REST API with JSON data.
+// All the other Discord REST Calls in this file use this function.
+func (s *Session) Request(method, urlStr string, data interface{}) (response []byte, err error) {
+
+ var body []byte
+ if data != nil {
+ body, err = json.Marshal(data)
+ if err != nil {
+ return
+ }
+ }
+
+ return s.request(method, urlStr, "application/json", body)
+}
+
+// request makes a (GET/POST/...) Requests to Discord REST API.
+func (s *Session) request(method, urlStr, contentType string, b []byte) (response []byte, err error) {
+
+ // rate limit mutex for this url
+ // TODO: review for performance improvements
+ // ideally we just ignore endpoints that we've never
+ // received a 429 on. But this simple method works and
+ // is a lot less complex :) It also might even be more
+ // performat due to less checks and maps.
+ var mu *sync.Mutex
+
+ s.rateLimit.Lock()
+ if s.rateLimit.url == nil {
+ s.rateLimit.url = make(map[string]*sync.Mutex)
+ }
+
+ bu := strings.Split(urlStr, "?")
+ mu, _ = s.rateLimit.url[bu[0]]
+ if mu == nil {
+ mu = new(sync.Mutex)
+ s.rateLimit.url[urlStr] = mu
+ }
+ s.rateLimit.Unlock()
+
+ mu.Lock() // lock this URL for ratelimiting
+ if s.Debug {
+ log.Printf("API REQUEST %8s :: %s\n", method, urlStr)
+ log.Printf("API REQUEST PAYLOAD :: [%s]\n", string(b))
+ }
+
+ req, err := http.NewRequest(method, urlStr, bytes.NewBuffer(b))
+ if err != nil {
+ return
+ }
+
+ // Not used on initial login..
+ // TODO: Verify if a login, otherwise complain about no-token
+ if s.Token != "" {
+ req.Header.Set("authorization", s.Token)
+ }
+
+ 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))
+
+ if s.Debug {
+ for k, v := range req.Header {
+ log.Printf("API REQUEST HEADER :: [%s] = %+v\n", k, v)
+ }
+ }
+
+ client := &http.Client{Timeout: (20 * time.Second)}
+
+ resp, err := client.Do(req)
+ mu.Unlock() // unlock ratelimit mutex
+ if err != nil {
+ return
+ }
+ defer func() {
+ err2 := resp.Body.Close()
+ if err2 != nil {
+ log.Println("error closing resp body")
+ }
+ }()
+
+ response, err = ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return
+ }
+
+ if s.Debug {
+
+ log.Printf("API RESPONSE STATUS :: %s\n", resp.Status)
+ for k, v := range resp.Header {
+ log.Printf("API RESPONSE HEADER :: [%s] = %+v\n", k, v)
+ }
+ log.Printf("API RESPONSE BODY :: [%s]\n\n\n", response)
+ }
+
+ switch resp.StatusCode {
+
+ case http.StatusOK:
+ case http.StatusCreated:
+ case http.StatusNoContent:
+
+ // TODO check for 401 response, invalidate token if we get one.
+
+ case 429: // TOO MANY REQUESTS - Rate limiting
+
+ mu.Lock() // lock URL ratelimit mutex
+
+ rl := TooManyRequests{}
+ err = json.Unmarshal(response, &rl)
+ if err != nil {
+ s.log(LogError, "rate limit unmarshal error, %s", err)
+ mu.Unlock()
+ return
+ }
+ s.log(LogInformational, "Rate Limiting %s, retry in %d", urlStr, rl.RetryAfter)
+ s.handle(RateLimit{TooManyRequests: &rl, URL: urlStr})
+
+ time.Sleep(rl.RetryAfter * time.Millisecond)
+ // we can make the above smarter
+ // this method can cause longer delays then required
+
+ mu.Unlock() // we have to unlock here
+ response, err = s.request(method, urlStr, contentType, b)
+
+ default: // Error condition
+ err = fmt.Errorf("HTTP %s, %s", resp.Status, response)
+ }
+
+ return
+}
+
+func unmarshal(data []byte, v interface{}) error {
+ err := json.Unmarshal(data, v)
+ if err != nil {
+ return ErrJSONUnmarshal
+ }
+
+ return nil
+}
+
+// ------------------------------------------------------------------------------------------------
+// Functions specific to Discord Sessions
+// ------------------------------------------------------------------------------------------------
+
+// Login asks the Discord server for an authentication token.
+func (s *Session) Login(email, password string) (err error) {
+
+ data := struct {
+ Email string `json:"email"`
+ Password string `json:"password"`
+ }{email, password}
+
+ response, err := s.Request("POST", EndpointLogin, data)
+ if err != nil {
+ return
+ }
+
+ temp := struct {
+ Token string `json:"token"`
+ }{}
+
+ err = unmarshal(response, &temp)
+ if err != nil {
+ return
+ }
+
+ s.Token = temp.Token
+ return
+}
+
+// Register sends a Register request to Discord, and returns the authentication token
+// Note that this account is temporary and should be verified for future use.
+// Another option is to save the authentication token external, but this isn't recommended.
+func (s *Session) Register(username string) (token string, err error) {
+
+ data := struct {
+ Username string `json:"username"`
+ }{username}
+
+ response, err := s.Request("POST", EndpointRegister, data)
+ if err != nil {
+ return
+ }
+
+ temp := struct {
+ Token string `json:"token"`
+ }{}
+
+ err = unmarshal(response, &temp)
+ if err != nil {
+ return
+ }
+
+ token = temp.Token
+ return
+}
+
+// Logout sends a logout request to Discord.
+// This does not seem to actually invalidate the token. So you can still
+// make API calls even after a Logout. So, it seems almost pointless to
+// even use.
+func (s *Session) Logout() (err error) {
+
+ // _, err = s.Request("POST", LOGOUT, fmt.Sprintf(`{"token": "%s"}`, s.Token))
+
+ if s.Token == "" {
+ return
+ }
+
+ data := struct {
+ Token string `json:"token"`
+ }{s.Token}
+
+ _, err = s.Request("POST", EndpointLogout, data)
+ return
+}
+
+// ------------------------------------------------------------------------------------------------
+// Functions specific to Discord Users
+// ------------------------------------------------------------------------------------------------
+
+// User returns the user details of the given userID
+// userID : A user ID or "@me" which is a shortcut of current user ID
+func (s *Session) User(userID string) (st *User, err error) {
+
+ body, err := s.Request("GET", EndpointUser(userID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// UserAvatar returns an image.Image of a users Avatar.
+// userID : A user ID or "@me" which is a shortcut of current user ID
+func (s *Session) UserAvatar(userID string) (img image.Image, err error) {
+ u, err := s.User(userID)
+ if err != nil {
+ return
+ }
+
+ body, err := s.Request("GET", EndpointUserAvatar(userID, u.Avatar), nil)
+ if err != nil {
+ return
+ }
+
+ img, _, err = image.Decode(bytes.NewReader(body))
+ return
+}
+
+// UserUpdate updates a users settings.
+func (s *Session) UserUpdate(email, password, username, avatar, newPassword string) (st *User, err error) {
+
+ // NOTE: Avatar must be either the hash/id of existing Avatar or
+ // data:image/png;base64,BASE64_STRING_OF_NEW_AVATAR_PNG
+ // to set a new avatar.
+ // If left blank, avatar will be set to null/blank
+
+ data := struct {
+ Email string `json:"email"`
+ Password string `json:"password"`
+ Username string `json:"username"`
+ Avatar string `json:"avatar,omitempty"`
+ NewPassword string `json:"new_password,omitempty"`
+ }{email, password, username, avatar, newPassword}
+
+ body, err := s.Request("PATCH", EndpointUser("@me"), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// UserSettings returns the settings for a given user
+func (s *Session) UserSettings() (st *Settings, err error) {
+
+ body, err := s.Request("GET", EndpointUserSettings("@me"), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// UserChannels returns an array of Channel structures for all private
+// channels.
+func (s *Session) UserChannels() (st []*Channel, err error) {
+
+ body, err := s.Request("GET", EndpointUserChannels("@me"), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// UserChannelCreate creates a new User (Private) Channel with another User
+// recipientID : A user ID for the user to which this channel is opened with.
+func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) {
+
+ data := struct {
+ RecipientID string `json:"recipient_id"`
+ }{recipientID}
+
+ body, err := s.Request("POST", EndpointUserChannels("@me"), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// UserGuilds returns an array of Guild structures for all guilds.
+func (s *Session) UserGuilds() (st []*Guild, err error) {
+
+ body, err := s.Request("GET", EndpointUserGuilds("@me"), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// UserGuildSettingsEdit Edits the users notification settings for a guild
+// guildID : The ID of the guild to edit the settings on
+// settings : The settings to update
+func (s *Session) UserGuildSettingsEdit(guildID string, settings *UserGuildSettingsEdit) (st *UserGuildSettings, err error) {
+
+ body, err := s.Request("PATCH", EndpointUserGuildSettings("@me", guildID), settings)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// NOTE: This function is now deprecated and will be removed in the future.
+// Please see the same function inside state.go
+// UserChannelPermissions returns the permission of a user in a channel.
+// userID : The ID of the user to calculate permissions for.
+// channelID : The ID of the channel to calculate permission for.
+func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int, err error) {
+ channel, err := s.State.Channel(channelID)
+ if err != nil || channel == nil {
+ channel, err = s.Channel(channelID)
+ if err != nil {
+ return
+ }
+ }
+
+ guild, err := s.State.Guild(channel.GuildID)
+ if err != nil || guild == nil {
+ guild, err = s.Guild(channel.GuildID)
+ if err != nil {
+ return
+ }
+ }
+
+ if userID == guild.OwnerID {
+ apermissions = PermissionAll
+ return
+ }
+
+ member, err := s.State.Member(guild.ID, userID)
+ if err != nil || member == nil {
+ member, err = s.GuildMember(guild.ID, userID)
+ if err != nil {
+ return
+ }
+ }
+
+ for _, role := range guild.Roles {
+ for _, roleID := range member.Roles {
+ if role.ID == roleID {
+ apermissions |= role.Permissions
+ break
+ }
+ }
+ }
+
+ if apermissions&PermissionManageRoles > 0 {
+ apermissions |= PermissionAll
+ }
+
+ // Member overwrites can override role overrides, so do two passes
+ for _, overwrite := range channel.PermissionOverwrites {
+ for _, roleID := range member.Roles {
+ if overwrite.Type == "role" && roleID == overwrite.ID {
+ apermissions &= ^overwrite.Deny
+ apermissions |= overwrite.Allow
+ break
+ }
+ }
+ }
+
+ for _, overwrite := range channel.PermissionOverwrites {
+ if overwrite.Type == "member" && overwrite.ID == userID {
+ apermissions &= ^overwrite.Deny
+ apermissions |= overwrite.Allow
+ break
+ }
+ }
+
+ if apermissions&PermissionManageRoles > 0 {
+ apermissions |= PermissionAllChannel
+ }
+
+ return
+}
+
+// ------------------------------------------------------------------------------------------------
+// Functions specific to Discord Guilds
+// ------------------------------------------------------------------------------------------------
+
+// Guild returns a Guild structure of a specific Guild.
+// guildID : The ID of a Guild
+func (s *Session) Guild(guildID string) (st *Guild, err error) {
+ if s.StateEnabled {
+ // Attempt to grab the guild from State first.
+ st, err = s.State.Guild(guildID)
+ if err == nil {
+ return
+ }
+ }
+
+ body, err := s.Request("GET", EndpointGuild(guildID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// GuildCreate creates a new Guild
+// name : A name for the Guild (2-100 characters)
+func (s *Session) GuildCreate(name string) (st *Guild, err error) {
+
+ data := struct {
+ Name string `json:"name"`
+ }{name}
+
+ body, err := s.Request("POST", EndpointGuilds, data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// GuildEdit edits a new Guild
+// guildID : The ID of a Guild
+// g : A GuildParams struct with the values Name, Region and VerificationLevel defined.
+func (s *Session) GuildEdit(guildID string, g GuildParams) (st *Guild, err error) {
+
+ // Bounds checking for VerificationLevel, interval: [0, 3]
+ if g.VerificationLevel != nil {
+ val := *g.VerificationLevel
+ if val < 0 || val > 3 {
+ err = errors.New("VerificationLevel out of bounds, should be between 0 and 3")
+ return
+ }
+ }
+
+ //Bounds checking for regions
+ if g.Region != "" {
+ isValid := false
+ regions, _ := s.VoiceRegions()
+ for _, r := range regions {
+ if g.Region == r.ID {
+ isValid = true
+ }
+ }
+ if !isValid {
+ var valid []string
+ for _, r := range regions {
+ valid = append(valid, r.ID)
+ }
+ err = fmt.Errorf("Region not a valid region (%q)", valid)
+ return
+ }
+ }
+
+ data := struct {
+ Name string `json:"name,omitempty"`
+ Region string `json:"region,omitempty"`
+ VerificationLevel *VerificationLevel `json:"verification_level,omitempty"`
+ }{g.Name, g.Region, g.VerificationLevel}
+
+ body, err := s.Request("PATCH", EndpointGuild(guildID), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// GuildDelete deletes a Guild.
+// guildID : The ID of a Guild
+func (s *Session) GuildDelete(guildID string) (st *Guild, err error) {
+
+ body, err := s.Request("DELETE", EndpointGuild(guildID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// GuildLeave leaves a Guild.
+// guildID : The ID of a Guild
+func (s *Session) GuildLeave(guildID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointUserGuild("@me", guildID), nil)
+ return
+}
+
+// GuildBans returns an array of User structures for all bans of a
+// given guild.
+// guildID : The ID of a Guild.
+func (s *Session) GuildBans(guildID string) (st []*User, err error) {
+
+ body, err := s.Request("GET", EndpointGuildBans(guildID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+
+ return
+}
+
+// GuildBanCreate bans the given user from the given guild.
+// guildID : The ID of a Guild.
+// userID : The ID of a User
+// days : The number of days of previous comments to delete.
+func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) {
+
+ uri := EndpointGuildBan(guildID, userID)
+
+ if days > 0 {
+ uri = fmt.Sprintf("%s?delete-message-days=%d", uri, days)
+ }
+
+ _, err = s.Request("PUT", uri, nil)
+ return
+}
+
+// GuildBanDelete removes the given user from the guild bans
+// guildID : The ID of a Guild.
+// userID : The ID of a User
+func (s *Session) GuildBanDelete(guildID, userID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointGuildBan(guildID, userID), nil)
+ return
+}
+
+// GuildMembers returns a list of members for a guild.
+// guildID : The ID of a Guild.
+// offset : A number of members to skip
+// limit : max number of members to return (max 1000)
+func (s *Session) GuildMembers(guildID string, offset, limit int) (st []*Member, err error) {
+
+ uri := EndpointGuildMembers(guildID)
+
+ v := url.Values{}
+
+ if offset > 0 {
+ v.Set("offset", strconv.Itoa(offset))
+ }
+
+ if limit > 0 {
+ v.Set("limit", strconv.Itoa(limit))
+ }
+
+ if len(v) > 0 {
+ uri = fmt.Sprintf("%s?%s", uri, v.Encode())
+ }
+
+ body, err := s.Request("GET", uri, nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// GuildMember returns a member of a guild.
+// guildID : The ID of a Guild.
+// userID : The ID of a User
+func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) {
+
+ body, err := s.Request("GET", EndpointGuildMember(guildID, userID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// GuildMemberDelete removes the given user from the given guild.
+// guildID : The ID of a Guild.
+// userID : The ID of a User
+func (s *Session) GuildMemberDelete(guildID, userID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointGuildMember(guildID, userID), nil)
+ return
+}
+
+// GuildMemberEdit edits the roles of a member.
+// guildID : The ID of a Guild.
+// userID : The ID of a User.
+// roles : A list of role ID's to set on the member.
+func (s *Session) GuildMemberEdit(guildID, userID string, roles []string) (err error) {
+
+ data := struct {
+ Roles []string `json:"roles"`
+ }{roles}
+
+ _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+// GuildMemberMove moves a guild member from one voice channel to another/none
+// guildID : The ID of a Guild.
+// userID : The ID of a User.
+// channelID : The ID of a channel to move user to, or null?
+// NOTE : I am not entirely set on the name of this function and it may change
+// prior to the final 1.0.0 release of Discordgo
+func (s *Session) GuildMemberMove(guildID, userID, channelID string) (err error) {
+
+ data := struct {
+ ChannelID string `json:"channel_id"`
+ }{channelID}
+
+ _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+// GuildMemberNickname updates the nickname of a guild member
+// guildID : The ID of a guild
+// userID : The ID of a user
+func (s *Session) GuildMemberNickname(guildID, userID, nickname string) (err error) {
+
+ data := struct {
+ Nick string `json:"nick"`
+ }{nickname}
+
+ _, err = s.Request("PATCH", EndpointGuildMember(guildID, userID), data)
+ return
+}
+
+// GuildChannels returns an array of Channel structures for all channels of a
+// given guild.
+// guildID : The ID of a Guild.
+func (s *Session) GuildChannels(guildID string) (st []*Channel, err error) {
+
+ body, err := s.request("GET", EndpointGuildChannels(guildID), "", nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+
+ return
+}
+
+// GuildChannelCreate creates a new channel in the given guild
+// guildID : The ID of a Guild.
+// name : Name of the channel (2-100 chars length)
+// ctype : 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}
+
+ body, err := s.Request("POST", EndpointGuildChannels(guildID), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// 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.Request("PATCH", EndpointGuildChannels(guildID), channels)
+ return
+}
+
+// GuildInvites returns an array of Invite structures for the given guild
+// guildID : The ID of a Guild.
+func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) {
+ body, err := s.Request("GET", EndpointGuildInvites(guildID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// GuildRoles returns all roles for a given guild.
+// guildID : The ID of a Guild.
+func (s *Session) GuildRoles(guildID string) (st []*Role, err error) {
+
+ body, err := s.Request("GET", EndpointGuildRoles(guildID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+
+ return // TODO return pointer
+}
+
+// GuildRoleCreate returns a new Guild Role.
+// guildID: The ID of a Guild.
+func (s *Session) GuildRoleCreate(guildID string) (st *Role, err error) {
+
+ body, err := s.Request("POST", EndpointGuildRoles(guildID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+
+ return
+}
+
+// GuildRoleEdit updates an existing Guild Role with new values
+// guildID : The ID of a Guild.
+// roleID : The ID of a Role.
+// name : The name of the Role.
+// color : The color of the role (decimal, not hex).
+// hoist : Whether to display the role's users separately.
+// perm : The permissions for the role.
+func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist bool, perm int) (st *Role, err error) {
+
+ // Prevent sending a color int that is too big.
+ if color > 0xFFFFFF {
+ err = fmt.Errorf("color value cannot be larger than 0xFFFFFF")
+ }
+
+ data := struct {
+ Name string `json:"name"` // The color the role should have (as a decimal, not hex)
+ Color int `json:"color"` // Whether to display the role's users separately
+ Hoist bool `json:"hoist"` // The role's name (overwrites existing)
+ Permissions int `json:"permissions"` // The overall permissions number of the role (overwrites existing)
+ }{name, color, hoist, perm}
+
+ body, err := s.Request("PATCH", EndpointGuildRole(guildID, roleID), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+
+ return
+}
+
+// GuildRoleReorder reoders guild roles
+// guildID : The ID of a Guild.
+// roles : A list of ordered roles.
+func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, err error) {
+
+ body, err := s.Request("PATCH", EndpointGuildRoles(guildID), roles)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+
+ return
+}
+
+// GuildRoleDelete deletes an existing role.
+// guildID : The ID of a Guild.
+// roleID : The ID of a Role.
+func (s *Session) GuildRoleDelete(guildID, roleID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointGuildRole(guildID, roleID), nil)
+
+ return
+}
+
+// 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) {
+
+ body, err := s.Request("GET", EndpointGuildIntegrations(guildID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+
+ return
+}
+
+// GuildIntegrationCreate creates a Guild Integration.
+// guildID : The ID of a Guild.
+// integrationType : The Integration type.
+// integrationID : The ID of an integration.
+func (s *Session) GuildIntegrationCreate(guildID, integrationType, integrationID string) (err error) {
+
+ data := struct {
+ Type string `json:"type"`
+ ID string `json:"id"`
+ }{integrationType, integrationID}
+
+ _, err = s.Request("POST", EndpointGuildIntegrations(guildID), data)
+ return
+}
+
+// GuildIntegrationEdit edits a Guild Integration.
+// guildID : The ID of a Guild.
+// integrationType : The Integration type.
+// integrationID : The ID of an integration.
+// expireBehavior : The behavior when an integration subscription lapses (see the integration object documentation).
+// expireGracePeriod : Period (in seconds) where the integration will ignore lapsed subscriptions.
+// enableEmoticons : Whether emoticons should be synced for this integration (twitch only currently).
+func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBehavior, expireGracePeriod int, enableEmoticons bool) (err error) {
+
+ data := struct {
+ ExpireBehavior int `json:"expire_behavior"`
+ ExpireGracePeriod int `json:"expire_grace_period"`
+ EnableEmoticons bool `json:"enable_emoticons"`
+ }{expireBehavior, expireGracePeriod, enableEmoticons}
+
+ _, err = s.Request("PATCH", EndpointGuildIntegration(guildID, integrationID), data)
+ return
+}
+
+// GuildIntegrationDelete removes the given integration from the Guild.
+// guildID : The ID of a Guild.
+// integrationID : The ID of an integration.
+func (s *Session) GuildIntegrationDelete(guildID, integrationID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointGuildIntegration(guildID, integrationID), nil)
+ return
+}
+
+// GuildIntegrationSync syncs an integration.
+// guildID : The ID of a Guild.
+// integrationID : The ID of an integration.
+func (s *Session) GuildIntegrationSync(guildID, integrationID string) (err error) {
+
+ _, err = s.Request("POST", EndpointGuildIntegrationSync(guildID, integrationID), nil)
+ return
+}
+
+// GuildIcon returns an image.Image of a guild icon.
+// guildID : The ID of a Guild.
+func (s *Session) GuildIcon(guildID string) (img image.Image, err error) {
+ g, err := s.Guild(guildID)
+ if err != nil {
+ return
+ }
+
+ if g.Icon == "" {
+ err = errors.New("Guild does not have an icon set.")
+ return
+ }
+
+ body, err := s.Request("GET", EndpointGuildIcon(guildID, g.Icon), nil)
+ if err != nil {
+ return
+ }
+
+ img, _, err = image.Decode(bytes.NewReader(body))
+ return
+}
+
+// GuildSplash returns an image.Image of a guild splash image.
+// guildID : The ID of a Guild.
+func (s *Session) GuildSplash(guildID string) (img image.Image, err error) {
+ g, err := s.Guild(guildID)
+ if err != nil {
+ return
+ }
+
+ if g.Splash == "" {
+ err = errors.New("Guild does not have a splash set.")
+ return
+ }
+
+ body, err := s.Request("GET", EndpointGuildSplash(guildID, g.Splash), nil)
+ if err != nil {
+ return
+ }
+
+ img, _, err = image.Decode(bytes.NewReader(body))
+ return
+}
+
+// GuildEmbed returns the embed for a Guild.
+// guildID : The ID of a Guild.
+func (s *Session) GuildEmbed(guildID string) (st *GuildEmbed, err error) {
+
+ body, err := s.Request("GET", EndpointGuildEmbed(guildID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// GuildEmbedEdit returns the embed for a Guild.
+// guildID : The ID of a Guild.
+func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string) (err error) {
+
+ data := GuildEmbed{enabled, channelID}
+
+ _, err = s.Request("PATCH", EndpointGuildEmbed(guildID), data)
+ return
+}
+
+// ------------------------------------------------------------------------------------------------
+// Functions specific to Discord Channels
+// ------------------------------------------------------------------------------------------------
+
+// Channel returns a Channel strucutre of a specific Channel.
+// channelID : The ID of the Channel you want returned.
+func (s *Session) Channel(channelID string) (st *Channel, err error) {
+ body, err := s.Request("GET", EndpointChannel(channelID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ChannelEdit edits the given channel
+// channelID : The ID of a Channel
+// name : The new name to assign the channel.
+func (s *Session) ChannelEdit(channelID, name string) (st *Channel, err error) {
+
+ data := struct {
+ Name string `json:"name"`
+ }{name}
+
+ body, err := s.Request("PATCH", EndpointChannel(channelID), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ChannelDelete deletes the given channel
+// channelID : The ID of a Channel
+func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) {
+
+ body, err := s.Request("DELETE", EndpointChannel(channelID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ChannelTyping broadcasts to all members that authenticated user is typing in
+// the given channel.
+// channelID : The ID of a Channel
+func (s *Session) ChannelTyping(channelID string) (err error) {
+
+ _, err = s.Request("POST", EndpointChannelTyping(channelID), nil)
+ return
+}
+
+// ChannelMessages returns an array of Message structures for messages within
+// a given channel.
+// channelID : The ID of a Channel.
+// limit : The number messages that can be returned. (max 100)
+// beforeID : If provided all messages returned will be before given ID.
+// afterID : If provided all messages returned will be after given ID.
+func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID string) (st []*Message, err error) {
+
+ uri := EndpointChannelMessages(channelID)
+
+ v := url.Values{}
+ if limit > 0 {
+ v.Set("limit", strconv.Itoa(limit))
+ }
+ if afterID != "" {
+ v.Set("after", afterID)
+ }
+ if beforeID != "" {
+ v.Set("before", beforeID)
+ }
+ if len(v) > 0 {
+ uri = fmt.Sprintf("%s?%s", uri, v.Encode())
+ }
+
+ body, err := s.Request("GET", uri, nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ChannelMessage gets a single message by ID from a given channel.
+// channeld : The ID of a Channel
+// messageID : the ID of a Message
+func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err error) {
+
+ response, err := s.Request("GET", EndpointChannelMessage(channelID, messageID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(response, &st)
+ return
+}
+
+// ChannelMessageAck acknowledges and marks the given message as read
+// channeld : The ID of a Channel
+// messageID : the ID of a Message
+func (s *Session) ChannelMessageAck(channelID, messageID string) (err error) {
+
+ _, err = s.request("POST", EndpointChannelMessageAck(channelID, messageID), "", nil)
+ return
+}
+
+// channelMessageSend sends a message to the given channel.
+// channelID : The ID of a Channel.
+// content : The message to send.
+// tts : Whether to send the message with TTS.
+func (s *Session) channelMessageSend(channelID, content string, tts bool) (st *Message, err error) {
+
+ // TODO: nonce string ?
+ data := struct {
+ Content string `json:"content"`
+ TTS bool `json:"tts"`
+ }{content, tts}
+
+ // Send the message to the given channel
+ response, err := s.Request("POST", EndpointChannelMessages(channelID), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(response, &st)
+ return
+}
+
+// ChannelMessageSend sends a message to the given channel.
+// channelID : The ID of a Channel.
+// content : The message to send.
+func (s *Session) ChannelMessageSend(channelID string, content string) (st *Message, err error) {
+
+ return s.channelMessageSend(channelID, content, false)
+}
+
+// ChannelMessageSendTTS sends a message to the given channel with Text to Speech.
+// channelID : The ID of a Channel.
+// content : The message to send.
+func (s *Session) ChannelMessageSendTTS(channelID string, content string) (st *Message, err error) {
+
+ return s.channelMessageSend(channelID, content, true)
+}
+
+// ChannelMessageEdit edits an existing message, replacing it entirely with
+// the given content.
+// channeld : The ID of a Channel
+// messageID : the ID of a Message
+func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (st *Message, err error) {
+
+ data := struct {
+ Content string `json:"content"`
+ }{content}
+
+ response, err := s.Request("PATCH", EndpointChannelMessage(channelID, messageID), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(response, &st)
+ return
+}
+
+// ChannelMessageDelete deletes a message from the Channel.
+func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointChannelMessage(channelID, messageID), nil)
+ return
+}
+
+// ChannelMessagesBulkDelete bulk deletes the messages from the channel for the provided messageIDs.
+// If only one messageID is in the slice call channelMessageDelete funciton.
+// If the slice is empty do nothing.
+// channelID : The ID of the channel for the messages to delete.
+// messages : The IDs of the messages to be deleted. A slice of string IDs. A maximum of 100 messages.
+func (s *Session) ChannelMessagesBulkDelete(channelID string, messages []string) (err error) {
+
+ if len(messages) == 0 {
+ return
+ }
+
+ if len(messages) == 1 {
+ err = s.ChannelMessageDelete(channelID, messages[0])
+ return
+ }
+
+ if len(messages) > 100 {
+ messages = messages[:100]
+ }
+
+ data := struct {
+ Messages []string `json:"messages"`
+ }{messages}
+
+ _, err = s.Request("POST", EndpointChannelMessagesBulkDelete(channelID), data)
+ return
+}
+
+// ChannelMessagePin pins a message within a given channel.
+// channelID: The ID of a channel.
+// messageID: The ID of a message.
+func (s *Session) ChannelMessagePin(channelID, messageID string) (err error) {
+
+ _, err = s.Request("PUT", EndpointChannelMessagePin(channelID, messageID), nil)
+ return
+}
+
+// ChannelMessageUnpin unpins a message within a given channel.
+// channelID: The ID of a channel.
+// messageID: The ID of a message.
+func (s *Session) ChannelMessageUnpin(channelID, messageID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointChannelMessagePin(channelID, messageID), nil)
+ return
+}
+
+// ChannelMessagesPinned returns an array of Message structures for pinned messages
+// within a given channel
+// channelID : The ID of a Channel.
+func (s *Session) ChannelMessagesPinned(channelID string) (st []*Message, err error) {
+
+ body, err := s.Request("GET", EndpointChannelMessagesPins(channelID), nil)
+
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ChannelFileSend sends a file to the given channel.
+// channelID : The ID of a Channel.
+// io.Reader : A reader for the file contents.
+func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (st *Message, err error) {
+
+ body := &bytes.Buffer{}
+ bodywriter := multipart.NewWriter(body)
+
+ writer, err := bodywriter.CreateFormFile("file", name)
+ if err != nil {
+ return nil, err
+ }
+
+ _, err = io.Copy(writer, r)
+ if err != nil {
+ return
+ }
+
+ err = bodywriter.Close()
+ if err != nil {
+ return
+ }
+
+ response, err := s.request("POST", EndpointChannelMessages(channelID), bodywriter.FormDataContentType(), body.Bytes())
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(response, &st)
+ return
+}
+
+// ChannelInvites returns an array of Invite structures for the given channel
+// channelID : The ID of a Channel
+func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) {
+
+ body, err := s.Request("GET", EndpointChannelInvites(channelID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ChannelInviteCreate creates a new invite for the given channel.
+// channelID : The ID of a Channel
+// i : An Invite struct with the values MaxAge, MaxUses, Temporary,
+// and XkcdPass defined.
+func (s *Session) ChannelInviteCreate(channelID string, i Invite) (st *Invite, err error) {
+
+ data := struct {
+ MaxAge int `json:"max_age"`
+ MaxUses int `json:"max_uses"`
+ Temporary bool `json:"temporary"`
+ XKCDPass string `json:"xkcdpass"`
+ }{i.MaxAge, i.MaxUses, i.Temporary, i.XkcdPass}
+
+ body, err := s.Request("POST", EndpointChannelInvites(channelID), data)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ChannelPermissionSet creates a Permission Override for the given channel.
+// NOTE: This func name may changed. Using Set instead of Create because
+// you can both create a new override or update an override with this function.
+func (s *Session) ChannelPermissionSet(channelID, targetID, targetType string, allow, deny int) (err error) {
+
+ data := struct {
+ ID string `json:"id"`
+ Type string `json:"type"`
+ Allow int `json:"allow"`
+ Deny int `json:"deny"`
+ }{targetID, targetType, allow, deny}
+
+ _, err = s.Request("PUT", EndpointChannelPermission(channelID, targetID), data)
+ return
+}
+
+// ChannelPermissionDelete deletes a specific permission override for the given channel.
+// NOTE: Name of this func may change.
+func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error) {
+
+ _, err = s.Request("DELETE", EndpointChannelPermission(channelID, targetID), nil)
+ return
+}
+
+// ------------------------------------------------------------------------------------------------
+// Functions specific to Discord Invites
+// ------------------------------------------------------------------------------------------------
+
+// Invite returns an Invite structure of the given invite
+// inviteID : The invite code (or maybe xkcdpass?)
+func (s *Session) Invite(inviteID string) (st *Invite, err error) {
+
+ body, err := s.Request("GET", EndpointInvite(inviteID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// InviteDelete deletes an existing invite
+// inviteID : the code (or maybe xkcdpass?) of an invite
+func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) {
+
+ body, err := s.Request("DELETE", EndpointInvite(inviteID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// InviteAccept accepts an Invite to a Guild or Channel
+// inviteID : The invite code (or maybe xkcdpass?)
+func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) {
+
+ body, err := s.Request("POST", EndpointInvite(inviteID), nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ------------------------------------------------------------------------------------------------
+// Functions specific to Discord Voice
+// ------------------------------------------------------------------------------------------------
+
+// VoiceRegions returns the voice server regions
+func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) {
+
+ body, err := s.Request("GET", EndpointVoiceRegions, nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// VoiceICE returns the voice server ICE information
+func (s *Session) VoiceICE() (st *VoiceICE, err error) {
+
+ body, err := s.Request("GET", EndpointVoiceIce, nil)
+ if err != nil {
+ return
+ }
+
+ err = unmarshal(body, &st)
+ return
+}
+
+// ------------------------------------------------------------------------------------------------
+// Functions specific to Discord Websockets
+// ------------------------------------------------------------------------------------------------
+
+// Gateway returns the a websocket Gateway address
+func (s *Session) Gateway() (gateway string, err error) {
+
+ response, err := s.Request("GET", EndpointGateway, nil)
+ if err != nil {
+ return
+ }
+
+ temp := struct {
+ URL string `json:"url"`
+ }{}
+
+ err = unmarshal(response, &temp)
+ if err != nil {
+ return
+ }
+
+ gateway = temp.URL
+ return
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/state.go b/vendor/github.com/bwmarrin/discordgo/state.go
new file mode 100644
index 00000000..e9eb4d67
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/state.go
@@ -0,0 +1,746 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains code related to state tracking. If enabled, state
+// tracking will capture the initial READY packet and many other websocket
+// events and maintain an in-memory state of of guilds, channels, users, and
+// so forth. This information can be accessed through the Session.State struct.
+
+package discordgo
+
+import (
+ "errors"
+ "sync"
+)
+
+// ErrNilState is returned when the state is nil.
+var ErrNilState = errors.New("State not instantiated, please use discordgo.New() or assign Session.State.")
+
+// A State contains the current known state.
+// As discord sends this in a READY blob, it seems reasonable to simply
+// use that struct as the data store.
+type State struct {
+ sync.RWMutex
+ Ready
+
+ MaxMessageCount int
+ TrackChannels bool
+ TrackEmojis bool
+ TrackMembers bool
+ TrackRoles bool
+ TrackVoice bool
+
+ guildMap map[string]*Guild
+ channelMap map[string]*Channel
+}
+
+// NewState creates an empty state.
+func NewState() *State {
+ return &State{
+ Ready: Ready{
+ PrivateChannels: []*Channel{},
+ Guilds: []*Guild{},
+ },
+ TrackChannels: true,
+ TrackEmojis: true,
+ TrackMembers: true,
+ TrackRoles: true,
+ TrackVoice: true,
+ guildMap: make(map[string]*Guild),
+ channelMap: make(map[string]*Channel),
+ }
+}
+
+// OnReady takes a Ready event and updates all internal state.
+func (s *State) OnReady(r *Ready) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ s.Ready = *r
+
+ for _, g := range s.Guilds {
+ s.guildMap[g.ID] = g
+
+ for _, c := range g.Channels {
+ c.GuildID = g.ID
+ s.channelMap[c.ID] = c
+ }
+ }
+
+ for _, c := range s.PrivateChannels {
+ s.channelMap[c.ID] = c
+ }
+
+ return nil
+}
+
+// GuildAdd adds a guild to the current world state, or
+// updates it if it already exists.
+func (s *State) GuildAdd(guild *Guild) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ // Update the channels to point to the right guild, adding them to the channelMap as we go
+ for _, c := range guild.Channels {
+ c.GuildID = guild.ID
+ s.channelMap[c.ID] = c
+ }
+
+ // If the guild exists, replace it.
+ if g, ok := s.guildMap[guild.ID]; ok {
+ // If this guild already exists with data, don't stomp on props.
+ if g.Unavailable != nil && !*g.Unavailable {
+ guild.Members = g.Members
+ guild.Presences = g.Presences
+ guild.Channels = g.Channels
+ guild.VoiceStates = g.VoiceStates
+ }
+
+ *g = *guild
+ return nil
+ }
+
+ s.Guilds = append(s.Guilds, guild)
+ s.guildMap[guild.ID] = guild
+
+ return nil
+}
+
+// GuildRemove removes a guild from current world state.
+func (s *State) GuildRemove(guild *Guild) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ _, err := s.Guild(guild.ID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ delete(s.guildMap, guild.ID)
+
+ for i, g := range s.Guilds {
+ if g.ID == guild.ID {
+ s.Guilds = append(s.Guilds[:i], s.Guilds[i+1:]...)
+ return nil
+ }
+ }
+
+ return nil
+}
+
+// Guild gets a guild by ID.
+// Useful for querying if @me is in a guild:
+// _, err := discordgo.Session.State.Guild(guildID)
+// isInGuild := err == nil
+func (s *State) Guild(guildID string) (*Guild, error) {
+ if s == nil {
+ return nil, ErrNilState
+ }
+
+ s.RLock()
+ defer s.RUnlock()
+
+ if g, ok := s.guildMap[guildID]; ok {
+ return g, nil
+ }
+
+ return nil, errors.New("Guild not found.")
+}
+
+// TODO: Consider moving Guild state update methods onto *Guild.
+
+// MemberAdd adds a member to the current world state, or
+// updates it if it already exists.
+func (s *State) MemberAdd(member *Member) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ guild, err := s.Guild(member.GuildID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ for i, m := range guild.Members {
+ if m.User.ID == member.User.ID {
+ guild.Members[i] = member
+ return nil
+ }
+ }
+
+ guild.Members = append(guild.Members, member)
+ return nil
+}
+
+// MemberRemove removes a member from current world state.
+func (s *State) MemberRemove(member *Member) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ guild, err := s.Guild(member.GuildID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ for i, m := range guild.Members {
+ if m.User.ID == member.User.ID {
+ guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)
+ return nil
+ }
+ }
+
+ return errors.New("Member not found.")
+}
+
+// Member gets a member by ID from a guild.
+func (s *State) Member(guildID, userID string) (*Member, error) {
+ if s == nil {
+ return nil, ErrNilState
+ }
+
+ guild, err := s.Guild(guildID)
+ if err != nil {
+ return nil, err
+ }
+
+ s.RLock()
+ defer s.RUnlock()
+
+ for _, m := range guild.Members {
+ if m.User.ID == userID {
+ return m, nil
+ }
+ }
+
+ return nil, errors.New("Member not found.")
+}
+
+// RoleAdd adds a role to the current world state, or
+// updates it if it already exists.
+func (s *State) RoleAdd(guildID string, role *Role) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ guild, err := s.Guild(guildID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ for i, r := range guild.Roles {
+ if r.ID == role.ID {
+ guild.Roles[i] = role
+ return nil
+ }
+ }
+
+ guild.Roles = append(guild.Roles, role)
+ return nil
+}
+
+// RoleRemove removes a role from current world state by ID.
+func (s *State) RoleRemove(guildID, roleID string) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ guild, err := s.Guild(guildID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ for i, r := range guild.Roles {
+ if r.ID == roleID {
+ guild.Roles = append(guild.Roles[:i], guild.Roles[i+1:]...)
+ return nil
+ }
+ }
+
+ return errors.New("Role not found.")
+}
+
+// Role gets a role by ID from a guild.
+func (s *State) Role(guildID, roleID string) (*Role, error) {
+ if s == nil {
+ return nil, ErrNilState
+ }
+
+ guild, err := s.Guild(guildID)
+ if err != nil {
+ return nil, err
+ }
+
+ s.RLock()
+ defer s.RUnlock()
+
+ for _, r := range guild.Roles {
+ if r.ID == roleID {
+ return r, nil
+ }
+ }
+
+ return nil, errors.New("Role not found.")
+}
+
+// ChannelAdd adds a guild to the current world state, or
+// updates it if it already exists.
+// Channels may exist either as PrivateChannels or inside
+// a guild.
+func (s *State) ChannelAdd(channel *Channel) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ // If the channel exists, replace it
+ if c, ok := s.channelMap[channel.ID]; ok {
+ channel.Messages = c.Messages
+ channel.PermissionOverwrites = c.PermissionOverwrites
+
+ *c = *channel
+ return nil
+ }
+
+ if channel.IsPrivate {
+ s.PrivateChannels = append(s.PrivateChannels, channel)
+ } else {
+ guild, ok := s.guildMap[channel.GuildID]
+ if !ok {
+ return errors.New("Guild for channel not found.")
+ }
+
+ guild.Channels = append(guild.Channels, channel)
+ }
+
+ s.channelMap[channel.ID] = channel
+
+ return nil
+}
+
+// ChannelRemove removes a channel from current world state.
+func (s *State) ChannelRemove(channel *Channel) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ _, err := s.Channel(channel.ID)
+ if err != nil {
+ return err
+ }
+
+ if channel.IsPrivate {
+ s.Lock()
+ defer s.Unlock()
+
+ for i, c := range s.PrivateChannels {
+ if c.ID == channel.ID {
+ s.PrivateChannels = append(s.PrivateChannels[:i], s.PrivateChannels[i+1:]...)
+ break
+ }
+ }
+ } else {
+ guild, err := s.Guild(channel.GuildID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ for i, c := range guild.Channels {
+ if c.ID == channel.ID {
+ guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
+ break
+ }
+ }
+ }
+
+ delete(s.channelMap, channel.ID)
+
+ return nil
+}
+
+// GuildChannel gets a channel by ID from a guild.
+// This method is Deprecated, use Channel(channelID)
+func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) {
+ return s.Channel(channelID)
+}
+
+// PrivateChannel gets a private channel by ID.
+// This method is Deprecated, use Channel(channelID)
+func (s *State) PrivateChannel(channelID string) (*Channel, error) {
+ return s.Channel(channelID)
+}
+
+// Channel gets a channel by ID, it will look in all guilds an private channels.
+func (s *State) Channel(channelID string) (*Channel, error) {
+ if s == nil {
+ return nil, ErrNilState
+ }
+
+ s.RLock()
+ defer s.RUnlock()
+
+ if c, ok := s.channelMap[channelID]; ok {
+ return c, nil
+ }
+
+ return nil, errors.New("Channel not found.")
+}
+
+// Emoji returns an emoji for a guild and emoji id.
+func (s *State) Emoji(guildID, emojiID string) (*Emoji, error) {
+ if s == nil {
+ return nil, ErrNilState
+ }
+
+ guild, err := s.Guild(guildID)
+ if err != nil {
+ return nil, err
+ }
+
+ s.RLock()
+ defer s.RUnlock()
+
+ for _, e := range guild.Emojis {
+ if e.ID == emojiID {
+ return e, nil
+ }
+ }
+
+ return nil, errors.New("Emoji not found.")
+}
+
+// EmojiAdd adds an emoji to the current world state.
+func (s *State) EmojiAdd(guildID string, emoji *Emoji) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ guild, err := s.Guild(guildID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ for i, e := range guild.Emojis {
+ if e.ID == emoji.ID {
+ guild.Emojis[i] = emoji
+ return nil
+ }
+ }
+
+ guild.Emojis = append(guild.Emojis, emoji)
+ return nil
+}
+
+// EmojisAdd adds multiple emojis to the world state.
+func (s *State) EmojisAdd(guildID string, emojis []*Emoji) error {
+ for _, e := range emojis {
+ if err := s.EmojiAdd(guildID, e); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// MessageAdd adds a message to the current world state, or updates it if it exists.
+// If the channel cannot be found, the message is discarded.
+// Messages are kept in state up to s.MaxMessageCount
+func (s *State) MessageAdd(message *Message) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ c, err := s.Channel(message.ChannelID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ // If the message exists, merge in the new message contents.
+ for _, m := range c.Messages {
+ if m.ID == message.ID {
+ if message.Content != "" {
+ m.Content = message.Content
+ }
+ if message.EditedTimestamp != "" {
+ m.EditedTimestamp = message.EditedTimestamp
+ }
+ if message.Mentions != nil {
+ m.Mentions = message.Mentions
+ }
+ if message.Embeds != nil {
+ m.Embeds = message.Embeds
+ }
+ if message.Attachments != nil {
+ m.Attachments = message.Attachments
+ }
+
+ return nil
+ }
+ }
+
+ c.Messages = append(c.Messages, message)
+
+ if len(c.Messages) > s.MaxMessageCount {
+ c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:]
+ }
+ return nil
+}
+
+// MessageRemove removes a message from the world state.
+func (s *State) MessageRemove(message *Message) error {
+ if s == nil {
+ return ErrNilState
+ }
+
+ c, err := s.Channel(message.ChannelID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ for i, m := range c.Messages {
+ if m.ID == message.ID {
+ c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
+ return nil
+ }
+ }
+
+ return errors.New("Message not found.")
+}
+
+func (s *State) voiceStateUpdate(update *VoiceStateUpdate) error {
+ guild, err := s.Guild(update.GuildID)
+ if err != nil {
+ return err
+ }
+
+ s.Lock()
+ defer s.Unlock()
+
+ // Handle Leaving Channel
+ if update.ChannelID == "" {
+ for i, state := range guild.VoiceStates {
+ if state.UserID == update.UserID {
+ guild.VoiceStates = append(guild.VoiceStates[:i], guild.VoiceStates[i+1:]...)
+ return nil
+ }
+ }
+ } else {
+ for i, state := range guild.VoiceStates {
+ if state.UserID == update.UserID {
+ guild.VoiceStates[i] = update.VoiceState
+ return nil
+ }
+ }
+
+ guild.VoiceStates = append(guild.VoiceStates, update.VoiceState)
+ }
+
+ return nil
+}
+
+// Message gets a message by channel and message ID.
+func (s *State) Message(channelID, messageID string) (*Message, error) {
+ if s == nil {
+ return nil, ErrNilState
+ }
+
+ c, err := s.Channel(channelID)
+ if err != nil {
+ return nil, err
+ }
+
+ s.RLock()
+ defer s.RUnlock()
+
+ for _, m := range c.Messages {
+ if m.ID == messageID {
+ return m, nil
+ }
+ }
+
+ return nil, errors.New("Message not found.")
+}
+
+// onInterface handles all events related to states.
+func (s *State) onInterface(se *Session, i interface{}) (err error) {
+ if s == nil {
+ return ErrNilState
+ }
+ if !se.StateEnabled {
+ return nil
+ }
+
+ switch t := i.(type) {
+ case *Ready:
+ err = s.OnReady(t)
+ case *GuildCreate:
+ err = s.GuildAdd(t.Guild)
+ case *GuildUpdate:
+ err = s.GuildAdd(t.Guild)
+ case *GuildDelete:
+ err = s.GuildRemove(t.Guild)
+ case *GuildMemberAdd:
+ if s.TrackMembers {
+ err = s.MemberAdd(t.Member)
+ }
+ case *GuildMemberUpdate:
+ if s.TrackMembers {
+ err = s.MemberAdd(t.Member)
+ }
+ case *GuildMemberRemove:
+ if s.TrackMembers {
+ err = s.MemberRemove(t.Member)
+ }
+ case *GuildRoleCreate:
+ if s.TrackRoles {
+ err = s.RoleAdd(t.GuildID, t.Role)
+ }
+ case *GuildRoleUpdate:
+ if s.TrackRoles {
+ err = s.RoleAdd(t.GuildID, t.Role)
+ }
+ case *GuildRoleDelete:
+ if s.TrackRoles {
+ err = s.RoleRemove(t.GuildID, t.RoleID)
+ }
+ case *GuildEmojisUpdate:
+ if s.TrackEmojis {
+ err = s.EmojisAdd(t.GuildID, t.Emojis)
+ }
+ case *ChannelCreate:
+ if s.TrackChannels {
+ err = s.ChannelAdd(t.Channel)
+ }
+ case *ChannelUpdate:
+ if s.TrackChannels {
+ err = s.ChannelAdd(t.Channel)
+ }
+ case *ChannelDelete:
+ if s.TrackChannels {
+ err = s.ChannelRemove(t.Channel)
+ }
+ case *MessageCreate:
+ if s.MaxMessageCount != 0 {
+ err = s.MessageAdd(t.Message)
+ }
+ case *MessageUpdate:
+ if s.MaxMessageCount != 0 {
+ err = s.MessageAdd(t.Message)
+ }
+ case *MessageDelete:
+ if s.MaxMessageCount != 0 {
+ err = s.MessageRemove(t.Message)
+ }
+ case *VoiceStateUpdate:
+ if s.TrackVoice {
+ err = s.voiceStateUpdate(t)
+ }
+ }
+
+ return
+}
+
+// UserChannelPermissions returns the permission of a user in a channel.
+// userID : The ID of the user to calculate permissions for.
+// channelID : The ID of the channel to calculate permission for.
+func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) {
+
+ channel, err := s.Channel(channelID)
+ if err != nil {
+ return
+ }
+
+ guild, err := s.Guild(channel.GuildID)
+ if err != nil {
+ return
+ }
+
+ if userID == guild.OwnerID {
+ apermissions = PermissionAll
+ return
+ }
+
+ member, err := s.Member(guild.ID, userID)
+ if err != nil {
+ return
+ }
+
+ for _, role := range guild.Roles {
+ for _, roleID := range member.Roles {
+ if role.ID == roleID {
+ apermissions |= role.Permissions
+ break
+ }
+ }
+ }
+
+ if apermissions&PermissionManageRoles > 0 {
+ apermissions |= PermissionAll
+ }
+
+ // Member overwrites can override role overrides, so do two passes
+ for _, overwrite := range channel.PermissionOverwrites {
+ for _, roleID := range member.Roles {
+ if overwrite.Type == "role" && roleID == overwrite.ID {
+ apermissions &= ^overwrite.Deny
+ apermissions |= overwrite.Allow
+ break
+ }
+ }
+ }
+
+ for _, overwrite := range channel.PermissionOverwrites {
+ if overwrite.Type == "member" && overwrite.ID == userID {
+ apermissions &= ^overwrite.Deny
+ apermissions |= overwrite.Allow
+ break
+ }
+ }
+
+ if apermissions&PermissionManageRoles > 0 {
+ apermissions |= PermissionAllChannel
+ }
+
+ return
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/structs.go b/vendor/github.com/bwmarrin/discordgo/structs.go
new file mode 100644
index 00000000..19a291f8
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/structs.go
@@ -0,0 +1,521 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains all structures for the discordgo package. These
+// may be moved about later into separate files but I find it easier to have
+// them all located together.
+
+package discordgo
+
+import (
+ "encoding/json"
+ "reflect"
+ "sync"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+// A Session represents a connection to the Discord API.
+type Session struct {
+ sync.RWMutex
+
+ // General configurable settings.
+
+ // Authentication token for this session
+ Token string
+
+ // Debug for printing JSON request/responses
+ Debug bool // Deprecated, will be removed.
+ LogLevel int
+
+ // Should the session reconnect the websocket on errors.
+ ShouldReconnectOnError bool
+
+ // Should the session request compressed websocket data.
+ Compress bool
+
+ // Sharding
+ ShardID int
+ ShardCount int
+
+ // Should state tracking be enabled.
+ // State tracking is the best way for getting the the users
+ // active guilds and the members of the guilds.
+ StateEnabled bool
+
+ // Exposed but should not be modified by User.
+
+ // Whether the Data Websocket is ready
+ DataReady bool // NOTE: Maye be deprecated soon
+
+ // Status stores the currect status of the websocket connection
+ // this is being tested, may stay, may go away.
+ status int32
+
+ // Whether the Voice Websocket is ready
+ VoiceReady bool // NOTE: Deprecated.
+
+ // Whether the UDP Connection is ready
+ UDPReady bool // NOTE: Deprecated
+
+ // Stores a mapping of guild id's to VoiceConnections
+ VoiceConnections map[string]*VoiceConnection
+
+ // Managed state object, updated internally with events when
+ // StateEnabled is true.
+ State *State
+
+ handlersMu sync.RWMutex
+ // This is a mapping of event struct to a reflected value
+ // for event handlers.
+ // We store the reflected value instead of the function
+ // reference as it is more performant, instead of re-reflecting
+ // the function each event.
+ handlers map[interface{}][]reflect.Value
+
+ // The websocket connection.
+ wsConn *websocket.Conn
+
+ // When nil, the session is not listening.
+ listening chan interface{}
+
+ // used to deal with rate limits
+ // may switch to slices later
+ // TODO: performance test map vs slices
+ rateLimit rateLimitMutex
+
+ // sequence tracks the current gateway api websocket sequence number
+ sequence int
+
+ // stores sessions current Discord Gateway
+ gateway string
+
+ // stores session ID of current Gateway connection
+ sessionID string
+
+ // used to make sure gateway websocket writes do not happen concurrently
+ wsMutex sync.Mutex
+}
+
+type rateLimitMutex struct {
+ sync.Mutex
+ url map[string]*sync.Mutex
+ // bucket map[string]*sync.Mutex // TODO :)
+}
+
+// A Resumed struct holds the data received in a RESUMED event
+type Resumed struct {
+ HeartbeatInterval time.Duration `json:"heartbeat_interval"`
+ Trace []string `json:"_trace"`
+}
+
+// A VoiceRegion stores data for a specific voice region server.
+type VoiceRegion struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Hostname string `json:"sample_hostname"`
+ Port int `json:"sample_port"`
+}
+
+// A VoiceICE stores data for voice ICE servers.
+type VoiceICE struct {
+ TTL string `json:"ttl"`
+ Servers []*ICEServer `json:"servers"`
+}
+
+// A ICEServer stores data for a specific voice ICE server.
+type ICEServer struct {
+ URL string `json:"url"`
+ Username string `json:"username"`
+ Credential string `json:"credential"`
+}
+
+// A Invite stores all data related to a specific Discord Guild or Channel invite.
+type Invite struct {
+ Guild *Guild `json:"guild"`
+ Channel *Channel `json:"channel"`
+ Inviter *User `json:"inviter"`
+ Code string `json:"code"`
+ CreatedAt string `json:"created_at"` // TODO make timestamp
+ MaxAge int `json:"max_age"`
+ Uses int `json:"uses"`
+ MaxUses int `json:"max_uses"`
+ XkcdPass string `json:"xkcdpass"`
+ Revoked bool `json:"revoked"`
+ Temporary bool `json:"temporary"`
+}
+
+// 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 string `json:"type"`
+ LastMessageID string `json:"last_message_id"`
+ Position int `json:"position"`
+ Bitrate int `json:"bitrate"`
+ IsPrivate bool `json:"is_private"`
+ Recipient *User `json:"recipient"`
+ Messages []*Message `json:"-"`
+ PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
+}
+
+// A PermissionOverwrite holds permission overwrite data for a Channel
+type PermissionOverwrite struct {
+ ID string `json:"id"`
+ Type string `json:"type"`
+ Deny int `json:"deny"`
+ Allow int `json:"allow"`
+}
+
+// Emoji struct holds data related to Emoji's
+type Emoji struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Roles []string `json:"roles"`
+ Managed bool `json:"managed"`
+ RequireColons bool `json:"require_colons"`
+}
+
+// VerificationLevel type defination
+type VerificationLevel int
+
+// Constants for VerificationLevel levels from 0 to 3 inclusive
+const (
+ VerificationLevelNone VerificationLevel = iota
+ VerificationLevelLow
+ VerificationLevelMedium
+ VerificationLevelHigh
+)
+
+// 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 string `json:"joined_at"` // make this a timestamp
+ Splash string `json:"splash"`
+ AfkTimeout int `json:"afk_timeout"`
+ 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"`
+}
+
+// A GuildParams stores all the data needed to update discord guild settings
+type GuildParams struct {
+ Name string `json:"name"`
+ Region string `json:"region"`
+ VerificationLevel *VerificationLevel `json:"verification_level"`
+}
+
+// A Role stores information about Discord guild member roles.
+type Role struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Managed bool `json:"managed"`
+ Hoist bool `json:"hoist"`
+ Color int `json:"color"`
+ Position int `json:"position"`
+ Permissions int `json:"permissions"`
+}
+
+// A VoiceState stores the voice states of Guilds
+type VoiceState struct {
+ UserID string `json:"user_id"`
+ SessionID string `json:"session_id"`
+ ChannelID string `json:"channel_id"`
+ GuildID string `json:"guild_id"`
+ Suppress bool `json:"suppress"`
+ SelfMute bool `json:"self_mute"`
+ SelfDeaf bool `json:"self_deaf"`
+ Mute bool `json:"mute"`
+ Deaf bool `json:"deaf"`
+}
+
+// A Presence stores the online, offline, or idle and game status of Guild members.
+type Presence struct {
+ User *User `json:"user"`
+ Status string `json:"status"`
+ Game *Game `json:"game"`
+}
+
+// A Game struct holds the name of the "playing .." game for a user
+type Game struct {
+ Name string `json:"name"`
+ Type int `json:"type"`
+ URL string `json:"url"`
+}
+
+// A Member stores user information for Guild members.
+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"`
+}
+
+// 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"`
+}
+
+// A Settings stores data for a specific users Discord client settings.
+type Settings struct {
+ RenderEmbeds bool `json:"render_embeds"`
+ InlineEmbedMedia bool `json:"inline_embed_media"`
+ InlineAttachmentMedia bool `json:"inline_attachment_media"`
+ EnableTtsCommand bool `json:"enable_tts_command"`
+ MessageDisplayCompact bool `json:"message_display_compact"`
+ ShowCurrentGame bool `json:"show_current_game"`
+ AllowEmailFriendRequest bool `json:"allow_email_friend_request"`
+ ConvertEmoticons bool `json:"convert_emoticons"`
+ Locale string `json:"locale"`
+ Theme string `json:"theme"`
+ GuildPositions []string `json:"guild_positions"`
+ RestrictedGuilds []string `json:"restricted_guilds"`
+ FriendSourceFlags *FriendSourceFlags `json:"friend_source_flags"`
+}
+
+// FriendSourceFlags stores ... TODO :)
+type FriendSourceFlags struct {
+ All bool `json:"all"`
+ MutualGuilds bool `json:"mutual_guilds"`
+ MutualFriends bool `json:"mutual_friends"`
+}
+
+// An Event provides a basic initial struct for all websocket event.
+type Event struct {
+ Operation int `json:"op"`
+ Sequence int `json:"s"`
+ Type string `json:"t"`
+ RawData json.RawMessage `json:"d"`
+ Struct interface{} `json:"-"`
+}
+
+// A Ready stores all data for the websocket READY event.
+type Ready struct {
+ Version int `json:"v"`
+ SessionID string `json:"session_id"`
+ HeartbeatInterval time.Duration `json:"heartbeat_interval"`
+ User *User `json:"user"`
+ ReadState []*ReadState `json:"read_state"`
+ PrivateChannels []*Channel `json:"private_channels"`
+ Guilds []*Guild `json:"guilds"`
+
+ // Undocumented fields
+ Settings *Settings `json:"user_settings"`
+ UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
+ Relationships []*Relationship `json:"relationships"`
+ Presences []*Presence `json:"presences"`
+}
+
+// A Relationship between the logged in user and Relationship.User
+type Relationship struct {
+ User *User `json:"user"`
+ Type int `json:"type"` // 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req
+ ID string `json:"id"`
+}
+
+// A TooManyRequests struct holds information received from Discord
+// when receiving a HTTP 429 response.
+type TooManyRequests struct {
+ Bucket string `json:"bucket"`
+ Message string `json:"message"`
+ RetryAfter time.Duration `json:"retry_after"`
+}
+
+// A ReadState stores data on the read state of channels.
+type ReadState struct {
+ MentionCount int `json:"mention_count"`
+ LastMessageID string `json:"last_message_id"`
+ ID string `json:"id"`
+}
+
+// A TypingStart stores data for the typing start websocket event.
+type TypingStart struct {
+ UserID string `json:"user_id"`
+ ChannelID string `json:"channel_id"`
+ Timestamp int `json:"timestamp"`
+}
+
+// A PresenceUpdate stores data for the presence update websocket event.
+type PresenceUpdate struct {
+ Presence
+ GuildID string `json:"guild_id"`
+ Roles []string `json:"roles"`
+}
+
+// A MessageAck stores data for the message ack websocket event.
+type MessageAck struct {
+ MessageID string `json:"message_id"`
+ ChannelID string `json:"channel_id"`
+}
+
+// A GuildIntegrationsUpdate stores data for the guild integrations update
+// websocket event.
+type GuildIntegrationsUpdate struct {
+ GuildID string `json:"guild_id"`
+}
+
+// A GuildRole stores data for guild role websocket events.
+type GuildRole struct {
+ Role *Role `json:"role"`
+ GuildID string `json:"guild_id"`
+}
+
+// A GuildRoleDelete stores data for the guild role delete websocket event.
+type GuildRoleDelete struct {
+ RoleID string `json:"role_id"`
+ GuildID string `json:"guild_id"`
+}
+
+// A GuildBan stores data for a guild ban.
+type GuildBan struct {
+ User *User `json:"user"`
+ GuildID string `json:"guild_id"`
+}
+
+// A GuildEmojisUpdate stores data for a guild emoji update event.
+type GuildEmojisUpdate struct {
+ GuildID string `json:"guild_id"`
+ Emojis []*Emoji `json:"emojis"`
+}
+
+// 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 UserGuildSettingsChannelOverride stores data for a channel override for a users guild settings.
+type UserGuildSettingsChannelOverride struct {
+ Muted bool `json:"muted"`
+ MessageNotifications int `json:"message_notifications"`
+ ChannelID string `json:"channel_id"`
+}
+
+// A UserGuildSettings stores data for a users guild settings.
+type UserGuildSettings struct {
+ SupressEveryone bool `json:"suppress_everyone"`
+ Muted bool `json:"muted"`
+ MobilePush bool `json:"mobile_push"`
+ MessageNotifications int `json:"message_notifications"`
+ GuildID string `json:"guild_id"`
+ ChannelOverrides []*UserGuildSettingsChannelOverride `json:"channel_overrides"`
+}
+
+// A UserGuildSettingsEdit stores data for editing UserGuildSettings
+type UserGuildSettingsEdit struct {
+ SupressEveryone bool `json:"suppress_everyone"`
+ Muted bool `json:"muted"`
+ MobilePush bool `json:"mobile_push"`
+ MessageNotifications int `json:"message_notifications"`
+ ChannelOverrides map[string]*UserGuildSettingsChannelOverride `json:"channel_overrides"`
+}
+
+// Constants for the different bit offsets of text channel permissions
+const (
+ PermissionReadMessages = 1 << (iota + 10)
+ PermissionSendMessages
+ PermissionSendTTSMessages
+ PermissionManageMessages
+ PermissionEmbedLinks
+ PermissionAttachFiles
+ PermissionReadMessageHistory
+ PermissionMentionEveryone
+)
+
+// Constants for the different bit offsets of voice permissions
+const (
+ PermissionVoiceConnect = 1 << (iota + 20)
+ PermissionVoiceSpeak
+ PermissionVoiceMuteMembers
+ PermissionVoiceDeafenMembers
+ PermissionVoiceMoveMembers
+ PermissionVoiceUseVAD
+)
+
+// Constants for the different bit offsets of general permissions
+const (
+ PermissionCreateInstantInvite = 1 << iota
+ PermissionKickMembers
+ PermissionBanMembers
+ PermissionManageRoles
+ PermissionManageChannels
+ PermissionManageServer
+
+ PermissionAllText = PermissionReadMessages |
+ PermissionSendMessages |
+ PermissionSendTTSMessages |
+ PermissionManageMessages |
+ PermissionEmbedLinks |
+ PermissionAttachFiles |
+ PermissionReadMessageHistory |
+ PermissionMentionEveryone
+ PermissionAllVoice = PermissionVoiceConnect |
+ PermissionVoiceSpeak |
+ PermissionVoiceMuteMembers |
+ PermissionVoiceDeafenMembers |
+ PermissionVoiceMoveMembers |
+ PermissionVoiceUseVAD
+ PermissionAllChannel = PermissionAllText |
+ PermissionAllVoice |
+ PermissionCreateInstantInvite |
+ PermissionManageRoles |
+ PermissionManageChannels
+ PermissionAll = PermissionAllChannel |
+ PermissionKickMembers |
+ PermissionBanMembers |
+ PermissionManageServer
+)
diff --git a/vendor/github.com/bwmarrin/discordgo/voice.go b/vendor/github.com/bwmarrin/discordgo/voice.go
new file mode 100644
index 00000000..094aa59e
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/voice.go
@@ -0,0 +1,853 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains code related to Discord voice suppport
+
+package discordgo
+
+import (
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "log"
+ "net"
+ "runtime"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/gorilla/websocket"
+ "golang.org/x/crypto/nacl/secretbox"
+)
+
+// ------------------------------------------------------------------------------------------------
+// Code related to both VoiceConnection Websocket and UDP connections.
+// ------------------------------------------------------------------------------------------------
+
+// A VoiceConnection struct holds all the data and functions related to a Discord Voice Connection.
+type VoiceConnection struct {
+ sync.RWMutex
+
+ Debug bool // If true, print extra logging -- DEPRECATED
+ LogLevel int
+ Ready bool // If true, voice is ready to send/receive audio
+ UserID string
+ GuildID string
+ ChannelID string
+ deaf bool
+ mute bool
+ speaking bool
+ reconnecting bool // If true, voice connection is trying to reconnect
+
+ OpusSend chan []byte // Chan for sending opus audio
+ OpusRecv chan *Packet // Chan for receiving opus audio
+
+ wsConn *websocket.Conn
+ wsMutex sync.Mutex
+ udpConn *net.UDPConn
+ session *Session
+
+ sessionID string
+ token string
+ endpoint string
+
+ // Used to send a close signal to goroutines
+ close chan struct{}
+
+ // Used to allow blocking until connected
+ connected chan bool
+
+ // Used to pass the sessionid from onVoiceStateUpdate
+ // sessionRecv chan string UNUSED ATM
+
+ op4 voiceOP4
+ op2 voiceOP2
+
+ voiceSpeakingUpdateHandlers []VoiceSpeakingUpdateHandler
+}
+
+// VoiceSpeakingUpdateHandler type provides a function defination for the
+// VoiceSpeakingUpdate event
+type VoiceSpeakingUpdateHandler func(vc *VoiceConnection, vs *VoiceSpeakingUpdate)
+
+// Speaking sends a speaking notification to Discord over the voice websocket.
+// This must be sent as true prior to sending audio and should be set to false
+// once finished sending audio.
+// b : Send true if speaking, false if not.
+func (v *VoiceConnection) Speaking(b bool) (err error) {
+
+ v.log(LogDebug, "called (%t)", b)
+
+ type voiceSpeakingData struct {
+ Speaking bool `json:"speaking"`
+ Delay int `json:"delay"`
+ }
+
+ type voiceSpeakingOp struct {
+ Op int `json:"op"` // Always 5
+ Data voiceSpeakingData `json:"d"`
+ }
+
+ if v.wsConn == nil {
+ return fmt.Errorf("No VoiceConnection websocket.")
+ }
+
+ data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}}
+ v.wsMutex.Lock()
+ err = v.wsConn.WriteJSON(data)
+ v.wsMutex.Unlock()
+ if err != nil {
+ v.speaking = false
+ log.Println("Speaking() write json error:", err)
+ return
+ }
+ v.speaking = b
+
+ return
+}
+
+// ChangeChannel sends Discord a request to change channels within a Guild
+// !!! NOTE !!! This function may be removed in favour of just using ChannelVoiceJoin
+func (v *VoiceConnection) ChangeChannel(channelID string, mute, deaf bool) (err error) {
+
+ v.log(LogInformational, "called")
+
+ data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, &channelID, mute, deaf}}
+ v.wsMutex.Lock()
+ err = v.session.wsConn.WriteJSON(data)
+ v.wsMutex.Unlock()
+ if err != nil {
+ return
+ }
+ v.ChannelID = channelID
+ v.deaf = deaf
+ v.mute = mute
+ v.speaking = false
+
+ return
+}
+
+// Disconnect disconnects from this voice channel and closes the websocket
+// and udp connections to Discord.
+// !!! 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
+ if v.sessionID != "" {
+ data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}}
+ v.wsMutex.Lock()
+ err = v.session.wsConn.WriteJSON(data)
+ v.wsMutex.Unlock()
+ v.sessionID = ""
+ }
+
+ // Close websocket and udp connections
+ v.Close()
+
+ v.log(LogInformational, "Deleting VoiceConnection %s", v.GuildID)
+ delete(v.session.VoiceConnections, v.GuildID)
+
+ return
+}
+
+// Close closes the voice ws and udp connections
+func (v *VoiceConnection) Close() {
+
+ v.log(LogInformational, "called")
+
+ v.Lock()
+ defer v.Unlock()
+
+ v.Ready = false
+ v.speaking = false
+
+ if v.close != nil {
+ v.log(LogInformational, "closing v.close")
+ close(v.close)
+ v.close = nil
+ }
+
+ if v.udpConn != nil {
+ v.log(LogInformational, "closing udp")
+ err := v.udpConn.Close()
+ if err != nil {
+ log.Println("error closing udp connection: ", err)
+ }
+ v.udpConn = nil
+ }
+
+ if v.wsConn != nil {
+ v.log(LogInformational, "sending close frame")
+
+ // To cleanly close a connection, a client should send a close
+ // frame and wait for the server to close the connection.
+ err := v.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
+ if err != nil {
+ v.log(LogError, "error closing websocket, %s", err)
+ }
+
+ // TODO: Wait for Discord to actually close the connection.
+ time.Sleep(1 * time.Second)
+
+ v.log(LogInformational, "closing websocket")
+ err = v.wsConn.Close()
+ if err != nil {
+ v.log(LogError, "error closing websocket, %s", err)
+ }
+
+ v.wsConn = nil
+ }
+}
+
+// AddHandler adds a Handler for VoiceSpeakingUpdate events.
+func (v *VoiceConnection) AddHandler(h VoiceSpeakingUpdateHandler) {
+ v.Lock()
+ defer v.Unlock()
+
+ v.voiceSpeakingUpdateHandlers = append(v.voiceSpeakingUpdateHandlers, h)
+}
+
+// VoiceSpeakingUpdate is a struct for a VoiceSpeakingUpdate event.
+type VoiceSpeakingUpdate struct {
+ UserID string `json:"user_id"`
+ SSRC int `json:"ssrc"`
+ Speaking bool `json:"speaking"`
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unexported Internal Functions Below.
+// ------------------------------------------------------------------------------------------------
+
+// A voiceOP4 stores the data for the voice operation 4 websocket event
+// which provides us with the NaCl SecretBox encryption key
+type voiceOP4 struct {
+ SecretKey [32]byte `json:"secret_key"`
+ Mode string `json:"mode"`
+}
+
+// A voiceOP2 stores the data for the voice operation 2 websocket event
+// which is sort of like the voice READY packet
+type voiceOP2 struct {
+ SSRC uint32 `json:"ssrc"`
+ Port int `json:"port"`
+ Modes []string `json:"modes"`
+ HeartbeatInterval time.Duration `json:"heartbeat_interval"`
+}
+
+// WaitUntilConnected waits for the Voice Connection to
+// become ready, if it does not become ready it retuns an err
+func (v *VoiceConnection) waitUntilConnected() error {
+
+ v.log(LogInformational, "called")
+
+ i := 0
+ for {
+ if v.Ready {
+ return nil
+ }
+
+ if i > 10 {
+ return fmt.Errorf("Timeout waiting for voice.")
+ }
+
+ time.Sleep(1 * time.Second)
+ i++
+ }
+}
+
+// Open opens a voice connection. This should be called
+// after VoiceChannelJoin is used and the data VOICE websocket events
+// are captured.
+func (v *VoiceConnection) open() (err error) {
+
+ v.log(LogInformational, "called")
+
+ v.Lock()
+ defer v.Unlock()
+
+ // Don't open a websocket if one is already open
+ if v.wsConn != nil {
+ v.log(LogWarning, "refusing to overwrite non-nil websocket")
+ return
+ }
+
+ // TODO temp? loop to wait for the SessionID
+ i := 0
+ for {
+ if v.sessionID != "" {
+ break
+ }
+ if i > 20 { // only loop for up to 1 second total
+ return fmt.Errorf("Did not receive voice Session ID in time.")
+ }
+ time.Sleep(50 * time.Millisecond)
+ i++
+ }
+
+ // Connect to VoiceConnection Websocket
+ vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(v.endpoint, ":80"))
+ v.log(LogInformational, "connecting to voice endpoint %s", vg)
+ v.wsConn, _, err = websocket.DefaultDialer.Dial(vg, nil)
+ if err != nil {
+ v.log(LogWarning, "error connecting to voice endpoint %s, %s", vg, err)
+ v.log(LogDebug, "voice struct: %#v\n", v)
+ return
+ }
+
+ type voiceHandshakeData struct {
+ ServerID string `json:"server_id"`
+ UserID string `json:"user_id"`
+ SessionID string `json:"session_id"`
+ Token string `json:"token"`
+ }
+ type voiceHandshakeOp struct {
+ Op int `json:"op"` // Always 0
+ Data voiceHandshakeData `json:"d"`
+ }
+ data := voiceHandshakeOp{0, voiceHandshakeData{v.GuildID, v.UserID, v.sessionID, v.token}}
+
+ err = v.wsConn.WriteJSON(data)
+ if err != nil {
+ v.log(LogWarning, "error sending init packet, %s", err)
+ return
+ }
+
+ v.close = make(chan struct{})
+ go v.wsListen(v.wsConn, v.close)
+
+ // add loop/check for Ready bool here?
+ // then return false if not ready?
+ // but then wsListen will also err.
+
+ return
+}
+
+// wsListen listens on the voice websocket for messages and passes them
+// to the voice event handler. This is automatically called by the Open func
+func (v *VoiceConnection) wsListen(wsConn *websocket.Conn, close <-chan struct{}) {
+
+ v.log(LogInformational, "called")
+
+ for {
+ _, message, err := v.wsConn.ReadMessage()
+ if err != nil {
+ // Detect if we have been closed manually. If a Close() has already
+ // happened, the websocket we are listening on will be different to the
+ // current session.
+ v.RLock()
+ sameConnection := v.wsConn == wsConn
+ v.RUnlock()
+ if sameConnection {
+
+ v.log(LogError, "voice endpoint %s websocket closed unexpectantly, %s", v.endpoint, err)
+
+ // Start reconnect goroutine then exit.
+ go v.reconnect()
+ }
+ return
+ }
+
+ // Pass received message to voice event handler
+ select {
+ case <-close:
+ return
+ default:
+ go v.onEvent(message)
+ }
+ }
+}
+
+// wsEvent handles any voice websocket events. This is only called by the
+// wsListen() function.
+func (v *VoiceConnection) onEvent(message []byte) {
+
+ v.log(LogDebug, "received: %s", string(message))
+
+ var e Event
+ if err := json.Unmarshal(message, &e); err != nil {
+ v.log(LogError, "unmarshall error, %s", err)
+ return
+ }
+
+ switch e.Operation {
+
+ case 2: // READY
+
+ if err := json.Unmarshal(e.RawData, &v.op2); err != nil {
+ v.log(LogError, "OP2 unmarshall error, %s, %s", err, string(e.RawData))
+ return
+ }
+
+ // Start the voice websocket heartbeat to keep the connection alive
+ go v.wsHeartbeat(v.wsConn, v.close, v.op2.HeartbeatInterval)
+ // TODO monitor a chan/bool to verify this was successful
+
+ // Start the UDP connection
+ err := v.udpOpen()
+ if err != nil {
+ v.log(LogError, "error opening udp connection, %s", err)
+ return
+ }
+
+ // Start the opusSender.
+ // TODO: Should we allow 48000/960 values to be user defined?
+ if v.OpusSend == nil {
+ v.OpusSend = make(chan []byte, 2)
+ }
+ go v.opusSender(v.udpConn, v.close, v.OpusSend, 48000, 960)
+
+ // Start the opusReceiver
+ if !v.deaf {
+ if v.OpusRecv == nil {
+ v.OpusRecv = make(chan *Packet, 2)
+ }
+
+ go v.opusReceiver(v.udpConn, v.close, v.OpusRecv)
+ }
+
+ // Send the ready event
+ v.connected <- true
+ return
+
+ case 3: // HEARTBEAT response
+ // add code to use this to track latency?
+ return
+
+ case 4: // udp encryption secret key
+ v.op4 = voiceOP4{}
+ if err := json.Unmarshal(e.RawData, &v.op4); err != nil {
+ v.log(LogError, "OP4 unmarshall error, %s, %s", err, string(e.RawData))
+ return
+ }
+ return
+
+ case 5:
+ if len(v.voiceSpeakingUpdateHandlers) == 0 {
+ return
+ }
+
+ voiceSpeakingUpdate := &VoiceSpeakingUpdate{}
+ if err := json.Unmarshal(e.RawData, voiceSpeakingUpdate); err != nil {
+ v.log(LogError, "OP5 unmarshall error, %s, %s", err, string(e.RawData))
+ return
+ }
+
+ for _, h := range v.voiceSpeakingUpdateHandlers {
+ h(v, voiceSpeakingUpdate)
+ }
+
+ default:
+ v.log(LogError, "unknown voice operation, %d, %s", e.Operation, string(e.RawData))
+ }
+
+ return
+}
+
+type voiceHeartbeatOp struct {
+ Op int `json:"op"` // Always 3
+ Data int `json:"d"`
+}
+
+// NOTE :: When a guild voice server changes how do we shut this down
+// properly, so a new connection can be setup without fuss?
+//
+// wsHeartbeat sends regular heartbeats to voice Discord so it knows the client
+// is still connected. If you do not send these heartbeats Discord will
+// disconnect the websocket connection after a few seconds.
+func (v *VoiceConnection) wsHeartbeat(wsConn *websocket.Conn, close <-chan struct{}, i time.Duration) {
+
+ if close == nil || wsConn == nil {
+ return
+ }
+
+ var err error
+ ticker := time.NewTicker(i * time.Millisecond)
+ for {
+ v.log(LogDebug, "sending heartbeat packet")
+ v.wsMutex.Lock()
+ err = wsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())})
+ v.wsMutex.Unlock()
+ if err != nil {
+ v.log(LogError, "error sending heartbeat to voice endpoint %s, %s", v.endpoint, err)
+ return
+ }
+
+ select {
+ case <-ticker.C:
+ // continue loop and send heartbeat
+ case <-close:
+ return
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Code related to the VoiceConnection UDP connection
+// ------------------------------------------------------------------------------------------------
+
+type voiceUDPData struct {
+ Address string `json:"address"` // Public IP of machine running this code
+ Port uint16 `json:"port"` // UDP Port of machine running this code
+ Mode string `json:"mode"` // always "xsalsa20_poly1305"
+}
+
+type voiceUDPD struct {
+ Protocol string `json:"protocol"` // Always "udp" ?
+ Data voiceUDPData `json:"data"`
+}
+
+type voiceUDPOp struct {
+ Op int `json:"op"` // Always 1
+ Data voiceUDPD `json:"d"`
+}
+
+// udpOpen opens a UDP connection to the voice server and completes the
+// initial required handshake. This connection is left open in the session
+// and can be used to send or receive audio. This should only be called
+// from voice.wsEvent OP2
+func (v *VoiceConnection) udpOpen() (err error) {
+
+ v.Lock()
+ defer v.Unlock()
+
+ if v.wsConn == nil {
+ return fmt.Errorf("nil voice websocket")
+ }
+
+ if v.udpConn != nil {
+ return fmt.Errorf("udp connection already open")
+ }
+
+ if v.close == nil {
+ return fmt.Errorf("nil close channel")
+ }
+
+ if v.endpoint == "" {
+ return fmt.Errorf("empty endpoint")
+ }
+
+ host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.op2.Port)
+ addr, err := net.ResolveUDPAddr("udp", host)
+ if err != nil {
+ v.log(LogWarning, "error resolving udp host %s, %s", host, err)
+ return
+ }
+
+ v.log(LogInformational, "connecting to udp addr %s", addr.String())
+ v.udpConn, err = net.DialUDP("udp", nil, addr)
+ if err != nil {
+ v.log(LogWarning, "error connecting to udp addr %s, %s", addr.String(), err)
+ return
+ }
+
+ // Create a 70 byte array and put the SSRC code from the Op 2 VoiceConnection event
+ // into it. Then send that over the UDP connection to Discord
+ sb := make([]byte, 70)
+ binary.BigEndian.PutUint32(sb, v.op2.SSRC)
+ _, err = v.udpConn.Write(sb)
+ if err != nil {
+ v.log(LogWarning, "udp write error to %s, %s", addr.String(), err)
+ return
+ }
+
+ // Create a 70 byte array and listen for the initial handshake response
+ // from Discord. Once we get it parse the IP and PORT information out
+ // of the response. This should be our public IP and PORT as Discord
+ // saw us.
+ rb := make([]byte, 70)
+ rlen, _, err := v.udpConn.ReadFromUDP(rb)
+ if err != nil {
+ v.log(LogWarning, "udp read error, %s, %s", addr.String(), err)
+ return
+ }
+
+ if rlen < 70 {
+ v.log(LogWarning, "received udp packet too small")
+ return fmt.Errorf("received udp packet too small")
+ }
+
+ // Loop over position 4 though 20 to grab the IP address
+ // Should never be beyond position 20.
+ var ip string
+ for i := 4; i < 20; i++ {
+ if rb[i] == 0 {
+ break
+ }
+ ip += string(rb[i])
+ }
+
+ // Grab port from position 68 and 69
+ port := binary.LittleEndian.Uint16(rb[68:70])
+
+ // Take the data from above and send it back to Discord to finalize
+ // the UDP connection handshake.
+ data := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "xsalsa20_poly1305"}}}
+
+ v.wsMutex.Lock()
+ err = v.wsConn.WriteJSON(data)
+ v.wsMutex.Unlock()
+ if err != nil {
+ v.log(LogWarning, "udp write error, %#v, %s", data, err)
+ return
+ }
+
+ // start udpKeepAlive
+ go v.udpKeepAlive(v.udpConn, v.close, 5*time.Second)
+ // TODO: find a way to check that it fired off okay
+
+ return
+}
+
+// udpKeepAlive sends a udp packet to keep the udp connection open
+// This is still a bit of a "proof of concept"
+func (v *VoiceConnection) udpKeepAlive(udpConn *net.UDPConn, close <-chan struct{}, i time.Duration) {
+
+ if udpConn == nil || close == nil {
+ return
+ }
+
+ var err error
+ var sequence uint64
+
+ packet := make([]byte, 8)
+
+ ticker := time.NewTicker(i)
+ for {
+
+ binary.LittleEndian.PutUint64(packet, sequence)
+ sequence++
+
+ _, err = udpConn.Write(packet)
+ if err != nil {
+ v.log(LogError, "write error, %s", err)
+ return
+ }
+
+ select {
+ case <-ticker.C:
+ // continue loop and send keepalive
+ case <-close:
+ return
+ }
+ }
+}
+
+// opusSender will listen on the given channel and send any
+// pre-encoded opus audio to Discord. Supposedly.
+func (v *VoiceConnection) opusSender(udpConn *net.UDPConn, close <-chan struct{}, opus <-chan []byte, rate, size int) {
+
+ if udpConn == nil || close == nil {
+ return
+ }
+
+ runtime.LockOSThread()
+
+ // VoiceConnection is now ready to receive audio packets
+ // TODO: this needs reviewed as I think there must be a better way.
+ v.Ready = true
+ defer func() { v.Ready = false }()
+
+ var sequence uint16
+ var timestamp uint32
+ var recvbuf []byte
+ var ok bool
+ udpHeader := make([]byte, 12)
+ var nonce [24]byte
+
+ // build the parts that don't change in the udpHeader
+ udpHeader[0] = 0x80
+ udpHeader[1] = 0x78
+ binary.BigEndian.PutUint32(udpHeader[8:], v.op2.SSRC)
+
+ // start a send loop that loops until buf chan is closed
+ ticker := time.NewTicker(time.Millisecond * time.Duration(size/(rate/1000)))
+ for {
+
+ // Get data from chan. If chan is closed, return.
+ select {
+ case <-close:
+ return
+ case recvbuf, ok = <-opus:
+ if !ok {
+ return
+ }
+ // else, continue loop
+ }
+
+ if !v.speaking {
+ err := v.Speaking(true)
+ if err != nil {
+ v.log(LogError, "error sending speaking packet, %s", err)
+ }
+ }
+
+ // Add sequence and timestamp to udpPacket
+ binary.BigEndian.PutUint16(udpHeader[2:], sequence)
+ binary.BigEndian.PutUint32(udpHeader[4:], timestamp)
+
+ // encrypt the opus data
+ copy(nonce[:], udpHeader)
+ sendbuf := secretbox.Seal(udpHeader, recvbuf, &nonce, &v.op4.SecretKey)
+
+ // block here until we're exactly at the right time :)
+ // Then send rtp audio packet to Discord over UDP
+ select {
+ case <-close:
+ return
+ case <-ticker.C:
+ // continue
+ }
+ _, err := udpConn.Write(sendbuf)
+
+ if err != nil {
+ v.log(LogError, "udp write error, %s", err)
+ v.log(LogDebug, "voice struct: %#v\n", v)
+ return
+ }
+
+ if (sequence) == 0xFFFF {
+ sequence = 0
+ } else {
+ sequence++
+ }
+
+ if (timestamp + uint32(size)) >= 0xFFFFFFFF {
+ timestamp = 0
+ } else {
+ timestamp += uint32(size)
+ }
+ }
+}
+
+// A Packet contains the headers and content of a received voice packet.
+type Packet struct {
+ SSRC uint32
+ Sequence uint16
+ Timestamp uint32
+ Type []byte
+ Opus []byte
+ PCM []int16
+}
+
+// opusReceiver listens on the UDP socket for incoming packets
+// and sends them across the given channel
+// NOTE :: This function may change names later.
+func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct{}, c chan *Packet) {
+
+ if udpConn == nil || close == nil {
+ return
+ }
+
+ p := Packet{}
+ recvbuf := make([]byte, 1024)
+ var nonce [24]byte
+
+ for {
+ rlen, err := udpConn.Read(recvbuf)
+ if err != nil {
+ // Detect if we have been closed manually. If a Close() has already
+ // happened, the udp connection we are listening on will be different
+ // to the current session.
+ v.RLock()
+ sameConnection := v.udpConn == udpConn
+ v.RUnlock()
+ if sameConnection {
+
+ v.log(LogError, "udp read error, %s, %s", v.endpoint, err)
+ v.log(LogDebug, "voice struct: %#v\n", v)
+
+ go v.reconnect()
+ }
+ return
+ }
+
+ select {
+ case <-close:
+ return
+ default:
+ // continue loop
+ }
+
+ // For now, skip anything except audio.
+ if rlen < 12 || recvbuf[0] != 0x80 {
+ continue
+ }
+
+ // build a audio packet struct
+ p.Type = recvbuf[0:2]
+ p.Sequence = binary.BigEndian.Uint16(recvbuf[2:4])
+ p.Timestamp = binary.BigEndian.Uint32(recvbuf[4:8])
+ p.SSRC = binary.BigEndian.Uint32(recvbuf[8:12])
+ // decrypt opus data
+ copy(nonce[:], recvbuf[0:12])
+ p.Opus, _ = secretbox.Open(nil, recvbuf[12:rlen], &nonce, &v.op4.SecretKey)
+
+ if c != nil {
+ c <- &p
+ }
+ }
+}
+
+// Reconnect will close down a voice connection then immediately try to
+// reconnect to that session.
+// NOTE : This func is messy and a WIP while I find what works.
+// It will be cleaned up once a proven stable option is flushed out.
+// aka: this is ugly shit code, please don't judge too harshly.
+func (v *VoiceConnection) reconnect() {
+
+ v.log(LogInformational, "called")
+
+ v.Lock()
+ if v.reconnecting {
+ v.log(LogInformational, "already reconnecting to channel %s, exiting", v.ChannelID)
+ v.Unlock()
+ return
+ }
+ v.reconnecting = true
+ v.Unlock()
+
+ defer func() { v.reconnecting = false }()
+
+ // Close any currently open connections
+ v.Close()
+
+ wait := time.Duration(1)
+ for {
+
+ <-time.After(wait * time.Second)
+ wait *= 2
+ if wait > 600 {
+ wait = 600
+ }
+
+ if v.session.DataReady == false || v.session.wsConn == nil {
+ v.log(LogInformational, "cannot reconenct to channel %s with unready session", v.ChannelID)
+ continue
+ }
+
+ v.log(LogInformational, "trying to reconnect to channel %s", v.ChannelID)
+
+ _, err := v.session.ChannelVoiceJoin(v.GuildID, v.ChannelID, v.mute, v.deaf)
+ if err == nil {
+ v.log(LogInformational, "successfully reconnected to channel %s", v.ChannelID)
+ return
+ }
+
+ // if the reconnect above didn't work lets just send a disconnect
+ // packet to reset things.
+ // Send a OP4 with a nil channel to disconnect
+ data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}}
+ v.session.wsMutex.Lock()
+ err = v.session.wsConn.WriteJSON(data)
+ v.session.wsMutex.Unlock()
+ if err != nil {
+ v.log(LogError, "error sending disconnect packet, %s", err)
+ }
+
+ v.log(LogInformational, "error reconnecting to channel %s, %s", v.ChannelID, err)
+ }
+}
diff --git a/vendor/github.com/bwmarrin/discordgo/wsapi.go b/vendor/github.com/bwmarrin/discordgo/wsapi.go
new file mode 100644
index 00000000..a19c3842
--- /dev/null
+++ b/vendor/github.com/bwmarrin/discordgo/wsapi.go
@@ -0,0 +1,679 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains low level functions for interacting with the Discord
+// data websocket interface.
+
+package discordgo
+
+import (
+ "bytes"
+ "compress/zlib"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "reflect"
+ "runtime"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+type resumePacket struct {
+ Op int `json:"op"`
+ Data struct {
+ Token string `json:"token"`
+ SessionID string `json:"session_id"`
+ Sequence int `json:"seq"`
+ } `json:"d"`
+}
+
+// Open opens a websocket connection to Discord.
+func (s *Session) Open() (err error) {
+
+ s.log(LogInformational, "called")
+
+ s.Lock()
+ defer func() {
+ if err != nil {
+ s.Unlock()
+ }
+ }()
+
+ if s.wsConn != nil {
+ err = errors.New("Web socket already opened.")
+ return
+ }
+
+ if s.VoiceConnections == nil {
+ s.log(LogInformational, "creating new VoiceConnections map")
+ s.VoiceConnections = make(map[string]*VoiceConnection)
+ }
+
+ // Get the gateway to use for the Websocket connection
+ if s.gateway == "" {
+ s.gateway, err = s.Gateway()
+ if err != nil {
+ return
+ }
+
+ // Add the version and encoding to the URL
+ s.gateway = fmt.Sprintf("%s?v=4&encoding=json", s.gateway)
+ }
+
+ header := http.Header{}
+ header.Add("accept-encoding", "zlib")
+
+ s.log(LogInformational, "connecting to gateway %s", s.gateway)
+ s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header)
+ if err != nil {
+ s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err)
+ s.gateway = "" // clear cached gateway
+ // TODO: should we add a retry block here?
+ return
+ }
+
+ if s.sessionID != "" && s.sequence > 0 {
+
+ p := resumePacket{}
+ p.Op = 6
+ p.Data.Token = s.Token
+ p.Data.SessionID = s.sessionID
+ p.Data.Sequence = s.sequence
+
+ s.log(LogInformational, "sending resume packet to gateway")
+ err = s.wsConn.WriteJSON(p)
+ if err != nil {
+ s.log(LogWarning, "error sending gateway resume packet, %s, %s", s.gateway, err)
+ return
+ }
+
+ } else {
+
+ err = s.identify()
+ if err != nil {
+ s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err)
+ return
+ }
+ }
+
+ // Create listening outside of listen, as it needs to happen inside the mutex
+ // lock.
+ s.listening = make(chan interface{})
+ go s.listen(s.wsConn, s.listening)
+
+ s.Unlock()
+
+ s.initialize()
+ s.log(LogInformational, "emit connect event")
+ s.handle(&Connect{})
+
+ s.log(LogInformational, "exiting")
+ return
+}
+
+// listen polls the websocket connection for events, it will stop when the
+// listening channel is closed, or an error occurs.
+func (s *Session) listen(wsConn *websocket.Conn, listening <-chan interface{}) {
+
+ s.log(LogInformational, "called")
+
+ for {
+
+ messageType, message, err := wsConn.ReadMessage()
+
+ if err != nil {
+
+ // Detect if we have been closed manually. If a Close() has already
+ // happened, the websocket we are listening on will be different to
+ // the current session.
+ s.RLock()
+ sameConnection := s.wsConn == wsConn
+ s.RUnlock()
+
+ if sameConnection {
+
+ s.log(LogWarning, "error reading from gateway %s websocket, %s", s.gateway, err)
+ // There has been an error reading, close the websocket so that
+ // OnDisconnect event is emitted.
+ err := s.Close()
+ if err != nil {
+ s.log(LogWarning, "error closing session connection, %s", err)
+ }
+
+ s.log(LogInformational, "calling reconnect() now")
+ s.reconnect()
+ }
+
+ return
+ }
+
+ select {
+
+ case <-listening:
+ return
+
+ default:
+ s.onEvent(messageType, message)
+
+ }
+ }
+}
+
+type heartbeatOp struct {
+ Op int `json:"op"`
+ Data int `json:"d"`
+}
+
+// heartbeat sends regular heartbeats to Discord so it knows the client
+// is still connected. If you do not send these heartbeats Discord will
+// disconnect the websocket connection after a few seconds.
+func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}, i time.Duration) {
+
+ s.log(LogInformational, "called")
+
+ if listening == nil || wsConn == nil {
+ return
+ }
+
+ var err error
+ ticker := time.NewTicker(i * time.Millisecond)
+
+ for {
+
+ s.log(LogInformational, "sending gateway websocket heartbeat seq %d", s.sequence)
+ s.wsMutex.Lock()
+ err = wsConn.WriteJSON(heartbeatOp{1, s.sequence})
+ s.wsMutex.Unlock()
+ if err != nil {
+ s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err)
+ s.Lock()
+ s.DataReady = false
+ s.Unlock()
+ return
+ }
+ s.Lock()
+ s.DataReady = true
+ s.Unlock()
+
+ select {
+ case <-ticker.C:
+ // continue loop and send heartbeat
+ case <-listening:
+ return
+ }
+ }
+}
+
+type updateStatusData struct {
+ IdleSince *int `json:"idle_since"`
+ Game *Game `json:"game"`
+}
+
+type updateStatusOp struct {
+ Op int `json:"op"`
+ 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")
+
+ s.RLock()
+ defer s.RUnlock()
+ if s.wsConn == nil {
+ return errors.New("no websocket connection exists")
+ }
+
+ var usd updateStatusData
+ if idle > 0 {
+ usd.IdleSince = &idle
+ }
+
+ if game != "" {
+ gameType := 0
+ if url != "" {
+ gameType = 1
+ }
+ usd.Game = &Game{
+ Name: game,
+ Type: gameType,
+ URL: url,
+ }
+ }
+
+ s.wsMutex.Lock()
+ err = s.wsConn.WriteJSON(updateStatusOp{3, usd})
+ s.wsMutex.Unlock()
+
+ 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, "")
+}
+
+// onEvent is the "event handler" for all messages received on the
+// Discord Gateway API websocket connection.
+//
+// If you use the AddHandler() function to register a handler for a
+// specific event this function will pass the event along to that handler.
+//
+// If you use the AddHandler() function to register a handler for the
+// "OnEvent" event then all events will be passed to that handler.
+//
+// TODO: You may also register a custom event handler entirely using...
+func (s *Session) onEvent(messageType int, message []byte) {
+
+ var err error
+ var reader io.Reader
+ reader = bytes.NewBuffer(message)
+
+ // If this is a compressed message, uncompress it.
+ if messageType == websocket.BinaryMessage {
+
+ z, err2 := zlib.NewReader(reader)
+ if err2 != nil {
+ s.log(LogError, "error uncompressing websocket message, %s", err)
+ return
+ }
+
+ defer func() {
+ err3 := z.Close()
+ if err3 != nil {
+ s.log(LogWarning, "error closing zlib, %s", err)
+ }
+ }()
+
+ reader = z
+ }
+
+ // Decode the event into an Event struct.
+ var e *Event
+ decoder := json.NewDecoder(reader)
+ if err = decoder.Decode(&e); err != nil {
+ s.log(LogError, "error decoding websocket message, %s", err)
+ return
+ }
+
+ s.log(LogDebug, "Op: %d, Seq: %d, Type: %s, Data: %s\n\n", e.Operation, e.Sequence, e.Type, string(e.RawData))
+
+ // Ping request.
+ // Must respond with a heartbeat packet within 5 seconds
+ if e.Operation == 1 {
+ s.log(LogInformational, "sending heartbeat in response to Op1")
+ s.wsMutex.Lock()
+ err = s.wsConn.WriteJSON(heartbeatOp{1, s.sequence})
+ s.wsMutex.Unlock()
+ if err != nil {
+ s.log(LogError, "error sending heartbeat in response to Op1")
+ return
+ }
+
+ return
+ }
+
+ // Reconnect
+ // Must immediately disconnect from gateway and reconnect to new gateway.
+ if e.Operation == 7 {
+ // TODO
+ }
+
+ // Invalid Session
+ // Must respond with a Identify packet.
+ if e.Operation == 9 {
+
+ s.log(LogInformational, "sending identify packet to gateway in response to Op9")
+
+ err = s.identify()
+ if err != nil {
+ s.log(LogWarning, "error sending gateway identify packet, %s, %s", s.gateway, err)
+ return
+ }
+
+ return
+ }
+
+ // Do not try to Dispatch a non-Dispatch Message
+ if e.Operation != 0 {
+ // But we probably should be doing something with them.
+ // TEMP
+ s.log(LogWarning, "unknown Op: %d, Seq: %d, Type: %s, Data: %s, message: %s", e.Operation, e.Sequence, e.Type, string(e.RawData), string(message))
+ return
+ }
+
+ // Store the message sequence
+ s.sequence = e.Sequence
+
+ // Map event to registered event handlers and pass it along
+ // to any registered functions
+ i := eventToInterface[e.Type]
+ if i != nil {
+
+ // Create a new instance of the event type.
+ i = reflect.New(reflect.TypeOf(i)).Interface()
+
+ // Attempt to unmarshal our event.
+ if err = json.Unmarshal(e.RawData, i); err != nil {
+ s.log(LogError, "error unmarshalling %s event, %s", e.Type, err)
+ }
+
+ // Send event to any registered event handlers for it's type.
+ // Because the above doesn't cancel this, in case of an error
+ // the struct could be partially populated or at default values.
+ // However, most errors are due to a single field and I feel
+ // it's better to pass along what we received than nothing at all.
+ // TODO: Think about that decision :)
+ // Either way, READY events must fire, even with errors.
+ go s.handle(i)
+
+ } else {
+ s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData))
+ }
+
+ // Emit event to the OnEvent handler
+ e.Struct = i
+ go s.handle(e)
+}
+
+// ------------------------------------------------------------------------------------------------
+// Code related to voice connections that initiate over the data websocket
+// ------------------------------------------------------------------------------------------------
+
+// A VoiceServerUpdate stores the data received during the Voice Server Update
+// data websocket event. This data is used during the initial Voice Channel
+// join handshaking.
+type VoiceServerUpdate struct {
+ Token string `json:"token"`
+ GuildID string `json:"guild_id"`
+ Endpoint string `json:"endpoint"`
+}
+
+type voiceChannelJoinData struct {
+ GuildID *string `json:"guild_id"`
+ ChannelID *string `json:"channel_id"`
+ SelfMute bool `json:"self_mute"`
+ SelfDeaf bool `json:"self_deaf"`
+}
+
+type voiceChannelJoinOp struct {
+ Op int `json:"op"`
+ Data voiceChannelJoinData `json:"d"`
+}
+
+// ChannelVoiceJoin joins the session user to a voice channel.
+//
+// gID : Guild ID of the channel to join.
+// cID : Channel ID of the channel to join.
+// mute : If true, you will be set to muted upon joining.
+// deaf : If true, you will be set to deafened upon joining.
+func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *VoiceConnection, err error) {
+
+ s.log(LogInformational, "called")
+
+ voice, _ = s.VoiceConnections[gID]
+
+ if voice == nil {
+ voice = &VoiceConnection{}
+ s.VoiceConnections[gID] = voice
+ }
+
+ voice.GuildID = gID
+ voice.ChannelID = cID
+ voice.deaf = deaf
+ voice.mute = mute
+ voice.session = s
+
+ // 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
+ }
+
+ // doesn't exactly work perfect yet.. TODO
+ err = voice.waitUntilConnected()
+ if err != nil {
+ s.log(LogWarning, "error waiting for voice to connect, %s", err)
+ voice.Close()
+ return
+ }
+
+ return
+}
+
+// onVoiceStateUpdate handles Voice State Update events on the data websocket.
+func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
+
+ // If we don't have a connection for the channel, don't bother
+ if st.ChannelID == "" {
+ return
+ }
+
+ // Check if we have a voice connection to update
+ voice, exists := s.VoiceConnections[st.GuildID]
+ if !exists {
+ return
+ }
+
+ // Need to have this happen at login and store it in the Session
+ // TODO : This should be done upon connecting to Discord, or
+ // be moved to a small helper function
+ self, err := s.User("@me") // TODO: move to Login/New
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ // We only care about events that are about us
+ if st.UserID != self.ID {
+ return
+ }
+
+ // Store the SessionID for later use.
+ voice.UserID = self.ID // TODO: Review
+ voice.sessionID = st.SessionID
+}
+
+// onVoiceServerUpdate handles the Voice Server Update data websocket event.
+//
+// This is also fired if the Guild's voice region changes while connected
+// to a voice channel. In that case, need to re-establish connection to
+// the new region endpoint.
+func (s *Session) onVoiceServerUpdate(se *Session, st *VoiceServerUpdate) {
+
+ s.log(LogInformational, "called")
+
+ voice, exists := s.VoiceConnections[st.GuildID]
+
+ // If no VoiceConnection exists, just skip this
+ if !exists {
+ return
+ }
+
+ // If currently connected to voice ws/udp, then disconnect.
+ // Has no effect if not connected.
+ voice.Close()
+
+ // Store values for later use
+ voice.token = st.Token
+ voice.endpoint = st.Endpoint
+ voice.GuildID = st.GuildID
+
+ // Open a conenction to the voice server
+ err := voice.open()
+ if err != nil {
+ s.log(LogError, "onVoiceServerUpdate voice.open, %s", err)
+ }
+}
+
+type identifyProperties struct {
+ OS string `json:"$os"`
+ Browser string `json:"$browser"`
+ Device string `json:"$device"`
+ Referer string `json:"$referer"`
+ ReferringDomain string `json:"$referring_domain"`
+}
+
+type identifyData struct {
+ Token string `json:"token"`
+ Properties identifyProperties `json:"properties"`
+ LargeThreshold int `json:"large_threshold"`
+ Compress bool `json:"compress"`
+ Shard *[2]int `json:"shard,omitempty"`
+}
+
+type identifyOp struct {
+ Op int `json:"op"`
+ Data identifyData `json:"d"`
+}
+
+// identify sends the identify packet to the gateway
+func (s *Session) identify() error {
+
+ properties := identifyProperties{runtime.GOOS,
+ "Discordgo v" + VERSION,
+ "",
+ "",
+ "",
+ }
+
+ data := identifyData{s.Token,
+ properties,
+ 250,
+ s.Compress,
+ nil,
+ }
+
+ if s.ShardCount > 1 {
+
+ if s.ShardID >= s.ShardCount {
+ return errors.New("ShardID must be less than ShardCount")
+ }
+
+ data.Shard = &[2]int{s.ShardID, s.ShardCount}
+ }
+
+ op := identifyOp{2, data}
+
+ s.wsMutex.Lock()
+ err := s.wsConn.WriteJSON(op)
+ s.wsMutex.Unlock()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *Session) reconnect() {
+
+ s.log(LogInformational, "called")
+
+ var err error
+
+ if s.ShouldReconnectOnError {
+
+ wait := time.Duration(1)
+
+ for {
+ s.log(LogInformational, "trying to reconnect to gateway")
+
+ err = s.Open()
+ if err == nil {
+ s.log(LogInformational, "successfully reconnected to gateway")
+
+ // I'm not sure if this is actually needed.
+ // if the gw reconnect works properly, voice should stay alive
+ // However, there seems to be cases where something "weird"
+ // happens. So we're doing this for now just to improve
+ // stability in those edge cases.
+ for _, v := range s.VoiceConnections {
+
+ s.log(LogInformational, "reconnecting voice connection to guild %s", v.GuildID)
+ go v.reconnect()
+
+ // This is here just to prevent violently spamming the
+ // voice reconnects
+ time.Sleep(1 * time.Second)
+
+ }
+ return
+ }
+
+ s.log(LogError, "error reconnecting to gateway, %s", err)
+
+ <-time.After(wait * time.Second)
+ wait *= 2
+ if wait > 600 {
+ wait = 600
+ }
+ }
+ }
+}
+
+// Close closes a websocket and stops all listening/heartbeat goroutines.
+// TODO: Add support for Voice WS/UDP connections
+func (s *Session) Close() (err error) {
+
+ s.log(LogInformational, "called")
+ s.Lock()
+
+ s.DataReady = false
+
+ if s.listening != nil {
+ s.log(LogInformational, "closing listening channel")
+ close(s.listening)
+ s.listening = nil
+ }
+
+ // TODO: Close all active Voice Connections too
+ // this should force stop any reconnecting voice channels too
+
+ if s.wsConn != nil {
+
+ s.log(LogInformational, "sending close frame")
+ // To cleanly close a connection, a client should send a close
+ // frame and wait for the server to close the connection.
+ err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
+ if err != nil {
+ s.log(LogError, "error closing websocket, %s", err)
+ }
+
+ // TODO: Wait for Discord to actually close the connection.
+ time.Sleep(1 * time.Second)
+
+ s.log(LogInformational, "closing gateway websocket")
+ err = s.wsConn.Close()
+ if err != nil {
+ s.log(LogError, "error closing websocket, %s", err)
+ }
+
+ s.wsConn = nil
+ }
+
+ s.Unlock()
+
+ s.log(LogInformational, "emit disconnect event")
+ s.handle(&Disconnect{})
+
+ return
+}
diff --git a/vendor/golang.org/x/crypto/poly1305/LICENSE b/vendor/golang.org/x/crypto/poly1305/LICENSE
new file mode 100644
index 00000000..6a66aea5
--- /dev/null
+++ b/vendor/golang.org/x/crypto/poly1305/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/crypto/poly1305/const_amd64.s b/vendor/golang.org/x/crypto/poly1305/const_amd64.s
new file mode 100644
index 00000000..8e861f33
--- /dev/null
+++ b/vendor/golang.org/x/crypto/poly1305/const_amd64.s
@@ -0,0 +1,45 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+DATA ·SCALE(SB)/8, $0x37F4000000000000
+GLOBL ·SCALE(SB), 8, $8
+DATA ·TWO32(SB)/8, $0x41F0000000000000
+GLOBL ·TWO32(SB), 8, $8
+DATA ·TWO64(SB)/8, $0x43F0000000000000
+GLOBL ·TWO64(SB), 8, $8
+DATA ·TWO96(SB)/8, $0x45F0000000000000
+GLOBL ·TWO96(SB), 8, $8
+DATA ·ALPHA32(SB)/8, $0x45E8000000000000
+GLOBL ·ALPHA32(SB), 8, $8
+DATA ·ALPHA64(SB)/8, $0x47E8000000000000
+GLOBL ·ALPHA64(SB), 8, $8
+DATA ·ALPHA96(SB)/8, $0x49E8000000000000
+GLOBL ·ALPHA96(SB), 8, $8
+DATA ·ALPHA130(SB)/8, $0x4C08000000000000
+GLOBL ·ALPHA130(SB), 8, $8
+DATA ·DOFFSET0(SB)/8, $0x4330000000000000
+GLOBL ·DOFFSET0(SB), 8, $8
+DATA ·DOFFSET1(SB)/8, $0x4530000000000000
+GLOBL ·DOFFSET1(SB), 8, $8
+DATA ·DOFFSET2(SB)/8, $0x4730000000000000
+GLOBL ·DOFFSET2(SB), 8, $8
+DATA ·DOFFSET3(SB)/8, $0x4930000000000000
+GLOBL ·DOFFSET3(SB), 8, $8
+DATA ·DOFFSET3MINUSTWO128(SB)/8, $0x492FFFFE00000000
+GLOBL ·DOFFSET3MINUSTWO128(SB), 8, $8
+DATA ·HOFFSET0(SB)/8, $0x43300001FFFFFFFB
+GLOBL ·HOFFSET0(SB), 8, $8
+DATA ·HOFFSET1(SB)/8, $0x45300001FFFFFFFE
+GLOBL ·HOFFSET1(SB), 8, $8
+DATA ·HOFFSET2(SB)/8, $0x47300001FFFFFFFE
+GLOBL ·HOFFSET2(SB), 8, $8
+DATA ·HOFFSET3(SB)/8, $0x49300003FFFFFFFE
+GLOBL ·HOFFSET3(SB), 8, $8
+DATA ·ROUNDING(SB)/2, $0x137f
+GLOBL ·ROUNDING(SB), 8, $2
diff --git a/vendor/golang.org/x/crypto/poly1305/poly1305.go b/vendor/golang.org/x/crypto/poly1305/poly1305.go
new file mode 100644
index 00000000..4a5f826f
--- /dev/null
+++ b/vendor/golang.org/x/crypto/poly1305/poly1305.go
@@ -0,0 +1,32 @@
+// Copyright 2012 The Go 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 poly1305 implements Poly1305 one-time message authentication code as specified in http://cr.yp.to/mac/poly1305-20050329.pdf.
+
+Poly1305 is a fast, one-time authentication function. It is infeasible for an
+attacker to generate an authenticator for a message without the key. However, a
+key must only be used for a single message. Authenticating two different
+messages with the same key allows an attacker to forge authenticators for other
+messages with the same key.
+
+Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
+used with a fixed key in order to generate one-time keys from an nonce.
+However, in this package AES isn't used and the one-time key is specified
+directly.
+*/
+package poly1305 // import "golang.org/x/crypto/poly1305"
+
+import "crypto/subtle"
+
+// TagSize is the size, in bytes, of a poly1305 authenticator.
+const TagSize = 16
+
+// Verify returns true if mac is a valid authenticator for m with the given
+// key.
+func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
+ var tmp [16]byte
+ Sum(&tmp, m, key)
+ return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
+}
diff --git a/vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s b/vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s
new file mode 100644
index 00000000..f8d4ee92
--- /dev/null
+++ b/vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s
@@ -0,0 +1,497 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
+TEXT ·poly1305(SB),0,$224-32
+ MOVQ out+0(FP),DI
+ MOVQ m+8(FP),SI
+ MOVQ mlen+16(FP),DX
+ MOVQ key+24(FP),CX
+
+ MOVQ SP,R11
+ MOVQ $31,R9
+ NOTQ R9
+ ANDQ R9,SP
+ ADDQ $32,SP
+
+ MOVQ R11,32(SP)
+ MOVQ R12,40(SP)
+ MOVQ R13,48(SP)
+ MOVQ R14,56(SP)
+ MOVQ R15,64(SP)
+ MOVQ BX,72(SP)
+ MOVQ BP,80(SP)
+ FLDCW ·ROUNDING(SB)
+ MOVL 0(CX),R8
+ MOVL 4(CX),R9
+ MOVL 8(CX),AX
+ MOVL 12(CX),R10
+ MOVQ DI,88(SP)
+ MOVQ CX,96(SP)
+ MOVL $0X43300000,108(SP)
+ MOVL $0X45300000,116(SP)
+ MOVL $0X47300000,124(SP)
+ MOVL $0X49300000,132(SP)
+ ANDL $0X0FFFFFFF,R8
+ ANDL $0X0FFFFFFC,R9
+ ANDL $0X0FFFFFFC,AX
+ ANDL $0X0FFFFFFC,R10
+ MOVL R8,104(SP)
+ MOVL R9,112(SP)
+ MOVL AX,120(SP)
+ MOVL R10,128(SP)
+ FMOVD 104(SP), F0
+ FSUBD ·DOFFSET0(SB), F0
+ FMOVD 112(SP), F0
+ FSUBD ·DOFFSET1(SB), F0
+ FMOVD 120(SP), F0
+ FSUBD ·DOFFSET2(SB), F0
+ FMOVD 128(SP), F0
+ FSUBD ·DOFFSET3(SB), F0
+ FXCHD F0, F3
+ FMOVDP F0, 136(SP)
+ FXCHD F0, F1
+ FMOVD F0, 144(SP)
+ FMULD ·SCALE(SB), F0
+ FMOVDP F0, 152(SP)
+ FMOVD F0, 160(SP)
+ FMULD ·SCALE(SB), F0
+ FMOVDP F0, 168(SP)
+ FMOVD F0, 176(SP)
+ FMULD ·SCALE(SB), F0
+ FMOVDP F0, 184(SP)
+ FLDZ
+ FLDZ
+ FLDZ
+ FLDZ
+ CMPQ DX,$16
+ JB ADDATMOST15BYTES
+ INITIALATLEAST16BYTES:
+ MOVL 12(SI),DI
+ MOVL 8(SI),CX
+ MOVL 4(SI),R8
+ MOVL 0(SI),R9
+ MOVL DI,128(SP)
+ MOVL CX,120(SP)
+ MOVL R8,112(SP)
+ MOVL R9,104(SP)
+ ADDQ $16,SI
+ SUBQ $16,DX
+ FXCHD F0, F3
+ FADDD 128(SP), F0
+ FSUBD ·DOFFSET3MINUSTWO128(SB), F0
+ FXCHD F0, F1
+ FADDD 112(SP), F0
+ FSUBD ·DOFFSET1(SB), F0
+ FXCHD F0, F2
+ FADDD 120(SP), F0
+ FSUBD ·DOFFSET2(SB), F0
+ FXCHD F0, F3
+ FADDD 104(SP), F0
+ FSUBD ·DOFFSET0(SB), F0
+ CMPQ DX,$16
+ JB MULTIPLYADDATMOST15BYTES
+ MULTIPLYADDATLEAST16BYTES:
+ MOVL 12(SI),DI
+ MOVL 8(SI),CX
+ MOVL 4(SI),R8
+ MOVL 0(SI),R9
+ MOVL DI,128(SP)
+ MOVL CX,120(SP)
+ MOVL R8,112(SP)
+ MOVL R9,104(SP)
+ ADDQ $16,SI
+ SUBQ $16,DX
+ FMOVD ·ALPHA130(SB), F0
+ FADDD F2,F0
+ FSUBD ·ALPHA130(SB), F0
+ FSUBD F0,F2
+ FMULD ·SCALE(SB), F0
+ FMOVD ·ALPHA32(SB), F0
+ FADDD F2,F0
+ FSUBD ·ALPHA32(SB), F0
+ FSUBD F0,F2
+ FXCHD F0, F2
+ FADDDP F0,F1
+ FMOVD ·ALPHA64(SB), F0
+ FADDD F4,F0
+ FSUBD ·ALPHA64(SB), F0
+ FSUBD F0,F4
+ FMOVD ·ALPHA96(SB), F0
+ FADDD F6,F0
+ FSUBD ·ALPHA96(SB), F0
+ FSUBD F0,F6
+ FXCHD F0, F6
+ FADDDP F0,F1
+ FXCHD F0, F3
+ FADDDP F0,F5
+ FXCHD F0, F3
+ FADDDP F0,F1
+ FMOVD 176(SP), F0
+ FMULD F3,F0
+ FMOVD 160(SP), F0
+ FMULD F4,F0
+ FMOVD 144(SP), F0
+ FMULD F5,F0
+ FMOVD 136(SP), F0
+ FMULDP F0,F6
+ FMOVD 160(SP), F0
+ FMULD F4,F0
+ FADDDP F0,F3
+ FMOVD 144(SP), F0
+ FMULD F4,F0
+ FADDDP F0,F2
+ FMOVD 136(SP), F0
+ FMULD F4,F0
+ FADDDP F0,F1
+ FMOVD 184(SP), F0
+ FMULDP F0,F4
+ FXCHD F0, F3
+ FADDDP F0,F5
+ FMOVD 144(SP), F0
+ FMULD F4,F0
+ FADDDP F0,F2
+ FMOVD 136(SP), F0
+ FMULD F4,F0
+ FADDDP F0,F1
+ FMOVD 184(SP), F0
+ FMULD F4,F0
+ FADDDP F0,F3
+ FMOVD 168(SP), F0
+ FMULDP F0,F4
+ FXCHD F0, F3
+ FADDDP F0,F4
+ FMOVD 136(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F1
+ FXCHD F0, F3
+ FMOVD 184(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F3
+ FXCHD F0, F1
+ FMOVD 168(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F1
+ FMOVD 152(SP), F0
+ FMULDP F0,F5
+ FXCHD F0, F4
+ FADDDP F0,F1
+ CMPQ DX,$16
+ FXCHD F0, F2
+ FMOVD 128(SP), F0
+ FSUBD ·DOFFSET3MINUSTWO128(SB), F0
+ FADDDP F0,F1
+ FXCHD F0, F1
+ FMOVD 120(SP), F0
+ FSUBD ·DOFFSET2(SB), F0
+ FADDDP F0,F1
+ FXCHD F0, F3
+ FMOVD 112(SP), F0
+ FSUBD ·DOFFSET1(SB), F0
+ FADDDP F0,F1
+ FXCHD F0, F2
+ FMOVD 104(SP), F0
+ FSUBD ·DOFFSET0(SB), F0
+ FADDDP F0,F1
+ JAE MULTIPLYADDATLEAST16BYTES
+ MULTIPLYADDATMOST15BYTES:
+ FMOVD ·ALPHA130(SB), F0
+ FADDD F2,F0
+ FSUBD ·ALPHA130(SB), F0
+ FSUBD F0,F2
+ FMULD ·SCALE(SB), F0
+ FMOVD ·ALPHA32(SB), F0
+ FADDD F2,F0
+ FSUBD ·ALPHA32(SB), F0
+ FSUBD F0,F2
+ FMOVD ·ALPHA64(SB), F0
+ FADDD F5,F0
+ FSUBD ·ALPHA64(SB), F0
+ FSUBD F0,F5
+ FMOVD ·ALPHA96(SB), F0
+ FADDD F7,F0
+ FSUBD ·ALPHA96(SB), F0
+ FSUBD F0,F7
+ FXCHD F0, F7
+ FADDDP F0,F1
+ FXCHD F0, F5
+ FADDDP F0,F1
+ FXCHD F0, F3
+ FADDDP F0,F5
+ FADDDP F0,F1
+ FMOVD 176(SP), F0
+ FMULD F1,F0
+ FMOVD 160(SP), F0
+ FMULD F2,F0
+ FMOVD 144(SP), F0
+ FMULD F3,F0
+ FMOVD 136(SP), F0
+ FMULDP F0,F4
+ FMOVD 160(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F3
+ FMOVD 144(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F2
+ FMOVD 136(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F1
+ FMOVD 184(SP), F0
+ FMULDP F0,F5
+ FXCHD F0, F4
+ FADDDP F0,F3
+ FMOVD 144(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F2
+ FMOVD 136(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F1
+ FMOVD 184(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F4
+ FMOVD 168(SP), F0
+ FMULDP F0,F5
+ FXCHD F0, F4
+ FADDDP F0,F2
+ FMOVD 136(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F1
+ FMOVD 184(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F4
+ FMOVD 168(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F3
+ FMOVD 152(SP), F0
+ FMULDP F0,F5
+ FXCHD F0, F4
+ FADDDP F0,F1
+ ADDATMOST15BYTES:
+ CMPQ DX,$0
+ JE NOMOREBYTES
+ MOVL $0,0(SP)
+ MOVL $0, 4 (SP)
+ MOVL $0, 8 (SP)
+ MOVL $0, 12 (SP)
+ LEAQ 0(SP),DI
+ MOVQ DX,CX
+ REP; MOVSB
+ MOVB $1,0(DI)
+ MOVL 12 (SP),DI
+ MOVL 8 (SP),SI
+ MOVL 4 (SP),DX
+ MOVL 0(SP),CX
+ MOVL DI,128(SP)
+ MOVL SI,120(SP)
+ MOVL DX,112(SP)
+ MOVL CX,104(SP)
+ FXCHD F0, F3
+ FADDD 128(SP), F0
+ FSUBD ·DOFFSET3(SB), F0
+ FXCHD F0, F2
+ FADDD 120(SP), F0
+ FSUBD ·DOFFSET2(SB), F0
+ FXCHD F0, F1
+ FADDD 112(SP), F0
+ FSUBD ·DOFFSET1(SB), F0
+ FXCHD F0, F3
+ FADDD 104(SP), F0
+ FSUBD ·DOFFSET0(SB), F0
+ FMOVD ·ALPHA130(SB), F0
+ FADDD F3,F0
+ FSUBD ·ALPHA130(SB), F0
+ FSUBD F0,F3
+ FMULD ·SCALE(SB), F0
+ FMOVD ·ALPHA32(SB), F0
+ FADDD F2,F0
+ FSUBD ·ALPHA32(SB), F0
+ FSUBD F0,F2
+ FMOVD ·ALPHA64(SB), F0
+ FADDD F6,F0
+ FSUBD ·ALPHA64(SB), F0
+ FSUBD F0,F6
+ FMOVD ·ALPHA96(SB), F0
+ FADDD F5,F0
+ FSUBD ·ALPHA96(SB), F0
+ FSUBD F0,F5
+ FXCHD F0, F4
+ FADDDP F0,F3
+ FXCHD F0, F6
+ FADDDP F0,F1
+ FXCHD F0, F3
+ FADDDP F0,F5
+ FXCHD F0, F3
+ FADDDP F0,F1
+ FMOVD 176(SP), F0
+ FMULD F3,F0
+ FMOVD 160(SP), F0
+ FMULD F4,F0
+ FMOVD 144(SP), F0
+ FMULD F5,F0
+ FMOVD 136(SP), F0
+ FMULDP F0,F6
+ FMOVD 160(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F3
+ FMOVD 144(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F2
+ FMOVD 136(SP), F0
+ FMULD F5,F0
+ FADDDP F0,F1
+ FMOVD 184(SP), F0
+ FMULDP F0,F5
+ FXCHD F0, F4
+ FADDDP F0,F5
+ FMOVD 144(SP), F0
+ FMULD F6,F0
+ FADDDP F0,F2
+ FMOVD 136(SP), F0
+ FMULD F6,F0
+ FADDDP F0,F1
+ FMOVD 184(SP), F0
+ FMULD F6,F0
+ FADDDP F0,F4
+ FMOVD 168(SP), F0
+ FMULDP F0,F6
+ FXCHD F0, F5
+ FADDDP F0,F4
+ FMOVD 136(SP), F0
+ FMULD F2,F0
+ FADDDP F0,F1
+ FMOVD 184(SP), F0
+ FMULD F2,F0
+ FADDDP F0,F5
+ FMOVD 168(SP), F0
+ FMULD F2,F0
+ FADDDP F0,F3
+ FMOVD 152(SP), F0
+ FMULDP F0,F2
+ FXCHD F0, F1
+ FADDDP F0,F3
+ FXCHD F0, F3
+ FXCHD F0, F2
+ NOMOREBYTES:
+ MOVL $0,R10
+ FMOVD ·ALPHA130(SB), F0
+ FADDD F4,F0
+ FSUBD ·ALPHA130(SB), F0
+ FSUBD F0,F4
+ FMULD ·SCALE(SB), F0
+ FMOVD ·ALPHA32(SB), F0
+ FADDD F2,F0
+ FSUBD ·ALPHA32(SB), F0
+ FSUBD F0,F2
+ FMOVD ·ALPHA64(SB), F0
+ FADDD F4,F0
+ FSUBD ·ALPHA64(SB), F0
+ FSUBD F0,F4
+ FMOVD ·ALPHA96(SB), F0
+ FADDD F6,F0
+ FSUBD ·ALPHA96(SB), F0
+ FXCHD F0, F6
+ FSUBD F6,F0
+ FXCHD F0, F4
+ FADDDP F0,F3
+ FXCHD F0, F4
+ FADDDP F0,F1
+ FXCHD F0, F2
+ FADDDP F0,F3
+ FXCHD F0, F4
+ FADDDP F0,F3
+ FXCHD F0, F3
+ FADDD ·HOFFSET0(SB), F0
+ FXCHD F0, F3
+ FADDD ·HOFFSET1(SB), F0
+ FXCHD F0, F1
+ FADDD ·HOFFSET2(SB), F0
+ FXCHD F0, F2
+ FADDD ·HOFFSET3(SB), F0
+ FXCHD F0, F3
+ FMOVDP F0, 104(SP)
+ FMOVDP F0, 112(SP)
+ FMOVDP F0, 120(SP)
+ FMOVDP F0, 128(SP)
+ MOVL 108(SP),DI
+ ANDL $63,DI
+ MOVL 116(SP),SI
+ ANDL $63,SI
+ MOVL 124(SP),DX
+ ANDL $63,DX
+ MOVL 132(SP),CX
+ ANDL $63,CX
+ MOVL 112(SP),R8
+ ADDL DI,R8
+ MOVQ R8,112(SP)
+ MOVL 120(SP),DI
+ ADCL SI,DI
+ MOVQ DI,120(SP)
+ MOVL 128(SP),DI
+ ADCL DX,DI
+ MOVQ DI,128(SP)
+ MOVL R10,DI
+ ADCL CX,DI
+ MOVQ DI,136(SP)
+ MOVQ $5,DI
+ MOVL 104(SP),SI
+ ADDL SI,DI
+ MOVQ DI,104(SP)
+ MOVL R10,DI
+ MOVQ 112(SP),DX
+ ADCL DX,DI
+ MOVQ DI,112(SP)
+ MOVL R10,DI
+ MOVQ 120(SP),CX
+ ADCL CX,DI
+ MOVQ DI,120(SP)
+ MOVL R10,DI
+ MOVQ 128(SP),R8
+ ADCL R8,DI
+ MOVQ DI,128(SP)
+ MOVQ $0XFFFFFFFC,DI
+ MOVQ 136(SP),R9
+ ADCL R9,DI
+ SARL $16,DI
+ MOVQ DI,R9
+ XORL $0XFFFFFFFF,R9
+ ANDQ DI,SI
+ MOVQ 104(SP),AX
+ ANDQ R9,AX
+ ORQ AX,SI
+ ANDQ DI,DX
+ MOVQ 112(SP),AX
+ ANDQ R9,AX
+ ORQ AX,DX
+ ANDQ DI,CX
+ MOVQ 120(SP),AX
+ ANDQ R9,AX
+ ORQ AX,CX
+ ANDQ DI,R8
+ MOVQ 128(SP),DI
+ ANDQ R9,DI
+ ORQ DI,R8
+ MOVQ 88(SP),DI
+ MOVQ 96(SP),R9
+ ADDL 16(R9),SI
+ ADCL 20(R9),DX
+ ADCL 24(R9),CX
+ ADCL 28(R9),R8
+ MOVL SI,0(DI)
+ MOVL DX,4(DI)
+ MOVL CX,8(DI)
+ MOVL R8,12(DI)
+ MOVQ 32(SP),R11
+ MOVQ 40(SP),R12
+ MOVQ 48(SP),R13
+ MOVQ 56(SP),R14
+ MOVQ 64(SP),R15
+ MOVQ 72(SP),BX
+ MOVQ 80(SP),BP
+ MOVQ R11,SP
+ RET
diff --git a/vendor/golang.org/x/crypto/poly1305/poly1305_arm.s b/vendor/golang.org/x/crypto/poly1305/poly1305_arm.s
new file mode 100644
index 00000000..c1538674
--- /dev/null
+++ b/vendor/golang.org/x/crypto/poly1305/poly1305_arm.s
@@ -0,0 +1,379 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 5a from the public
+// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
+
+// +build arm,!gccgo,!appengine
+
+DATA poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
+DATA poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
+DATA poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
+DATA poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
+DATA poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
+GLOBL poly1305_init_constants_armv6<>(SB), 8, $20
+
+// Warning: the linker may use R11 to synthesize certain instructions. Please
+// take care and verify that no synthetic instructions use it.
+
+TEXT poly1305_init_ext_armv6<>(SB),4,$-4
+ MOVM.DB.W [R4-R11], (R13)
+ MOVM.IA.W (R1), [R2-R5]
+ MOVW $poly1305_init_constants_armv6<>(SB), R7
+ MOVW R2, R8
+ MOVW R2>>26, R9
+ MOVW R3>>20, g
+ MOVW R4>>14, R11
+ MOVW R5>>8, R12
+ ORR R3<<6, R9, R9
+ ORR R4<<12, g, g
+ ORR R5<<18, R11, R11
+ MOVM.IA (R7), [R2-R6]
+ AND R8, R2, R2
+ AND R9, R3, R3
+ AND g, R4, R4
+ AND R11, R5, R5
+ AND R12, R6, R6
+ MOVM.IA.W [R2-R6], (R0)
+ EOR R2, R2, R2
+ EOR R3, R3, R3
+ EOR R4, R4, R4
+ EOR R5, R5, R5
+ EOR R6, R6, R6
+ MOVM.IA.W [R2-R6], (R0)
+ MOVM.IA.W (R1), [R2-R5]
+ MOVM.IA [R2-R6], (R0)
+ MOVM.IA.W (R13), [R4-R11]
+ RET
+
+#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \
+ MOVBU (offset+0)(Rsrc), Rtmp; \
+ MOVBU Rtmp, (offset+0)(Rdst); \
+ MOVBU (offset+1)(Rsrc), Rtmp; \
+ MOVBU Rtmp, (offset+1)(Rdst); \
+ MOVBU (offset+2)(Rsrc), Rtmp; \
+ MOVBU Rtmp, (offset+2)(Rdst); \
+ MOVBU (offset+3)(Rsrc), Rtmp; \
+ MOVBU Rtmp, (offset+3)(Rdst)
+
+TEXT poly1305_blocks_armv6<>(SB),4,$-4
+ MOVM.DB.W [R4, R5, R6, R7, R8, R9, g, R11, R14], (R13)
+ SUB $128, R13
+ MOVW R0, 36(R13)
+ MOVW R1, 40(R13)
+ MOVW R2, 44(R13)
+ MOVW R1, R14
+ MOVW R2, R12
+ MOVW 56(R0), R8
+ WORD $0xe1180008 // TST R8, R8 not working see issue 5921
+ EOR R6, R6, R6
+ MOVW.EQ $(1<<24), R6
+ MOVW R6, 32(R13)
+ ADD $64, R13, g
+ MOVM.IA (R0), [R0-R9]
+ MOVM.IA [R0-R4], (g)
+ CMP $16, R12
+ BLO poly1305_blocks_armv6_done
+poly1305_blocks_armv6_mainloop:
+ WORD $0xe31e0003 // TST R14, #3 not working see issue 5921
+ BEQ poly1305_blocks_armv6_mainloop_aligned
+ ADD $48, R13, g
+ MOVW_UNALIGNED(R14, g, R0, 0)
+ MOVW_UNALIGNED(R14, g, R0, 4)
+ MOVW_UNALIGNED(R14, g, R0, 8)
+ MOVW_UNALIGNED(R14, g, R0, 12)
+ MOVM.IA (g), [R0-R3]
+ ADD $16, R14
+ B poly1305_blocks_armv6_mainloop_loaded
+poly1305_blocks_armv6_mainloop_aligned:
+ MOVM.IA.W (R14), [R0-R3]
+poly1305_blocks_armv6_mainloop_loaded:
+ MOVW R0>>26, g
+ MOVW R1>>20, R11
+ MOVW R2>>14, R12
+ MOVW R14, 40(R13)
+ MOVW R3>>8, R4
+ ORR R1<<6, g, g
+ ORR R2<<12, R11, R11
+ ORR R3<<18, R12, R12
+ BIC $0xfc000000, R0, R0
+ BIC $0xfc000000, g, g
+ MOVW 32(R13), R3
+ BIC $0xfc000000, R11, R11
+ BIC $0xfc000000, R12, R12
+ ADD R0, R5, R5
+ ADD g, R6, R6
+ ORR R3, R4, R4
+ ADD R11, R7, R7
+ ADD $64, R13, R14
+ ADD R12, R8, R8
+ ADD R4, R9, R9
+ MOVM.IA (R14), [R0-R4]
+ MULLU R4, R5, (R11, g)
+ MULLU R3, R5, (R14, R12)
+ MULALU R3, R6, (R11, g)
+ MULALU R2, R6, (R14, R12)
+ MULALU R2, R7, (R11, g)
+ MULALU R1, R7, (R14, R12)
+ ADD R4<<2, R4, R4
+ ADD R3<<2, R3, R3
+ MULALU R1, R8, (R11, g)
+ MULALU R0, R8, (R14, R12)
+ MULALU R0, R9, (R11, g)
+ MULALU R4, R9, (R14, R12)
+ MOVW g, 24(R13)
+ MOVW R11, 28(R13)
+ MOVW R12, 16(R13)
+ MOVW R14, 20(R13)
+ MULLU R2, R5, (R11, g)
+ MULLU R1, R5, (R14, R12)
+ MULALU R1, R6, (R11, g)
+ MULALU R0, R6, (R14, R12)
+ MULALU R0, R7, (R11, g)
+ MULALU R4, R7, (R14, R12)
+ ADD R2<<2, R2, R2
+ ADD R1<<2, R1, R1
+ MULALU R4, R8, (R11, g)
+ MULALU R3, R8, (R14, R12)
+ MULALU R3, R9, (R11, g)
+ MULALU R2, R9, (R14, R12)
+ MOVW g, 8(R13)
+ MOVW R11, 12(R13)
+ MOVW R12, 0(R13)
+ MOVW R14, w+4(SP)
+ MULLU R0, R5, (R11, g)
+ MULALU R4, R6, (R11, g)
+ MULALU R3, R7, (R11, g)
+ MULALU R2, R8, (R11, g)
+ MULALU R1, R9, (R11, g)
+ MOVM.IA (R13), [R0-R7]
+ MOVW g>>26, R12
+ MOVW R4>>26, R14
+ ORR R11<<6, R12, R12
+ ORR R5<<6, R14, R14
+ BIC $0xfc000000, g, g
+ BIC $0xfc000000, R4, R4
+ ADD.S R12, R0, R0
+ ADC $0, R1, R1
+ ADD.S R14, R6, R6
+ ADC $0, R7, R7
+ MOVW R0>>26, R12
+ MOVW R6>>26, R14
+ ORR R1<<6, R12, R12
+ ORR R7<<6, R14, R14
+ BIC $0xfc000000, R0, R0
+ BIC $0xfc000000, R6, R6
+ ADD R14<<2, R14, R14
+ ADD.S R12, R2, R2
+ ADC $0, R3, R3
+ ADD R14, g, g
+ MOVW R2>>26, R12
+ MOVW g>>26, R14
+ ORR R3<<6, R12, R12
+ BIC $0xfc000000, g, R5
+ BIC $0xfc000000, R2, R7
+ ADD R12, R4, R4
+ ADD R14, R0, R0
+ MOVW R4>>26, R12
+ BIC $0xfc000000, R4, R8
+ ADD R12, R6, R9
+ MOVW w+44(SP), R12
+ MOVW w+40(SP), R14
+ MOVW R0, R6
+ CMP $32, R12
+ SUB $16, R12, R12
+ MOVW R12, 44(R13)
+ BHS poly1305_blocks_armv6_mainloop
+poly1305_blocks_armv6_done:
+ MOVW 36(R13), R12
+ MOVW R5, 20(R12)
+ MOVW R6, 24(R12)
+ MOVW R7, 28(R12)
+ MOVW R8, 32(R12)
+ MOVW R9, 36(R12)
+ ADD $128, R13, R13
+ MOVM.IA.W (R13), [R4, R5, R6, R7, R8, R9, g, R11, R14]
+ RET
+
+#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \
+ MOVBU.P 1(Rsrc), Rtmp; \
+ MOVBU.P Rtmp, 1(Rdst); \
+ MOVBU.P 1(Rsrc), Rtmp; \
+ MOVBU.P Rtmp, 1(Rdst)
+
+#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \
+ MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \
+ MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp)
+
+TEXT poly1305_finish_ext_armv6<>(SB),4,$-4
+ MOVM.DB.W [R4, R5, R6, R7, R8, R9, g, R11, R14], (R13)
+ SUB $16, R13, R13
+ MOVW R0, R5
+ MOVW R1, R6
+ MOVW R2, R7
+ MOVW R3, R8
+ AND.S R2, R2, R2
+ BEQ poly1305_finish_ext_armv6_noremaining
+ EOR R0, R0
+ MOVW R13, R9
+ MOVW R0, 0(R13)
+ MOVW R0, 4(R13)
+ MOVW R0, 8(R13)
+ MOVW R0, 12(R13)
+ WORD $0xe3110003 // TST R1, #3 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_aligned
+ WORD $0xe3120008 // TST R2, #8 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip8
+ MOVWP_UNALIGNED(R1, R9, g)
+ MOVWP_UNALIGNED(R1, R9, g)
+poly1305_finish_ext_armv6_skip8:
+ WORD $0xe3120004 // TST $4, R2 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip4
+ MOVWP_UNALIGNED(R1, R9, g)
+poly1305_finish_ext_armv6_skip4:
+ WORD $0xe3120002 // TST $2, R2 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip2
+ MOVHUP_UNALIGNED(R1, R9, g)
+ B poly1305_finish_ext_armv6_skip2
+poly1305_finish_ext_armv6_aligned:
+ WORD $0xe3120008 // TST R2, #8 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip8_aligned
+ MOVM.IA.W (R1), [g-R11]
+ MOVM.IA.W [g-R11], (R9)
+poly1305_finish_ext_armv6_skip8_aligned:
+ WORD $0xe3120004 // TST $4, R2 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip4_aligned
+ MOVW.P 4(R1), g
+ MOVW.P g, 4(R9)
+poly1305_finish_ext_armv6_skip4_aligned:
+ WORD $0xe3120002 // TST $2, R2 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip2
+ MOVHU.P 2(R1), g
+ MOVH.P g, 2(R9)
+poly1305_finish_ext_armv6_skip2:
+ WORD $0xe3120001 // TST $1, R2 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip1
+ MOVBU.P 1(R1), g
+ MOVBU.P g, 1(R9)
+poly1305_finish_ext_armv6_skip1:
+ MOVW $1, R11
+ MOVBU R11, 0(R9)
+ MOVW R11, 56(R5)
+ MOVW R5, R0
+ MOVW R13, R1
+ MOVW $16, R2
+ BL poly1305_blocks_armv6<>(SB)
+poly1305_finish_ext_armv6_noremaining:
+ MOVW 20(R5), R0
+ MOVW 24(R5), R1
+ MOVW 28(R5), R2
+ MOVW 32(R5), R3
+ MOVW 36(R5), R4
+ MOVW R4>>26, R12
+ BIC $0xfc000000, R4, R4
+ ADD R12<<2, R12, R12
+ ADD R12, R0, R0
+ MOVW R0>>26, R12
+ BIC $0xfc000000, R0, R0
+ ADD R12, R1, R1
+ MOVW R1>>26, R12
+ BIC $0xfc000000, R1, R1
+ ADD R12, R2, R2
+ MOVW R2>>26, R12
+ BIC $0xfc000000, R2, R2
+ ADD R12, R3, R3
+ MOVW R3>>26, R12
+ BIC $0xfc000000, R3, R3
+ ADD R12, R4, R4
+ ADD $5, R0, R6
+ MOVW R6>>26, R12
+ BIC $0xfc000000, R6, R6
+ ADD R12, R1, R7
+ MOVW R7>>26, R12
+ BIC $0xfc000000, R7, R7
+ ADD R12, R2, g
+ MOVW g>>26, R12
+ BIC $0xfc000000, g, g
+ ADD R12, R3, R11
+ MOVW $-(1<<26), R12
+ ADD R11>>26, R12, R12
+ BIC $0xfc000000, R11, R11
+ ADD R12, R4, R14
+ MOVW R14>>31, R12
+ SUB $1, R12
+ AND R12, R6, R6
+ AND R12, R7, R7
+ AND R12, g, g
+ AND R12, R11, R11
+ AND R12, R14, R14
+ MVN R12, R12
+ AND R12, R0, R0
+ AND R12, R1, R1
+ AND R12, R2, R2
+ AND R12, R3, R3
+ AND R12, R4, R4
+ ORR R6, R0, R0
+ ORR R7, R1, R1
+ ORR g, R2, R2
+ ORR R11, R3, R3
+ ORR R14, R4, R4
+ ORR R1<<26, R0, R0
+ MOVW R1>>6, R1
+ ORR R2<<20, R1, R1
+ MOVW R2>>12, R2
+ ORR R3<<14, R2, R2
+ MOVW R3>>18, R3
+ ORR R4<<8, R3, R3
+ MOVW 40(R5), R6
+ MOVW 44(R5), R7
+ MOVW 48(R5), g
+ MOVW 52(R5), R11
+ ADD.S R6, R0, R0
+ ADC.S R7, R1, R1
+ ADC.S g, R2, R2
+ ADC.S R11, R3, R3
+ MOVM.IA [R0-R3], (R8)
+ MOVW R5, R12
+ EOR R0, R0, R0
+ EOR R1, R1, R1
+ EOR R2, R2, R2
+ EOR R3, R3, R3
+ EOR R4, R4, R4
+ EOR R5, R5, R5
+ EOR R6, R6, R6
+ EOR R7, R7, R7
+ MOVM.IA.W [R0-R7], (R12)
+ MOVM.IA [R0-R7], (R12)
+ ADD $16, R13, R13
+ MOVM.IA.W (R13), [R4, R5, R6, R7, R8, R9, g, R11, R14]
+ RET
+
+// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key)
+TEXT ·poly1305_auth_armv6(SB),0,$280-16
+ MOVW out+0(FP), R4
+ MOVW m+4(FP), R5
+ MOVW mlen+8(FP), R6
+ MOVW key+12(FP), R7
+
+ MOVW R13, R8
+ BIC $63, R13
+ SUB $64, R13, R13
+ MOVW R13, R0
+ MOVW R7, R1
+ BL poly1305_init_ext_armv6<>(SB)
+ BIC.S $15, R6, R2
+ BEQ poly1305_auth_armv6_noblocks
+ MOVW R13, R0
+ MOVW R5, R1
+ ADD R2, R5, R5
+ SUB R2, R6, R6
+ BL poly1305_blocks_armv6<>(SB)
+poly1305_auth_armv6_noblocks:
+ MOVW R13, R0
+ MOVW R5, R1
+ MOVW R6, R2
+ MOVW R4, R3
+ BL poly1305_finish_ext_armv6<>(SB)
+ MOVW R8, R13
+ RET
diff --git a/vendor/golang.org/x/crypto/poly1305/sum_amd64.go b/vendor/golang.org/x/crypto/poly1305/sum_amd64.go
new file mode 100644
index 00000000..6775c703
--- /dev/null
+++ b/vendor/golang.org/x/crypto/poly1305/sum_amd64.go
@@ -0,0 +1,24 @@
+// Copyright 2012 The Go 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 amd64,!gccgo,!appengine
+
+package poly1305
+
+// This function is implemented in poly1305_amd64.s
+
+//go:noescape
+
+func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+ var mPtr *byte
+ if len(m) > 0 {
+ mPtr = &m[0]
+ }
+ poly1305(out, mPtr, uint64(len(m)), key)
+}
diff --git a/vendor/golang.org/x/crypto/poly1305/sum_arm.go b/vendor/golang.org/x/crypto/poly1305/sum_arm.go
new file mode 100644
index 00000000..50b979c2
--- /dev/null
+++ b/vendor/golang.org/x/crypto/poly1305/sum_arm.go
@@ -0,0 +1,24 @@
+// Copyright 2015 The Go 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 arm,!gccgo,!appengine
+
+package poly1305
+
+// This function is implemented in poly1305_arm.s
+
+//go:noescape
+
+func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+ var mPtr *byte
+ if len(m) > 0 {
+ mPtr = &m[0]
+ }
+ poly1305_auth_armv6(out, mPtr, uint32(len(m)), key)
+}
diff --git a/vendor/golang.org/x/crypto/poly1305/sum_ref.go b/vendor/golang.org/x/crypto/poly1305/sum_ref.go
new file mode 100644
index 00000000..0b24fc78
--- /dev/null
+++ b/vendor/golang.org/x/crypto/poly1305/sum_ref.go
@@ -0,0 +1,1531 @@
+// Copyright 2012 The Go 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 !amd64,!arm gccgo appengine
+
+package poly1305
+
+// Based on original, public domain implementation from NaCl by D. J.
+// Bernstein.
+
+import "math"
+
+const (
+ alpham80 = 0.00000000558793544769287109375
+ alpham48 = 24.0
+ alpham16 = 103079215104.0
+ alpha0 = 6755399441055744.0
+ alpha18 = 1770887431076116955136.0
+ alpha32 = 29014219670751100192948224.0
+ alpha50 = 7605903601369376408980219232256.0
+ alpha64 = 124615124604835863084731911901282304.0
+ alpha82 = 32667107224410092492483962313449748299776.0
+ alpha96 = 535217884764734955396857238543560676143529984.0
+ alpha112 = 35076039295941670036888435985190792471742381031424.0
+ alpha130 = 9194973245195333150150082162901855101712434733101613056.0
+ scale = 0.0000000000000000000000000000000000000036734198463196484624023016788195177431833298649127735047148490821200539357960224151611328125
+ offset0 = 6755408030990331.0
+ offset1 = 29014256564239239022116864.0
+ offset2 = 124615283061160854719918951570079744.0
+ offset3 = 535219245894202480694386063513315216128475136.0
+)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+ r := key
+ s := key[16:]
+ var (
+ y7 float64
+ y6 float64
+ y1 float64
+ y0 float64
+ y5 float64
+ y4 float64
+ x7 float64
+ x6 float64
+ x1 float64
+ x0 float64
+ y3 float64
+ y2 float64
+ x5 float64
+ r3lowx0 float64
+ x4 float64
+ r0lowx6 float64
+ x3 float64
+ r3highx0 float64
+ x2 float64
+ r0highx6 float64
+ r0lowx0 float64
+ sr1lowx6 float64
+ r0highx0 float64
+ sr1highx6 float64
+ sr3low float64
+ r1lowx0 float64
+ sr2lowx6 float64
+ r1highx0 float64
+ sr2highx6 float64
+ r2lowx0 float64
+ sr3lowx6 float64
+ r2highx0 float64
+ sr3highx6 float64
+ r1highx4 float64
+ r1lowx4 float64
+ r0highx4 float64
+ r0lowx4 float64
+ sr3highx4 float64
+ sr3lowx4 float64
+ sr2highx4 float64
+ sr2lowx4 float64
+ r0lowx2 float64
+ r0highx2 float64
+ r1lowx2 float64
+ r1highx2 float64
+ r2lowx2 float64
+ r2highx2 float64
+ sr3lowx2 float64
+ sr3highx2 float64
+ z0 float64
+ z1 float64
+ z2 float64
+ z3 float64
+ m0 int64
+ m1 int64
+ m2 int64
+ m3 int64
+ m00 uint32
+ m01 uint32
+ m02 uint32
+ m03 uint32
+ m10 uint32
+ m11 uint32
+ m12 uint32
+ m13 uint32
+ m20 uint32
+ m21 uint32
+ m22 uint32
+ m23 uint32
+ m30 uint32
+ m31 uint32
+ m32 uint32
+ m33 uint64
+ lbelow2 int32
+ lbelow3 int32
+ lbelow4 int32
+ lbelow5 int32
+ lbelow6 int32
+ lbelow7 int32
+ lbelow8 int32
+ lbelow9 int32
+ lbelow10 int32
+ lbelow11 int32
+ lbelow12 int32
+ lbelow13 int32
+ lbelow14 int32
+ lbelow15 int32
+ s00 uint32
+ s01 uint32
+ s02 uint32
+ s03 uint32
+ s10 uint32
+ s11 uint32
+ s12 uint32
+ s13 uint32
+ s20 uint32
+ s21 uint32
+ s22 uint32
+ s23 uint32
+ s30 uint32
+ s31 uint32
+ s32 uint32
+ s33 uint32
+ bits32 uint64
+ f uint64
+ f0 uint64
+ f1 uint64
+ f2 uint64
+ f3 uint64
+ f4 uint64
+ g uint64
+ g0 uint64
+ g1 uint64
+ g2 uint64
+ g3 uint64
+ g4 uint64
+ )
+
+ var p int32
+
+ l := int32(len(m))
+
+ r00 := uint32(r[0])
+
+ r01 := uint32(r[1])
+
+ r02 := uint32(r[2])
+ r0 := int64(2151)
+
+ r03 := uint32(r[3])
+ r03 &= 15
+ r0 <<= 51
+
+ r10 := uint32(r[4])
+ r10 &= 252
+ r01 <<= 8
+ r0 += int64(r00)
+
+ r11 := uint32(r[5])
+ r02 <<= 16
+ r0 += int64(r01)
+
+ r12 := uint32(r[6])
+ r03 <<= 24
+ r0 += int64(r02)
+
+ r13 := uint32(r[7])
+ r13 &= 15
+ r1 := int64(2215)
+ r0 += int64(r03)
+
+ d0 := r0
+ r1 <<= 51
+ r2 := int64(2279)
+
+ r20 := uint32(r[8])
+ r20 &= 252
+ r11 <<= 8
+ r1 += int64(r10)
+
+ r21 := uint32(r[9])
+ r12 <<= 16
+ r1 += int64(r11)
+
+ r22 := uint32(r[10])
+ r13 <<= 24
+ r1 += int64(r12)
+
+ r23 := uint32(r[11])
+ r23 &= 15
+ r2 <<= 51
+ r1 += int64(r13)
+
+ d1 := r1
+ r21 <<= 8
+ r2 += int64(r20)
+
+ r30 := uint32(r[12])
+ r30 &= 252
+ r22 <<= 16
+ r2 += int64(r21)
+
+ r31 := uint32(r[13])
+ r23 <<= 24
+ r2 += int64(r22)
+
+ r32 := uint32(r[14])
+ r2 += int64(r23)
+ r3 := int64(2343)
+
+ d2 := r2
+ r3 <<= 51
+
+ r33 := uint32(r[15])
+ r33 &= 15
+ r31 <<= 8
+ r3 += int64(r30)
+
+ r32 <<= 16
+ r3 += int64(r31)
+
+ r33 <<= 24
+ r3 += int64(r32)
+
+ r3 += int64(r33)
+ h0 := alpha32 - alpha32
+
+ d3 := r3
+ h1 := alpha32 - alpha32
+
+ h2 := alpha32 - alpha32
+
+ h3 := alpha32 - alpha32
+
+ h4 := alpha32 - alpha32
+
+ r0low := math.Float64frombits(uint64(d0))
+ h5 := alpha32 - alpha32
+
+ r1low := math.Float64frombits(uint64(d1))
+ h6 := alpha32 - alpha32
+
+ r2low := math.Float64frombits(uint64(d2))
+ h7 := alpha32 - alpha32
+
+ r0low -= alpha0
+
+ r1low -= alpha32
+
+ r2low -= alpha64
+
+ r0high := r0low + alpha18
+
+ r3low := math.Float64frombits(uint64(d3))
+
+ r1high := r1low + alpha50
+ sr1low := scale * r1low
+
+ r2high := r2low + alpha82
+ sr2low := scale * r2low
+
+ r0high -= alpha18
+ r0high_stack := r0high
+
+ r3low -= alpha96
+
+ r1high -= alpha50
+ r1high_stack := r1high
+
+ sr1high := sr1low + alpham80
+
+ r0low -= r0high
+
+ r2high -= alpha82
+ sr3low = scale * r3low
+
+ sr2high := sr2low + alpham48
+
+ r1low -= r1high
+ r1low_stack := r1low
+
+ sr1high -= alpham80
+ sr1high_stack := sr1high
+
+ r2low -= r2high
+ r2low_stack := r2low
+
+ sr2high -= alpham48
+ sr2high_stack := sr2high
+
+ r3high := r3low + alpha112
+ r0low_stack := r0low
+
+ sr1low -= sr1high
+ sr1low_stack := sr1low
+
+ sr3high := sr3low + alpham16
+ r2high_stack := r2high
+
+ sr2low -= sr2high
+ sr2low_stack := sr2low
+
+ r3high -= alpha112
+ r3high_stack := r3high
+
+ sr3high -= alpham16
+ sr3high_stack := sr3high
+
+ r3low -= r3high
+ r3low_stack := r3low
+
+ sr3low -= sr3high
+ sr3low_stack := sr3low
+
+ if l < 16 {
+ goto addatmost15bytes
+ }
+
+ m00 = uint32(m[p+0])
+ m0 = 2151
+
+ m0 <<= 51
+ m1 = 2215
+ m01 = uint32(m[p+1])
+
+ m1 <<= 51
+ m2 = 2279
+ m02 = uint32(m[p+2])
+
+ m2 <<= 51
+ m3 = 2343
+ m03 = uint32(m[p+3])
+
+ m10 = uint32(m[p+4])
+ m01 <<= 8
+ m0 += int64(m00)
+
+ m11 = uint32(m[p+5])
+ m02 <<= 16
+ m0 += int64(m01)
+
+ m12 = uint32(m[p+6])
+ m03 <<= 24
+ m0 += int64(m02)
+
+ m13 = uint32(m[p+7])
+ m3 <<= 51
+ m0 += int64(m03)
+
+ m20 = uint32(m[p+8])
+ m11 <<= 8
+ m1 += int64(m10)
+
+ m21 = uint32(m[p+9])
+ m12 <<= 16
+ m1 += int64(m11)
+
+ m22 = uint32(m[p+10])
+ m13 <<= 24
+ m1 += int64(m12)
+
+ m23 = uint32(m[p+11])
+ m1 += int64(m13)
+
+ m30 = uint32(m[p+12])
+ m21 <<= 8
+ m2 += int64(m20)
+
+ m31 = uint32(m[p+13])
+ m22 <<= 16
+ m2 += int64(m21)
+
+ m32 = uint32(m[p+14])
+ m23 <<= 24
+ m2 += int64(m22)
+
+ m33 = uint64(m[p+15])
+ m2 += int64(m23)
+
+ d0 = m0
+ m31 <<= 8
+ m3 += int64(m30)
+
+ d1 = m1
+ m32 <<= 16
+ m3 += int64(m31)
+
+ d2 = m2
+ m33 += 256
+
+ m33 <<= 24
+ m3 += int64(m32)
+
+ m3 += int64(m33)
+ d3 = m3
+
+ p += 16
+ l -= 16
+
+ z0 = math.Float64frombits(uint64(d0))
+
+ z1 = math.Float64frombits(uint64(d1))
+
+ z2 = math.Float64frombits(uint64(d2))
+
+ z3 = math.Float64frombits(uint64(d3))
+
+ z0 -= alpha0
+
+ z1 -= alpha32
+
+ z2 -= alpha64
+
+ z3 -= alpha96
+
+ h0 += z0
+
+ h1 += z1
+
+ h3 += z2
+
+ h5 += z3
+
+ if l < 16 {
+ goto multiplyaddatmost15bytes
+ }
+
+multiplyaddatleast16bytes:
+
+ m2 = 2279
+ m20 = uint32(m[p+8])
+ y7 = h7 + alpha130
+
+ m2 <<= 51
+ m3 = 2343
+ m21 = uint32(m[p+9])
+ y6 = h6 + alpha130
+
+ m3 <<= 51
+ m0 = 2151
+ m22 = uint32(m[p+10])
+ y1 = h1 + alpha32
+
+ m0 <<= 51
+ m1 = 2215
+ m23 = uint32(m[p+11])
+ y0 = h0 + alpha32
+
+ m1 <<= 51
+ m30 = uint32(m[p+12])
+ y7 -= alpha130
+
+ m21 <<= 8
+ m2 += int64(m20)
+ m31 = uint32(m[p+13])
+ y6 -= alpha130
+
+ m22 <<= 16
+ m2 += int64(m21)
+ m32 = uint32(m[p+14])
+ y1 -= alpha32
+
+ m23 <<= 24
+ m2 += int64(m22)
+ m33 = uint64(m[p+15])
+ y0 -= alpha32
+
+ m2 += int64(m23)
+ m00 = uint32(m[p+0])
+ y5 = h5 + alpha96
+
+ m31 <<= 8
+ m3 += int64(m30)
+ m01 = uint32(m[p+1])
+ y4 = h4 + alpha96
+
+ m32 <<= 16
+ m02 = uint32(m[p+2])
+ x7 = h7 - y7
+ y7 *= scale
+
+ m33 += 256
+ m03 = uint32(m[p+3])
+ x6 = h6 - y6
+ y6 *= scale
+
+ m33 <<= 24
+ m3 += int64(m31)
+ m10 = uint32(m[p+4])
+ x1 = h1 - y1
+
+ m01 <<= 8
+ m3 += int64(m32)
+ m11 = uint32(m[p+5])
+ x0 = h0 - y0
+
+ m3 += int64(m33)
+ m0 += int64(m00)
+ m12 = uint32(m[p+6])
+ y5 -= alpha96
+
+ m02 <<= 16
+ m0 += int64(m01)
+ m13 = uint32(m[p+7])
+ y4 -= alpha96
+
+ m03 <<= 24
+ m0 += int64(m02)
+ d2 = m2
+ x1 += y7
+
+ m0 += int64(m03)
+ d3 = m3
+ x0 += y6
+
+ m11 <<= 8
+ m1 += int64(m10)
+ d0 = m0
+ x7 += y5
+
+ m12 <<= 16
+ m1 += int64(m11)
+ x6 += y4
+
+ m13 <<= 24
+ m1 += int64(m12)
+ y3 = h3 + alpha64
+
+ m1 += int64(m13)
+ d1 = m1
+ y2 = h2 + alpha64
+
+ x0 += x1
+
+ x6 += x7
+
+ y3 -= alpha64
+ r3low = r3low_stack
+
+ y2 -= alpha64
+ r0low = r0low_stack
+
+ x5 = h5 - y5
+ r3lowx0 = r3low * x0
+ r3high = r3high_stack
+
+ x4 = h4 - y4
+ r0lowx6 = r0low * x6
+ r0high = r0high_stack
+
+ x3 = h3 - y3
+ r3highx0 = r3high * x0
+ sr1low = sr1low_stack
+
+ x2 = h2 - y2
+ r0highx6 = r0high * x6
+ sr1high = sr1high_stack
+
+ x5 += y3
+ r0lowx0 = r0low * x0
+ r1low = r1low_stack
+
+ h6 = r3lowx0 + r0lowx6
+ sr1lowx6 = sr1low * x6
+ r1high = r1high_stack
+
+ x4 += y2
+ r0highx0 = r0high * x0
+ sr2low = sr2low_stack
+
+ h7 = r3highx0 + r0highx6
+ sr1highx6 = sr1high * x6
+ sr2high = sr2high_stack
+
+ x3 += y1
+ r1lowx0 = r1low * x0
+ r2low = r2low_stack
+
+ h0 = r0lowx0 + sr1lowx6
+ sr2lowx6 = sr2low * x6
+ r2high = r2high_stack
+
+ x2 += y0
+ r1highx0 = r1high * x0
+ sr3low = sr3low_stack
+
+ h1 = r0highx0 + sr1highx6
+ sr2highx6 = sr2high * x6
+ sr3high = sr3high_stack
+
+ x4 += x5
+ r2lowx0 = r2low * x0
+ z2 = math.Float64frombits(uint64(d2))
+
+ h2 = r1lowx0 + sr2lowx6
+ sr3lowx6 = sr3low * x6
+
+ x2 += x3
+ r2highx0 = r2high * x0
+ z3 = math.Float64frombits(uint64(d3))
+
+ h3 = r1highx0 + sr2highx6
+ sr3highx6 = sr3high * x6
+
+ r1highx4 = r1high * x4
+ z2 -= alpha64
+
+ h4 = r2lowx0 + sr3lowx6
+ r1lowx4 = r1low * x4
+
+ r0highx4 = r0high * x4
+ z3 -= alpha96
+
+ h5 = r2highx0 + sr3highx6
+ r0lowx4 = r0low * x4
+
+ h7 += r1highx4
+ sr3highx4 = sr3high * x4
+
+ h6 += r1lowx4
+ sr3lowx4 = sr3low * x4
+
+ h5 += r0highx4
+ sr2highx4 = sr2high * x4
+
+ h4 += r0lowx4
+ sr2lowx4 = sr2low * x4
+
+ h3 += sr3highx4
+ r0lowx2 = r0low * x2
+
+ h2 += sr3lowx4
+ r0highx2 = r0high * x2
+
+ h1 += sr2highx4
+ r1lowx2 = r1low * x2
+
+ h0 += sr2lowx4
+ r1highx2 = r1high * x2
+
+ h2 += r0lowx2
+ r2lowx2 = r2low * x2
+
+ h3 += r0highx2
+ r2highx2 = r2high * x2
+
+ h4 += r1lowx2
+ sr3lowx2 = sr3low * x2
+
+ h5 += r1highx2
+ sr3highx2 = sr3high * x2
+
+ p += 16
+ l -= 16
+ h6 += r2lowx2
+
+ h7 += r2highx2
+
+ z1 = math.Float64frombits(uint64(d1))
+ h0 += sr3lowx2
+
+ z0 = math.Float64frombits(uint64(d0))
+ h1 += sr3highx2
+
+ z1 -= alpha32
+
+ z0 -= alpha0
+
+ h5 += z3
+
+ h3 += z2
+
+ h1 += z1
+
+ h0 += z0
+
+ if l >= 16 {
+ goto multiplyaddatleast16bytes
+ }
+
+multiplyaddatmost15bytes:
+
+ y7 = h7 + alpha130
+
+ y6 = h6 + alpha130
+
+ y1 = h1 + alpha32
+
+ y0 = h0 + alpha32
+
+ y7 -= alpha130
+
+ y6 -= alpha130
+
+ y1 -= alpha32
+
+ y0 -= alpha32
+
+ y5 = h5 + alpha96
+
+ y4 = h4 + alpha96
+
+ x7 = h7 - y7
+ y7 *= scale
+
+ x6 = h6 - y6
+ y6 *= scale
+
+ x1 = h1 - y1
+
+ x0 = h0 - y0
+
+ y5 -= alpha96
+
+ y4 -= alpha96
+
+ x1 += y7
+
+ x0 += y6
+
+ x7 += y5
+
+ x6 += y4
+
+ y3 = h3 + alpha64
+
+ y2 = h2 + alpha64
+
+ x0 += x1
+
+ x6 += x7
+
+ y3 -= alpha64
+ r3low = r3low_stack
+
+ y2 -= alpha64
+ r0low = r0low_stack
+
+ x5 = h5 - y5
+ r3lowx0 = r3low * x0
+ r3high = r3high_stack
+
+ x4 = h4 - y4
+ r0lowx6 = r0low * x6
+ r0high = r0high_stack
+
+ x3 = h3 - y3
+ r3highx0 = r3high * x0
+ sr1low = sr1low_stack
+
+ x2 = h2 - y2
+ r0highx6 = r0high * x6
+ sr1high = sr1high_stack
+
+ x5 += y3
+ r0lowx0 = r0low * x0
+ r1low = r1low_stack
+
+ h6 = r3lowx0 + r0lowx6
+ sr1lowx6 = sr1low * x6
+ r1high = r1high_stack
+
+ x4 += y2
+ r0highx0 = r0high * x0
+ sr2low = sr2low_stack
+
+ h7 = r3highx0 + r0highx6
+ sr1highx6 = sr1high * x6
+ sr2high = sr2high_stack
+
+ x3 += y1
+ r1lowx0 = r1low * x0
+ r2low = r2low_stack
+
+ h0 = r0lowx0 + sr1lowx6
+ sr2lowx6 = sr2low * x6
+ r2high = r2high_stack
+
+ x2 += y0
+ r1highx0 = r1high * x0
+ sr3low = sr3low_stack
+
+ h1 = r0highx0 + sr1highx6
+ sr2highx6 = sr2high * x6
+ sr3high = sr3high_stack
+
+ x4 += x5
+ r2lowx0 = r2low * x0
+
+ h2 = r1lowx0 + sr2lowx6
+ sr3lowx6 = sr3low * x6
+
+ x2 += x3
+ r2highx0 = r2high * x0
+
+ h3 = r1highx0 + sr2highx6
+ sr3highx6 = sr3high * x6
+
+ r1highx4 = r1high * x4
+
+ h4 = r2lowx0 + sr3lowx6
+ r1lowx4 = r1low * x4
+
+ r0highx4 = r0high * x4
+
+ h5 = r2highx0 + sr3highx6
+ r0lowx4 = r0low * x4
+
+ h7 += r1highx4
+ sr3highx4 = sr3high * x4
+
+ h6 += r1lowx4
+ sr3lowx4 = sr3low * x4
+
+ h5 += r0highx4
+ sr2highx4 = sr2high * x4
+
+ h4 += r0lowx4
+ sr2lowx4 = sr2low * x4
+
+ h3 += sr3highx4
+ r0lowx2 = r0low * x2
+
+ h2 += sr3lowx4
+ r0highx2 = r0high * x2
+
+ h1 += sr2highx4
+ r1lowx2 = r1low * x2
+
+ h0 += sr2lowx4
+ r1highx2 = r1high * x2
+
+ h2 += r0lowx2
+ r2lowx2 = r2low * x2
+
+ h3 += r0highx2
+ r2highx2 = r2high * x2
+
+ h4 += r1lowx2
+ sr3lowx2 = sr3low * x2
+
+ h5 += r1highx2
+ sr3highx2 = sr3high * x2
+
+ h6 += r2lowx2
+
+ h7 += r2highx2
+
+ h0 += sr3lowx2
+
+ h1 += sr3highx2
+
+addatmost15bytes:
+
+ if l == 0 {
+ goto nomorebytes
+ }
+
+ lbelow2 = l - 2
+
+ lbelow3 = l - 3
+
+ lbelow2 >>= 31
+ lbelow4 = l - 4
+
+ m00 = uint32(m[p+0])
+ lbelow3 >>= 31
+ p += lbelow2
+
+ m01 = uint32(m[p+1])
+ lbelow4 >>= 31
+ p += lbelow3
+
+ m02 = uint32(m[p+2])
+ p += lbelow4
+ m0 = 2151
+
+ m03 = uint32(m[p+3])
+ m0 <<= 51
+ m1 = 2215
+
+ m0 += int64(m00)
+ m01 &^= uint32(lbelow2)
+
+ m02 &^= uint32(lbelow3)
+ m01 -= uint32(lbelow2)
+
+ m01 <<= 8
+ m03 &^= uint32(lbelow4)
+
+ m0 += int64(m01)
+ lbelow2 -= lbelow3
+
+ m02 += uint32(lbelow2)
+ lbelow3 -= lbelow4
+
+ m02 <<= 16
+ m03 += uint32(lbelow3)
+
+ m03 <<= 24
+ m0 += int64(m02)
+
+ m0 += int64(m03)
+ lbelow5 = l - 5
+
+ lbelow6 = l - 6
+ lbelow7 = l - 7
+
+ lbelow5 >>= 31
+ lbelow8 = l - 8
+
+ lbelow6 >>= 31
+ p += lbelow5
+
+ m10 = uint32(m[p+4])
+ lbelow7 >>= 31
+ p += lbelow6
+
+ m11 = uint32(m[p+5])
+ lbelow8 >>= 31
+ p += lbelow7
+
+ m12 = uint32(m[p+6])
+ m1 <<= 51
+ p += lbelow8
+
+ m13 = uint32(m[p+7])
+ m10 &^= uint32(lbelow5)
+ lbelow4 -= lbelow5
+
+ m10 += uint32(lbelow4)
+ lbelow5 -= lbelow6
+
+ m11 &^= uint32(lbelow6)
+ m11 += uint32(lbelow5)
+
+ m11 <<= 8
+ m1 += int64(m10)
+
+ m1 += int64(m11)
+ m12 &^= uint32(lbelow7)
+
+ lbelow6 -= lbelow7
+ m13 &^= uint32(lbelow8)
+
+ m12 += uint32(lbelow6)
+ lbelow7 -= lbelow8
+
+ m12 <<= 16
+ m13 += uint32(lbelow7)
+
+ m13 <<= 24
+ m1 += int64(m12)
+
+ m1 += int64(m13)
+ m2 = 2279
+
+ lbelow9 = l - 9
+ m3 = 2343
+
+ lbelow10 = l - 10
+ lbelow11 = l - 11
+
+ lbelow9 >>= 31
+ lbelow12 = l - 12
+
+ lbelow10 >>= 31
+ p += lbelow9
+
+ m20 = uint32(m[p+8])
+ lbelow11 >>= 31
+ p += lbelow10
+
+ m21 = uint32(m[p+9])
+ lbelow12 >>= 31
+ p += lbelow11
+
+ m22 = uint32(m[p+10])
+ m2 <<= 51
+ p += lbelow12
+
+ m23 = uint32(m[p+11])
+ m20 &^= uint32(lbelow9)
+ lbelow8 -= lbelow9
+
+ m20 += uint32(lbelow8)
+ lbelow9 -= lbelow10
+
+ m21 &^= uint32(lbelow10)
+ m21 += uint32(lbelow9)
+
+ m21 <<= 8
+ m2 += int64(m20)
+
+ m2 += int64(m21)
+ m22 &^= uint32(lbelow11)
+
+ lbelow10 -= lbelow11
+ m23 &^= uint32(lbelow12)
+
+ m22 += uint32(lbelow10)
+ lbelow11 -= lbelow12
+
+ m22 <<= 16
+ m23 += uint32(lbelow11)
+
+ m23 <<= 24
+ m2 += int64(m22)
+
+ m3 <<= 51
+ lbelow13 = l - 13
+
+ lbelow13 >>= 31
+ lbelow14 = l - 14
+
+ lbelow14 >>= 31
+ p += lbelow13
+ lbelow15 = l - 15
+
+ m30 = uint32(m[p+12])
+ lbelow15 >>= 31
+ p += lbelow14
+
+ m31 = uint32(m[p+13])
+ p += lbelow15
+ m2 += int64(m23)
+
+ m32 = uint32(m[p+14])
+ m30 &^= uint32(lbelow13)
+ lbelow12 -= lbelow13
+
+ m30 += uint32(lbelow12)
+ lbelow13 -= lbelow14
+
+ m3 += int64(m30)
+ m31 &^= uint32(lbelow14)
+
+ m31 += uint32(lbelow13)
+ m32 &^= uint32(lbelow15)
+
+ m31 <<= 8
+ lbelow14 -= lbelow15
+
+ m3 += int64(m31)
+ m32 += uint32(lbelow14)
+ d0 = m0
+
+ m32 <<= 16
+ m33 = uint64(lbelow15 + 1)
+ d1 = m1
+
+ m33 <<= 24
+ m3 += int64(m32)
+ d2 = m2
+
+ m3 += int64(m33)
+ d3 = m3
+
+ z3 = math.Float64frombits(uint64(d3))
+
+ z2 = math.Float64frombits(uint64(d2))
+
+ z1 = math.Float64frombits(uint64(d1))
+
+ z0 = math.Float64frombits(uint64(d0))
+
+ z3 -= alpha96
+
+ z2 -= alpha64
+
+ z1 -= alpha32
+
+ z0 -= alpha0
+
+ h5 += z3
+
+ h3 += z2
+
+ h1 += z1
+
+ h0 += z0
+
+ y7 = h7 + alpha130
+
+ y6 = h6 + alpha130
+
+ y1 = h1 + alpha32
+
+ y0 = h0 + alpha32
+
+ y7 -= alpha130
+
+ y6 -= alpha130
+
+ y1 -= alpha32
+
+ y0 -= alpha32
+
+ y5 = h5 + alpha96
+
+ y4 = h4 + alpha96
+
+ x7 = h7 - y7
+ y7 *= scale
+
+ x6 = h6 - y6
+ y6 *= scale
+
+ x1 = h1 - y1
+
+ x0 = h0 - y0
+
+ y5 -= alpha96
+
+ y4 -= alpha96
+
+ x1 += y7
+
+ x0 += y6
+
+ x7 += y5
+
+ x6 += y4
+
+ y3 = h3 + alpha64
+
+ y2 = h2 + alpha64
+
+ x0 += x1
+
+ x6 += x7
+
+ y3 -= alpha64
+ r3low = r3low_stack
+
+ y2 -= alpha64
+ r0low = r0low_stack
+
+ x5 = h5 - y5
+ r3lowx0 = r3low * x0
+ r3high = r3high_stack
+
+ x4 = h4 - y4
+ r0lowx6 = r0low * x6
+ r0high = r0high_stack
+
+ x3 = h3 - y3
+ r3highx0 = r3high * x0
+ sr1low = sr1low_stack
+
+ x2 = h2 - y2
+ r0highx6 = r0high * x6
+ sr1high = sr1high_stack
+
+ x5 += y3
+ r0lowx0 = r0low * x0
+ r1low = r1low_stack
+
+ h6 = r3lowx0 + r0lowx6
+ sr1lowx6 = sr1low * x6
+ r1high = r1high_stack
+
+ x4 += y2
+ r0highx0 = r0high * x0
+ sr2low = sr2low_stack
+
+ h7 = r3highx0 + r0highx6
+ sr1highx6 = sr1high * x6
+ sr2high = sr2high_stack
+
+ x3 += y1
+ r1lowx0 = r1low * x0
+ r2low = r2low_stack
+
+ h0 = r0lowx0 + sr1lowx6
+ sr2lowx6 = sr2low * x6
+ r2high = r2high_stack
+
+ x2 += y0
+ r1highx0 = r1high * x0
+ sr3low = sr3low_stack
+
+ h1 = r0highx0 + sr1highx6
+ sr2highx6 = sr2high * x6
+ sr3high = sr3high_stack
+
+ x4 += x5
+ r2lowx0 = r2low * x0
+
+ h2 = r1lowx0 + sr2lowx6
+ sr3lowx6 = sr3low * x6
+
+ x2 += x3
+ r2highx0 = r2high * x0
+
+ h3 = r1highx0 + sr2highx6
+ sr3highx6 = sr3high * x6
+
+ r1highx4 = r1high * x4
+
+ h4 = r2lowx0 + sr3lowx6
+ r1lowx4 = r1low * x4
+
+ r0highx4 = r0high * x4
+
+ h5 = r2highx0 + sr3highx6
+ r0lowx4 = r0low * x4
+
+ h7 += r1highx4
+ sr3highx4 = sr3high * x4
+
+ h6 += r1lowx4
+ sr3lowx4 = sr3low * x4
+
+ h5 += r0highx4
+ sr2highx4 = sr2high * x4
+
+ h4 += r0lowx4
+ sr2lowx4 = sr2low * x4
+
+ h3 += sr3highx4
+ r0lowx2 = r0low * x2
+
+ h2 += sr3lowx4
+ r0highx2 = r0high * x2
+
+ h1 += sr2highx4
+ r1lowx2 = r1low * x2
+
+ h0 += sr2lowx4
+ r1highx2 = r1high * x2
+
+ h2 += r0lowx2
+ r2lowx2 = r2low * x2
+
+ h3 += r0highx2
+ r2highx2 = r2high * x2
+
+ h4 += r1lowx2
+ sr3lowx2 = sr3low * x2
+
+ h5 += r1highx2
+ sr3highx2 = sr3high * x2
+
+ h6 += r2lowx2
+
+ h7 += r2highx2
+
+ h0 += sr3lowx2
+
+ h1 += sr3highx2
+
+nomorebytes:
+
+ y7 = h7 + alpha130
+
+ y0 = h0 + alpha32
+
+ y1 = h1 + alpha32
+
+ y2 = h2 + alpha64
+
+ y7 -= alpha130
+
+ y3 = h3 + alpha64
+
+ y4 = h4 + alpha96
+
+ y5 = h5 + alpha96
+
+ x7 = h7 - y7
+ y7 *= scale
+
+ y0 -= alpha32
+
+ y1 -= alpha32
+
+ y2 -= alpha64
+
+ h6 += x7
+
+ y3 -= alpha64
+
+ y4 -= alpha96
+
+ y5 -= alpha96
+
+ y6 = h6 + alpha130
+
+ x0 = h0 - y0
+
+ x1 = h1 - y1
+
+ x2 = h2 - y2
+
+ y6 -= alpha130
+
+ x0 += y7
+
+ x3 = h3 - y3
+
+ x4 = h4 - y4
+
+ x5 = h5 - y5
+
+ x6 = h6 - y6
+
+ y6 *= scale
+
+ x2 += y0
+
+ x3 += y1
+
+ x4 += y2
+
+ x0 += y6
+
+ x5 += y3
+
+ x6 += y4
+
+ x2 += x3
+
+ x0 += x1
+
+ x4 += x5
+
+ x6 += y5
+
+ x2 += offset1
+ d1 = int64(math.Float64bits(x2))
+
+ x0 += offset0
+ d0 = int64(math.Float64bits(x0))
+
+ x4 += offset2
+ d2 = int64(math.Float64bits(x4))
+
+ x6 += offset3
+ d3 = int64(math.Float64bits(x6))
+
+ f0 = uint64(d0)
+
+ f1 = uint64(d1)
+ bits32 = math.MaxUint64
+
+ f2 = uint64(d2)
+ bits32 >>= 32
+
+ f3 = uint64(d3)
+ f = f0 >> 32
+
+ f0 &= bits32
+ f &= 255
+
+ f1 += f
+ g0 = f0 + 5
+
+ g = g0 >> 32
+ g0 &= bits32
+
+ f = f1 >> 32
+ f1 &= bits32
+
+ f &= 255
+ g1 = f1 + g
+
+ g = g1 >> 32
+ f2 += f
+
+ f = f2 >> 32
+ g1 &= bits32
+
+ f2 &= bits32
+ f &= 255
+
+ f3 += f
+ g2 = f2 + g
+
+ g = g2 >> 32
+ g2 &= bits32
+
+ f4 = f3 >> 32
+ f3 &= bits32
+
+ f4 &= 255
+ g3 = f3 + g
+
+ g = g3 >> 32
+ g3 &= bits32
+
+ g4 = f4 + g
+
+ g4 = g4 - 4
+ s00 = uint32(s[0])
+
+ f = uint64(int64(g4) >> 63)
+ s01 = uint32(s[1])
+
+ f0 &= f
+ g0 &^= f
+ s02 = uint32(s[2])
+
+ f1 &= f
+ f0 |= g0
+ s03 = uint32(s[3])
+
+ g1 &^= f
+ f2 &= f
+ s10 = uint32(s[4])
+
+ f3 &= f
+ g2 &^= f
+ s11 = uint32(s[5])
+
+ g3 &^= f
+ f1 |= g1
+ s12 = uint32(s[6])
+
+ f2 |= g2
+ f3 |= g3
+ s13 = uint32(s[7])
+
+ s01 <<= 8
+ f0 += uint64(s00)
+ s20 = uint32(s[8])
+
+ s02 <<= 16
+ f0 += uint64(s01)
+ s21 = uint32(s[9])
+
+ s03 <<= 24
+ f0 += uint64(s02)
+ s22 = uint32(s[10])
+
+ s11 <<= 8
+ f1 += uint64(s10)
+ s23 = uint32(s[11])
+
+ s12 <<= 16
+ f1 += uint64(s11)
+ s30 = uint32(s[12])
+
+ s13 <<= 24
+ f1 += uint64(s12)
+ s31 = uint32(s[13])
+
+ f0 += uint64(s03)
+ f1 += uint64(s13)
+ s32 = uint32(s[14])
+
+ s21 <<= 8
+ f2 += uint64(s20)
+ s33 = uint32(s[15])
+
+ s22 <<= 16
+ f2 += uint64(s21)
+
+ s23 <<= 24
+ f2 += uint64(s22)
+
+ s31 <<= 8
+ f3 += uint64(s30)
+
+ s32 <<= 16
+ f3 += uint64(s31)
+
+ s33 <<= 24
+ f3 += uint64(s32)
+
+ f2 += uint64(s23)
+ f3 += uint64(s33)
+
+ out[0] = byte(f0)
+ f0 >>= 8
+ out[1] = byte(f0)
+ f0 >>= 8
+ out[2] = byte(f0)
+ f0 >>= 8
+ out[3] = byte(f0)
+ f0 >>= 8
+ f1 += f0
+
+ out[4] = byte(f1)
+ f1 >>= 8
+ out[5] = byte(f1)
+ f1 >>= 8
+ out[6] = byte(f1)
+ f1 >>= 8
+ out[7] = byte(f1)
+ f1 >>= 8
+ f2 += f1
+
+ out[8] = byte(f2)
+ f2 >>= 8
+ out[9] = byte(f2)
+ f2 >>= 8
+ out[10] = byte(f2)
+ f2 >>= 8
+ out[11] = byte(f2)
+ f2 >>= 8
+ f3 += f2
+
+ out[12] = byte(f3)
+ f3 >>= 8
+ out[13] = byte(f3)
+ f3 >>= 8
+ out[14] = byte(f3)
+ f3 >>= 8
+ out[15] = byte(f3)
+}
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/LICENSE b/vendor/golang.org/x/crypto/salsa20/salsa/LICENSE
new file mode 100644
index 00000000..6a66aea5
--- /dev/null
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go b/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go
new file mode 100644
index 00000000..4c96147c
--- /dev/null
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go
@@ -0,0 +1,144 @@
+// Copyright 2012 The Go 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 salsa provides low-level access to functions in the Salsa family.
+package salsa // import "golang.org/x/crypto/salsa20/salsa"
+
+// Sigma is the Salsa20 constant for 256-bit keys.
+var Sigma = [16]byte{'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'}
+
+// HSalsa20 applies the HSalsa20 core function to a 16-byte input in, 32-byte
+// key k, and 16-byte constant c, and puts the result into the 32-byte array
+// out.
+func HSalsa20(out *[32]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
+ x0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24
+ x1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24
+ x2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24
+ x3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24
+ x4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24
+ x5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24
+ x6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
+ x7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
+ x8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
+ x9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
+ x10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24
+ x11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24
+ x12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24
+ x13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24
+ x14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24
+ x15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24
+
+ for i := 0; i < 20; i += 2 {
+ u := x0 + x12
+ x4 ^= u<<7 | u>>(32-7)
+ u = x4 + x0
+ x8 ^= u<<9 | u>>(32-9)
+ u = x8 + x4
+ x12 ^= u<<13 | u>>(32-13)
+ u = x12 + x8
+ x0 ^= u<<18 | u>>(32-18)
+
+ u = x5 + x1
+ x9 ^= u<<7 | u>>(32-7)
+ u = x9 + x5
+ x13 ^= u<<9 | u>>(32-9)
+ u = x13 + x9
+ x1 ^= u<<13 | u>>(32-13)
+ u = x1 + x13
+ x5 ^= u<<18 | u>>(32-18)
+
+ u = x10 + x6
+ x14 ^= u<<7 | u>>(32-7)
+ u = x14 + x10
+ x2 ^= u<<9 | u>>(32-9)
+ u = x2 + x14
+ x6 ^= u<<13 | u>>(32-13)
+ u = x6 + x2
+ x10 ^= u<<18 | u>>(32-18)
+
+ u = x15 + x11
+ x3 ^= u<<7 | u>>(32-7)
+ u = x3 + x15
+ x7 ^= u<<9 | u>>(32-9)
+ u = x7 + x3
+ x11 ^= u<<13 | u>>(32-13)
+ u = x11 + x7
+ x15 ^= u<<18 | u>>(32-18)
+
+ u = x0 + x3
+ x1 ^= u<<7 | u>>(32-7)
+ u = x1 + x0
+ x2 ^= u<<9 | u>>(32-9)
+ u = x2 + x1
+ x3 ^= u<<13 | u>>(32-13)
+ u = x3 + x2
+ x0 ^= u<<18 | u>>(32-18)
+
+ u = x5 + x4
+ x6 ^= u<<7 | u>>(32-7)
+ u = x6 + x5
+ x7 ^= u<<9 | u>>(32-9)
+ u = x7 + x6
+ x4 ^= u<<13 | u>>(32-13)
+ u = x4 + x7
+ x5 ^= u<<18 | u>>(32-18)
+
+ u = x10 + x9
+ x11 ^= u<<7 | u>>(32-7)
+ u = x11 + x10
+ x8 ^= u<<9 | u>>(32-9)
+ u = x8 + x11
+ x9 ^= u<<13 | u>>(32-13)
+ u = x9 + x8
+ x10 ^= u<<18 | u>>(32-18)
+
+ u = x15 + x14
+ x12 ^= u<<7 | u>>(32-7)
+ u = x12 + x15
+ x13 ^= u<<9 | u>>(32-9)
+ u = x13 + x12
+ x14 ^= u<<13 | u>>(32-13)
+ u = x14 + x13
+ x15 ^= u<<18 | u>>(32-18)
+ }
+ out[0] = byte(x0)
+ out[1] = byte(x0 >> 8)
+ out[2] = byte(x0 >> 16)
+ out[3] = byte(x0 >> 24)
+
+ out[4] = byte(x5)
+ out[5] = byte(x5 >> 8)
+ out[6] = byte(x5 >> 16)
+ out[7] = byte(x5 >> 24)
+
+ out[8] = byte(x10)
+ out[9] = byte(x10 >> 8)
+ out[10] = byte(x10 >> 16)
+ out[11] = byte(x10 >> 24)
+
+ out[12] = byte(x15)
+ out[13] = byte(x15 >> 8)
+ out[14] = byte(x15 >> 16)
+ out[15] = byte(x15 >> 24)
+
+ out[16] = byte(x6)
+ out[17] = byte(x6 >> 8)
+ out[18] = byte(x6 >> 16)
+ out[19] = byte(x6 >> 24)
+
+ out[20] = byte(x7)
+ out[21] = byte(x7 >> 8)
+ out[22] = byte(x7 >> 16)
+ out[23] = byte(x7 >> 24)
+
+ out[24] = byte(x8)
+ out[25] = byte(x8 >> 8)
+ out[26] = byte(x8 >> 16)
+ out[27] = byte(x8 >> 24)
+
+ out[28] = byte(x9)
+ out[29] = byte(x9 >> 8)
+ out[30] = byte(x9 >> 16)
+ out[31] = byte(x9 >> 24)
+}
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s b/vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s
new file mode 100644
index 00000000..6e1df963
--- /dev/null
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s
@@ -0,0 +1,902 @@
+// Copyright 2012 The Go 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 amd64,!appengine,!gccgo
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte)
+TEXT ·salsa2020XORKeyStream(SB),0,$512-40
+ MOVQ out+0(FP),DI
+ MOVQ in+8(FP),SI
+ MOVQ n+16(FP),DX
+ MOVQ nonce+24(FP),CX
+ MOVQ key+32(FP),R8
+
+ MOVQ SP,R11
+ MOVQ $31,R9
+ NOTQ R9
+ ANDQ R9,SP
+ ADDQ $32,SP
+
+ MOVQ R11,352(SP)
+ MOVQ R12,360(SP)
+ MOVQ R13,368(SP)
+ MOVQ R14,376(SP)
+ MOVQ R15,384(SP)
+ MOVQ BX,392(SP)
+ MOVQ BP,400(SP)
+ MOVQ DX,R9
+ MOVQ CX,DX
+ MOVQ R8,R10
+ CMPQ R9,$0
+ JBE DONE
+ START:
+ MOVL 20(R10),CX
+ MOVL 0(R10),R8
+ MOVL 0(DX),AX
+ MOVL 16(R10),R11
+ MOVL CX,0(SP)
+ MOVL R8, 4 (SP)
+ MOVL AX, 8 (SP)
+ MOVL R11, 12 (SP)
+ MOVL 8(DX),CX
+ MOVL 24(R10),R8
+ MOVL 4(R10),AX
+ MOVL 4(DX),R11
+ MOVL CX,16(SP)
+ MOVL R8, 20 (SP)
+ MOVL AX, 24 (SP)
+ MOVL R11, 28 (SP)
+ MOVL 12(DX),CX
+ MOVL 12(R10),DX
+ MOVL 28(R10),R8
+ MOVL 8(R10),AX
+ MOVL DX,32(SP)
+ MOVL CX, 36 (SP)
+ MOVL R8, 40 (SP)
+ MOVL AX, 44 (SP)
+ MOVQ $1634760805,DX
+ MOVQ $857760878,CX
+ MOVQ $2036477234,R8
+ MOVQ $1797285236,AX
+ MOVL DX,48(SP)
+ MOVL CX, 52 (SP)
+ MOVL R8, 56 (SP)
+ MOVL AX, 60 (SP)
+ CMPQ R9,$256
+ JB BYTESBETWEEN1AND255
+ MOVOA 48(SP),X0
+ PSHUFL $0X55,X0,X1
+ PSHUFL $0XAA,X0,X2
+ PSHUFL $0XFF,X0,X3
+ PSHUFL $0X00,X0,X0
+ MOVOA X1,64(SP)
+ MOVOA X2,80(SP)
+ MOVOA X3,96(SP)
+ MOVOA X0,112(SP)
+ MOVOA 0(SP),X0
+ PSHUFL $0XAA,X0,X1
+ PSHUFL $0XFF,X0,X2
+ PSHUFL $0X00,X0,X3
+ PSHUFL $0X55,X0,X0
+ MOVOA X1,128(SP)
+ MOVOA X2,144(SP)
+ MOVOA X3,160(SP)
+ MOVOA X0,176(SP)
+ MOVOA 16(SP),X0
+ PSHUFL $0XFF,X0,X1
+ PSHUFL $0X55,X0,X2
+ PSHUFL $0XAA,X0,X0
+ MOVOA X1,192(SP)
+ MOVOA X2,208(SP)
+ MOVOA X0,224(SP)
+ MOVOA 32(SP),X0
+ PSHUFL $0X00,X0,X1
+ PSHUFL $0XAA,X0,X2
+ PSHUFL $0XFF,X0,X0
+ MOVOA X1,240(SP)
+ MOVOA X2,256(SP)
+ MOVOA X0,272(SP)
+ BYTESATLEAST256:
+ MOVL 16(SP),DX
+ MOVL 36 (SP),CX
+ MOVL DX,288(SP)
+ MOVL CX,304(SP)
+ ADDQ $1,DX
+ SHLQ $32,CX
+ ADDQ CX,DX
+ MOVQ DX,CX
+ SHRQ $32,CX
+ MOVL DX, 292 (SP)
+ MOVL CX, 308 (SP)
+ ADDQ $1,DX
+ SHLQ $32,CX
+ ADDQ CX,DX
+ MOVQ DX,CX
+ SHRQ $32,CX
+ MOVL DX, 296 (SP)
+ MOVL CX, 312 (SP)
+ ADDQ $1,DX
+ SHLQ $32,CX
+ ADDQ CX,DX
+ MOVQ DX,CX
+ SHRQ $32,CX
+ MOVL DX, 300 (SP)
+ MOVL CX, 316 (SP)
+ ADDQ $1,DX
+ SHLQ $32,CX
+ ADDQ CX,DX
+ MOVQ DX,CX
+ SHRQ $32,CX
+ MOVL DX,16(SP)
+ MOVL CX, 36 (SP)
+ MOVQ R9,408(SP)
+ MOVQ $20,DX
+ MOVOA 64(SP),X0
+ MOVOA 80(SP),X1
+ MOVOA 96(SP),X2
+ MOVOA 256(SP),X3
+ MOVOA 272(SP),X4
+ MOVOA 128(SP),X5
+ MOVOA 144(SP),X6
+ MOVOA 176(SP),X7
+ MOVOA 192(SP),X8
+ MOVOA 208(SP),X9
+ MOVOA 224(SP),X10
+ MOVOA 304(SP),X11
+ MOVOA 112(SP),X12
+ MOVOA 160(SP),X13
+ MOVOA 240(SP),X14
+ MOVOA 288(SP),X15
+ MAINLOOP1:
+ MOVOA X1,320(SP)
+ MOVOA X2,336(SP)
+ MOVOA X13,X1
+ PADDL X12,X1
+ MOVOA X1,X2
+ PSLLL $7,X1
+ PXOR X1,X14
+ PSRLL $25,X2
+ PXOR X2,X14
+ MOVOA X7,X1
+ PADDL X0,X1
+ MOVOA X1,X2
+ PSLLL $7,X1
+ PXOR X1,X11
+ PSRLL $25,X2
+ PXOR X2,X11
+ MOVOA X12,X1
+ PADDL X14,X1
+ MOVOA X1,X2
+ PSLLL $9,X1
+ PXOR X1,X15
+ PSRLL $23,X2
+ PXOR X2,X15
+ MOVOA X0,X1
+ PADDL X11,X1
+ MOVOA X1,X2
+ PSLLL $9,X1
+ PXOR X1,X9
+ PSRLL $23,X2
+ PXOR X2,X9
+ MOVOA X14,X1
+ PADDL X15,X1
+ MOVOA X1,X2
+ PSLLL $13,X1
+ PXOR X1,X13
+ PSRLL $19,X2
+ PXOR X2,X13
+ MOVOA X11,X1
+ PADDL X9,X1
+ MOVOA X1,X2
+ PSLLL $13,X1
+ PXOR X1,X7
+ PSRLL $19,X2
+ PXOR X2,X7
+ MOVOA X15,X1
+ PADDL X13,X1
+ MOVOA X1,X2
+ PSLLL $18,X1
+ PXOR X1,X12
+ PSRLL $14,X2
+ PXOR X2,X12
+ MOVOA 320(SP),X1
+ MOVOA X12,320(SP)
+ MOVOA X9,X2
+ PADDL X7,X2
+ MOVOA X2,X12
+ PSLLL $18,X2
+ PXOR X2,X0
+ PSRLL $14,X12
+ PXOR X12,X0
+ MOVOA X5,X2
+ PADDL X1,X2
+ MOVOA X2,X12
+ PSLLL $7,X2
+ PXOR X2,X3
+ PSRLL $25,X12
+ PXOR X12,X3
+ MOVOA 336(SP),X2
+ MOVOA X0,336(SP)
+ MOVOA X6,X0
+ PADDL X2,X0
+ MOVOA X0,X12
+ PSLLL $7,X0
+ PXOR X0,X4
+ PSRLL $25,X12
+ PXOR X12,X4
+ MOVOA X1,X0
+ PADDL X3,X0
+ MOVOA X0,X12
+ PSLLL $9,X0
+ PXOR X0,X10
+ PSRLL $23,X12
+ PXOR X12,X10
+ MOVOA X2,X0
+ PADDL X4,X0
+ MOVOA X0,X12
+ PSLLL $9,X0
+ PXOR X0,X8
+ PSRLL $23,X12
+ PXOR X12,X8
+ MOVOA X3,X0
+ PADDL X10,X0
+ MOVOA X0,X12
+ PSLLL $13,X0
+ PXOR X0,X5
+ PSRLL $19,X12
+ PXOR X12,X5
+ MOVOA X4,X0
+ PADDL X8,X0
+ MOVOA X0,X12
+ PSLLL $13,X0
+ PXOR X0,X6
+ PSRLL $19,X12
+ PXOR X12,X6
+ MOVOA X10,X0
+ PADDL X5,X0
+ MOVOA X0,X12
+ PSLLL $18,X0
+ PXOR X0,X1
+ PSRLL $14,X12
+ PXOR X12,X1
+ MOVOA 320(SP),X0
+ MOVOA X1,320(SP)
+ MOVOA X4,X1
+ PADDL X0,X1
+ MOVOA X1,X12
+ PSLLL $7,X1
+ PXOR X1,X7
+ PSRLL $25,X12
+ PXOR X12,X7
+ MOVOA X8,X1
+ PADDL X6,X1
+ MOVOA X1,X12
+ PSLLL $18,X1
+ PXOR X1,X2
+ PSRLL $14,X12
+ PXOR X12,X2
+ MOVOA 336(SP),X12
+ MOVOA X2,336(SP)
+ MOVOA X14,X1
+ PADDL X12,X1
+ MOVOA X1,X2
+ PSLLL $7,X1
+ PXOR X1,X5
+ PSRLL $25,X2
+ PXOR X2,X5
+ MOVOA X0,X1
+ PADDL X7,X1
+ MOVOA X1,X2
+ PSLLL $9,X1
+ PXOR X1,X10
+ PSRLL $23,X2
+ PXOR X2,X10
+ MOVOA X12,X1
+ PADDL X5,X1
+ MOVOA X1,X2
+ PSLLL $9,X1
+ PXOR X1,X8
+ PSRLL $23,X2
+ PXOR X2,X8
+ MOVOA X7,X1
+ PADDL X10,X1
+ MOVOA X1,X2
+ PSLLL $13,X1
+ PXOR X1,X4
+ PSRLL $19,X2
+ PXOR X2,X4
+ MOVOA X5,X1
+ PADDL X8,X1
+ MOVOA X1,X2
+ PSLLL $13,X1
+ PXOR X1,X14
+ PSRLL $19,X2
+ PXOR X2,X14
+ MOVOA X10,X1
+ PADDL X4,X1
+ MOVOA X1,X2
+ PSLLL $18,X1
+ PXOR X1,X0
+ PSRLL $14,X2
+ PXOR X2,X0
+ MOVOA 320(SP),X1
+ MOVOA X0,320(SP)
+ MOVOA X8,X0
+ PADDL X14,X0
+ MOVOA X0,X2
+ PSLLL $18,X0
+ PXOR X0,X12
+ PSRLL $14,X2
+ PXOR X2,X12
+ MOVOA X11,X0
+ PADDL X1,X0
+ MOVOA X0,X2
+ PSLLL $7,X0
+ PXOR X0,X6
+ PSRLL $25,X2
+ PXOR X2,X6
+ MOVOA 336(SP),X2
+ MOVOA X12,336(SP)
+ MOVOA X3,X0
+ PADDL X2,X0
+ MOVOA X0,X12
+ PSLLL $7,X0
+ PXOR X0,X13
+ PSRLL $25,X12
+ PXOR X12,X13
+ MOVOA X1,X0
+ PADDL X6,X0
+ MOVOA X0,X12
+ PSLLL $9,X0
+ PXOR X0,X15
+ PSRLL $23,X12
+ PXOR X12,X15
+ MOVOA X2,X0
+ PADDL X13,X0
+ MOVOA X0,X12
+ PSLLL $9,X0
+ PXOR X0,X9
+ PSRLL $23,X12
+ PXOR X12,X9
+ MOVOA X6,X0
+ PADDL X15,X0
+ MOVOA X0,X12
+ PSLLL $13,X0
+ PXOR X0,X11
+ PSRLL $19,X12
+ PXOR X12,X11
+ MOVOA X13,X0
+ PADDL X9,X0
+ MOVOA X0,X12
+ PSLLL $13,X0
+ PXOR X0,X3
+ PSRLL $19,X12
+ PXOR X12,X3
+ MOVOA X15,X0
+ PADDL X11,X0
+ MOVOA X0,X12
+ PSLLL $18,X0
+ PXOR X0,X1
+ PSRLL $14,X12
+ PXOR X12,X1
+ MOVOA X9,X0
+ PADDL X3,X0
+ MOVOA X0,X12
+ PSLLL $18,X0
+ PXOR X0,X2
+ PSRLL $14,X12
+ PXOR X12,X2
+ MOVOA 320(SP),X12
+ MOVOA 336(SP),X0
+ SUBQ $2,DX
+ JA MAINLOOP1
+ PADDL 112(SP),X12
+ PADDL 176(SP),X7
+ PADDL 224(SP),X10
+ PADDL 272(SP),X4
+ MOVD X12,DX
+ MOVD X7,CX
+ MOVD X10,R8
+ MOVD X4,R9
+ PSHUFL $0X39,X12,X12
+ PSHUFL $0X39,X7,X7
+ PSHUFL $0X39,X10,X10
+ PSHUFL $0X39,X4,X4
+ XORL 0(SI),DX
+ XORL 4(SI),CX
+ XORL 8(SI),R8
+ XORL 12(SI),R9
+ MOVL DX,0(DI)
+ MOVL CX,4(DI)
+ MOVL R8,8(DI)
+ MOVL R9,12(DI)
+ MOVD X12,DX
+ MOVD X7,CX
+ MOVD X10,R8
+ MOVD X4,R9
+ PSHUFL $0X39,X12,X12
+ PSHUFL $0X39,X7,X7
+ PSHUFL $0X39,X10,X10
+ PSHUFL $0X39,X4,X4
+ XORL 64(SI),DX
+ XORL 68(SI),CX
+ XORL 72(SI),R8
+ XORL 76(SI),R9
+ MOVL DX,64(DI)
+ MOVL CX,68(DI)
+ MOVL R8,72(DI)
+ MOVL R9,76(DI)
+ MOVD X12,DX
+ MOVD X7,CX
+ MOVD X10,R8
+ MOVD X4,R9
+ PSHUFL $0X39,X12,X12
+ PSHUFL $0X39,X7,X7
+ PSHUFL $0X39,X10,X10
+ PSHUFL $0X39,X4,X4
+ XORL 128(SI),DX
+ XORL 132(SI),CX
+ XORL 136(SI),R8
+ XORL 140(SI),R9
+ MOVL DX,128(DI)
+ MOVL CX,132(DI)
+ MOVL R8,136(DI)
+ MOVL R9,140(DI)
+ MOVD X12,DX
+ MOVD X7,CX
+ MOVD X10,R8
+ MOVD X4,R9
+ XORL 192(SI),DX
+ XORL 196(SI),CX
+ XORL 200(SI),R8
+ XORL 204(SI),R9
+ MOVL DX,192(DI)
+ MOVL CX,196(DI)
+ MOVL R8,200(DI)
+ MOVL R9,204(DI)
+ PADDL 240(SP),X14
+ PADDL 64(SP),X0
+ PADDL 128(SP),X5
+ PADDL 192(SP),X8
+ MOVD X14,DX
+ MOVD X0,CX
+ MOVD X5,R8
+ MOVD X8,R9
+ PSHUFL $0X39,X14,X14
+ PSHUFL $0X39,X0,X0
+ PSHUFL $0X39,X5,X5
+ PSHUFL $0X39,X8,X8
+ XORL 16(SI),DX
+ XORL 20(SI),CX
+ XORL 24(SI),R8
+ XORL 28(SI),R9
+ MOVL DX,16(DI)
+ MOVL CX,20(DI)
+ MOVL R8,24(DI)
+ MOVL R9,28(DI)
+ MOVD X14,DX
+ MOVD X0,CX
+ MOVD X5,R8
+ MOVD X8,R9
+ PSHUFL $0X39,X14,X14
+ PSHUFL $0X39,X0,X0
+ PSHUFL $0X39,X5,X5
+ PSHUFL $0X39,X8,X8
+ XORL 80(SI),DX
+ XORL 84(SI),CX
+ XORL 88(SI),R8
+ XORL 92(SI),R9
+ MOVL DX,80(DI)
+ MOVL CX,84(DI)
+ MOVL R8,88(DI)
+ MOVL R9,92(DI)
+ MOVD X14,DX
+ MOVD X0,CX
+ MOVD X5,R8
+ MOVD X8,R9
+ PSHUFL $0X39,X14,X14
+ PSHUFL $0X39,X0,X0
+ PSHUFL $0X39,X5,X5
+ PSHUFL $0X39,X8,X8
+ XORL 144(SI),DX
+ XORL 148(SI),CX
+ XORL 152(SI),R8
+ XORL 156(SI),R9
+ MOVL DX,144(DI)
+ MOVL CX,148(DI)
+ MOVL R8,152(DI)
+ MOVL R9,156(DI)
+ MOVD X14,DX
+ MOVD X0,CX
+ MOVD X5,R8
+ MOVD X8,R9
+ XORL 208(SI),DX
+ XORL 212(SI),CX
+ XORL 216(SI),R8
+ XORL 220(SI),R9
+ MOVL DX,208(DI)
+ MOVL CX,212(DI)
+ MOVL R8,216(DI)
+ MOVL R9,220(DI)
+ PADDL 288(SP),X15
+ PADDL 304(SP),X11
+ PADDL 80(SP),X1
+ PADDL 144(SP),X6
+ MOVD X15,DX
+ MOVD X11,CX
+ MOVD X1,R8
+ MOVD X6,R9
+ PSHUFL $0X39,X15,X15
+ PSHUFL $0X39,X11,X11
+ PSHUFL $0X39,X1,X1
+ PSHUFL $0X39,X6,X6
+ XORL 32(SI),DX
+ XORL 36(SI),CX
+ XORL 40(SI),R8
+ XORL 44(SI),R9
+ MOVL DX,32(DI)
+ MOVL CX,36(DI)
+ MOVL R8,40(DI)
+ MOVL R9,44(DI)
+ MOVD X15,DX
+ MOVD X11,CX
+ MOVD X1,R8
+ MOVD X6,R9
+ PSHUFL $0X39,X15,X15
+ PSHUFL $0X39,X11,X11
+ PSHUFL $0X39,X1,X1
+ PSHUFL $0X39,X6,X6
+ XORL 96(SI),DX
+ XORL 100(SI),CX
+ XORL 104(SI),R8
+ XORL 108(SI),R9
+ MOVL DX,96(DI)
+ MOVL CX,100(DI)
+ MOVL R8,104(DI)
+ MOVL R9,108(DI)
+ MOVD X15,DX
+ MOVD X11,CX
+ MOVD X1,R8
+ MOVD X6,R9
+ PSHUFL $0X39,X15,X15
+ PSHUFL $0X39,X11,X11
+ PSHUFL $0X39,X1,X1
+ PSHUFL $0X39,X6,X6
+ XORL 160(SI),DX
+ XORL 164(SI),CX
+ XORL 168(SI),R8
+ XORL 172(SI),R9
+ MOVL DX,160(DI)
+ MOVL CX,164(DI)
+ MOVL R8,168(DI)
+ MOVL R9,172(DI)
+ MOVD X15,DX
+ MOVD X11,CX
+ MOVD X1,R8
+ MOVD X6,R9
+ XORL 224(SI),DX
+ XORL 228(SI),CX
+ XORL 232(SI),R8
+ XORL 236(SI),R9
+ MOVL DX,224(DI)
+ MOVL CX,228(DI)
+ MOVL R8,232(DI)
+ MOVL R9,236(DI)
+ PADDL 160(SP),X13
+ PADDL 208(SP),X9
+ PADDL 256(SP),X3
+ PADDL 96(SP),X2
+ MOVD X13,DX
+ MOVD X9,CX
+ MOVD X3,R8
+ MOVD X2,R9
+ PSHUFL $0X39,X13,X13
+ PSHUFL $0X39,X9,X9
+ PSHUFL $0X39,X3,X3
+ PSHUFL $0X39,X2,X2
+ XORL 48(SI),DX
+ XORL 52(SI),CX
+ XORL 56(SI),R8
+ XORL 60(SI),R9
+ MOVL DX,48(DI)
+ MOVL CX,52(DI)
+ MOVL R8,56(DI)
+ MOVL R9,60(DI)
+ MOVD X13,DX
+ MOVD X9,CX
+ MOVD X3,R8
+ MOVD X2,R9
+ PSHUFL $0X39,X13,X13
+ PSHUFL $0X39,X9,X9
+ PSHUFL $0X39,X3,X3
+ PSHUFL $0X39,X2,X2
+ XORL 112(SI),DX
+ XORL 116(SI),CX
+ XORL 120(SI),R8
+ XORL 124(SI),R9
+ MOVL DX,112(DI)
+ MOVL CX,116(DI)
+ MOVL R8,120(DI)
+ MOVL R9,124(DI)
+ MOVD X13,DX
+ MOVD X9,CX
+ MOVD X3,R8
+ MOVD X2,R9
+ PSHUFL $0X39,X13,X13
+ PSHUFL $0X39,X9,X9
+ PSHUFL $0X39,X3,X3
+ PSHUFL $0X39,X2,X2
+ XORL 176(SI),DX
+ XORL 180(SI),CX
+ XORL 184(SI),R8
+ XORL 188(SI),R9
+ MOVL DX,176(DI)
+ MOVL CX,180(DI)
+ MOVL R8,184(DI)
+ MOVL R9,188(DI)
+ MOVD X13,DX
+ MOVD X9,CX
+ MOVD X3,R8
+ MOVD X2,R9
+ XORL 240(SI),DX
+ XORL 244(SI),CX
+ XORL 248(SI),R8
+ XORL 252(SI),R9
+ MOVL DX,240(DI)
+ MOVL CX,244(DI)
+ MOVL R8,248(DI)
+ MOVL R9,252(DI)
+ MOVQ 408(SP),R9
+ SUBQ $256,R9
+ ADDQ $256,SI
+ ADDQ $256,DI
+ CMPQ R9,$256
+ JAE BYTESATLEAST256
+ CMPQ R9,$0
+ JBE DONE
+ BYTESBETWEEN1AND255:
+ CMPQ R9,$64
+ JAE NOCOPY
+ MOVQ DI,DX
+ LEAQ 416(SP),DI
+ MOVQ R9,CX
+ REP; MOVSB
+ LEAQ 416(SP),DI
+ LEAQ 416(SP),SI
+ NOCOPY:
+ MOVQ R9,408(SP)
+ MOVOA 48(SP),X0
+ MOVOA 0(SP),X1
+ MOVOA 16(SP),X2
+ MOVOA 32(SP),X3
+ MOVOA X1,X4
+ MOVQ $20,CX
+ MAINLOOP2:
+ PADDL X0,X4
+ MOVOA X0,X5
+ MOVOA X4,X6
+ PSLLL $7,X4
+ PSRLL $25,X6
+ PXOR X4,X3
+ PXOR X6,X3
+ PADDL X3,X5
+ MOVOA X3,X4
+ MOVOA X5,X6
+ PSLLL $9,X5
+ PSRLL $23,X6
+ PXOR X5,X2
+ PSHUFL $0X93,X3,X3
+ PXOR X6,X2
+ PADDL X2,X4
+ MOVOA X2,X5
+ MOVOA X4,X6
+ PSLLL $13,X4
+ PSRLL $19,X6
+ PXOR X4,X1
+ PSHUFL $0X4E,X2,X2
+ PXOR X6,X1
+ PADDL X1,X5
+ MOVOA X3,X4
+ MOVOA X5,X6
+ PSLLL $18,X5
+ PSRLL $14,X6
+ PXOR X5,X0
+ PSHUFL $0X39,X1,X1
+ PXOR X6,X0
+ PADDL X0,X4
+ MOVOA X0,X5
+ MOVOA X4,X6
+ PSLLL $7,X4
+ PSRLL $25,X6
+ PXOR X4,X1
+ PXOR X6,X1
+ PADDL X1,X5
+ MOVOA X1,X4
+ MOVOA X5,X6
+ PSLLL $9,X5
+ PSRLL $23,X6
+ PXOR X5,X2
+ PSHUFL $0X93,X1,X1
+ PXOR X6,X2
+ PADDL X2,X4
+ MOVOA X2,X5
+ MOVOA X4,X6
+ PSLLL $13,X4
+ PSRLL $19,X6
+ PXOR X4,X3
+ PSHUFL $0X4E,X2,X2
+ PXOR X6,X3
+ PADDL X3,X5
+ MOVOA X1,X4
+ MOVOA X5,X6
+ PSLLL $18,X5
+ PSRLL $14,X6
+ PXOR X5,X0
+ PSHUFL $0X39,X3,X3
+ PXOR X6,X0
+ PADDL X0,X4
+ MOVOA X0,X5
+ MOVOA X4,X6
+ PSLLL $7,X4
+ PSRLL $25,X6
+ PXOR X4,X3
+ PXOR X6,X3
+ PADDL X3,X5
+ MOVOA X3,X4
+ MOVOA X5,X6
+ PSLLL $9,X5
+ PSRLL $23,X6
+ PXOR X5,X2
+ PSHUFL $0X93,X3,X3
+ PXOR X6,X2
+ PADDL X2,X4
+ MOVOA X2,X5
+ MOVOA X4,X6
+ PSLLL $13,X4
+ PSRLL $19,X6
+ PXOR X4,X1
+ PSHUFL $0X4E,X2,X2
+ PXOR X6,X1
+ PADDL X1,X5
+ MOVOA X3,X4
+ MOVOA X5,X6
+ PSLLL $18,X5
+ PSRLL $14,X6
+ PXOR X5,X0
+ PSHUFL $0X39,X1,X1
+ PXOR X6,X0
+ PADDL X0,X4
+ MOVOA X0,X5
+ MOVOA X4,X6
+ PSLLL $7,X4
+ PSRLL $25,X6
+ PXOR X4,X1
+ PXOR X6,X1
+ PADDL X1,X5
+ MOVOA X1,X4
+ MOVOA X5,X6
+ PSLLL $9,X5
+ PSRLL $23,X6
+ PXOR X5,X2
+ PSHUFL $0X93,X1,X1
+ PXOR X6,X2
+ PADDL X2,X4
+ MOVOA X2,X5
+ MOVOA X4,X6
+ PSLLL $13,X4
+ PSRLL $19,X6
+ PXOR X4,X3
+ PSHUFL $0X4E,X2,X2
+ PXOR X6,X3
+ SUBQ $4,CX
+ PADDL X3,X5
+ MOVOA X1,X4
+ MOVOA X5,X6
+ PSLLL $18,X5
+ PXOR X7,X7
+ PSRLL $14,X6
+ PXOR X5,X0
+ PSHUFL $0X39,X3,X3
+ PXOR X6,X0
+ JA MAINLOOP2
+ PADDL 48(SP),X0
+ PADDL 0(SP),X1
+ PADDL 16(SP),X2
+ PADDL 32(SP),X3
+ MOVD X0,CX
+ MOVD X1,R8
+ MOVD X2,R9
+ MOVD X3,AX
+ PSHUFL $0X39,X0,X0
+ PSHUFL $0X39,X1,X1
+ PSHUFL $0X39,X2,X2
+ PSHUFL $0X39,X3,X3
+ XORL 0(SI),CX
+ XORL 48(SI),R8
+ XORL 32(SI),R9
+ XORL 16(SI),AX
+ MOVL CX,0(DI)
+ MOVL R8,48(DI)
+ MOVL R9,32(DI)
+ MOVL AX,16(DI)
+ MOVD X0,CX
+ MOVD X1,R8
+ MOVD X2,R9
+ MOVD X3,AX
+ PSHUFL $0X39,X0,X0
+ PSHUFL $0X39,X1,X1
+ PSHUFL $0X39,X2,X2
+ PSHUFL $0X39,X3,X3
+ XORL 20(SI),CX
+ XORL 4(SI),R8
+ XORL 52(SI),R9
+ XORL 36(SI),AX
+ MOVL CX,20(DI)
+ MOVL R8,4(DI)
+ MOVL R9,52(DI)
+ MOVL AX,36(DI)
+ MOVD X0,CX
+ MOVD X1,R8
+ MOVD X2,R9
+ MOVD X3,AX
+ PSHUFL $0X39,X0,X0
+ PSHUFL $0X39,X1,X1
+ PSHUFL $0X39,X2,X2
+ PSHUFL $0X39,X3,X3
+ XORL 40(SI),CX
+ XORL 24(SI),R8
+ XORL 8(SI),R9
+ XORL 56(SI),AX
+ MOVL CX,40(DI)
+ MOVL R8,24(DI)
+ MOVL R9,8(DI)
+ MOVL AX,56(DI)
+ MOVD X0,CX
+ MOVD X1,R8
+ MOVD X2,R9
+ MOVD X3,AX
+ XORL 60(SI),CX
+ XORL 44(SI),R8
+ XORL 28(SI),R9
+ XORL 12(SI),AX
+ MOVL CX,60(DI)
+ MOVL R8,44(DI)
+ MOVL R9,28(DI)
+ MOVL AX,12(DI)
+ MOVQ 408(SP),R9
+ MOVL 16(SP),CX
+ MOVL 36 (SP),R8
+ ADDQ $1,CX
+ SHLQ $32,R8
+ ADDQ R8,CX
+ MOVQ CX,R8
+ SHRQ $32,R8
+ MOVL CX,16(SP)
+ MOVL R8, 36 (SP)
+ CMPQ R9,$64
+ JA BYTESATLEAST65
+ JAE BYTESATLEAST64
+ MOVQ DI,SI
+ MOVQ DX,DI
+ MOVQ R9,CX
+ REP; MOVSB
+ BYTESATLEAST64:
+ DONE:
+ MOVQ 352(SP),R11
+ MOVQ 360(SP),R12
+ MOVQ 368(SP),R13
+ MOVQ 376(SP),R14
+ MOVQ 384(SP),R15
+ MOVQ 392(SP),BX
+ MOVQ 400(SP),BP
+ MOVQ R11,SP
+ RET
+ BYTESATLEAST65:
+ SUBQ $64,R9
+ ADDQ $64,DI
+ ADDQ $64,SI
+ JMP BYTESBETWEEN1AND255
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go
new file mode 100644
index 00000000..9bfc0927
--- /dev/null
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go
@@ -0,0 +1,199 @@
+// Copyright 2012 The Go 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 salsa
+
+// Core208 applies the Salsa20/8 core function to the 64-byte array in and puts
+// the result into the 64-byte array out. The input and output may be the same array.
+func Core208(out *[64]byte, in *[64]byte) {
+ j0 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
+ j1 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
+ j2 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
+ j3 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
+ j4 := uint32(in[16]) | uint32(in[17])<<8 | uint32(in[18])<<16 | uint32(in[19])<<24
+ j5 := uint32(in[20]) | uint32(in[21])<<8 | uint32(in[22])<<16 | uint32(in[23])<<24
+ j6 := uint32(in[24]) | uint32(in[25])<<8 | uint32(in[26])<<16 | uint32(in[27])<<24
+ j7 := uint32(in[28]) | uint32(in[29])<<8 | uint32(in[30])<<16 | uint32(in[31])<<24
+ j8 := uint32(in[32]) | uint32(in[33])<<8 | uint32(in[34])<<16 | uint32(in[35])<<24
+ j9 := uint32(in[36]) | uint32(in[37])<<8 | uint32(in[38])<<16 | uint32(in[39])<<24
+ j10 := uint32(in[40]) | uint32(in[41])<<8 | uint32(in[42])<<16 | uint32(in[43])<<24
+ j11 := uint32(in[44]) | uint32(in[45])<<8 | uint32(in[46])<<16 | uint32(in[47])<<24
+ j12 := uint32(in[48]) | uint32(in[49])<<8 | uint32(in[50])<<16 | uint32(in[51])<<24
+ j13 := uint32(in[52]) | uint32(in[53])<<8 | uint32(in[54])<<16 | uint32(in[55])<<24
+ j14 := uint32(in[56]) | uint32(in[57])<<8 | uint32(in[58])<<16 | uint32(in[59])<<24
+ j15 := uint32(in[60]) | uint32(in[61])<<8 | uint32(in[62])<<16 | uint32(in[63])<<24
+
+ x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8
+ x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15
+
+ for i := 0; i < 8; i += 2 {
+ u := x0 + x12
+ x4 ^= u<<7 | u>>(32-7)
+ u = x4 + x0
+ x8 ^= u<<9 | u>>(32-9)
+ u = x8 + x4
+ x12 ^= u<<13 | u>>(32-13)
+ u = x12 + x8
+ x0 ^= u<<18 | u>>(32-18)
+
+ u = x5 + x1
+ x9 ^= u<<7 | u>>(32-7)
+ u = x9 + x5
+ x13 ^= u<<9 | u>>(32-9)
+ u = x13 + x9
+ x1 ^= u<<13 | u>>(32-13)
+ u = x1 + x13
+ x5 ^= u<<18 | u>>(32-18)
+
+ u = x10 + x6
+ x14 ^= u<<7 | u>>(32-7)
+ u = x14 + x10
+ x2 ^= u<<9 | u>>(32-9)
+ u = x2 + x14
+ x6 ^= u<<13 | u>>(32-13)
+ u = x6 + x2
+ x10 ^= u<<18 | u>>(32-18)
+
+ u = x15 + x11
+ x3 ^= u<<7 | u>>(32-7)
+ u = x3 + x15
+ x7 ^= u<<9 | u>>(32-9)
+ u = x7 + x3
+ x11 ^= u<<13 | u>>(32-13)
+ u = x11 + x7
+ x15 ^= u<<18 | u>>(32-18)
+
+ u = x0 + x3
+ x1 ^= u<<7 | u>>(32-7)
+ u = x1 + x0
+ x2 ^= u<<9 | u>>(32-9)
+ u = x2 + x1
+ x3 ^= u<<13 | u>>(32-13)
+ u = x3 + x2
+ x0 ^= u<<18 | u>>(32-18)
+
+ u = x5 + x4
+ x6 ^= u<<7 | u>>(32-7)
+ u = x6 + x5
+ x7 ^= u<<9 | u>>(32-9)
+ u = x7 + x6
+ x4 ^= u<<13 | u>>(32-13)
+ u = x4 + x7
+ x5 ^= u<<18 | u>>(32-18)
+
+ u = x10 + x9
+ x11 ^= u<<7 | u>>(32-7)
+ u = x11 + x10
+ x8 ^= u<<9 | u>>(32-9)
+ u = x8 + x11
+ x9 ^= u<<13 | u>>(32-13)
+ u = x9 + x8
+ x10 ^= u<<18 | u>>(32-18)
+
+ u = x15 + x14
+ x12 ^= u<<7 | u>>(32-7)
+ u = x12 + x15
+ x13 ^= u<<9 | u>>(32-9)
+ u = x13 + x12
+ x14 ^= u<<13 | u>>(32-13)
+ u = x14 + x13
+ x15 ^= u<<18 | u>>(32-18)
+ }
+ x0 += j0
+ x1 += j1
+ x2 += j2
+ x3 += j3
+ x4 += j4
+ x5 += j5
+ x6 += j6
+ x7 += j7
+ x8 += j8
+ x9 += j9
+ x10 += j10
+ x11 += j11
+ x12 += j12
+ x13 += j13
+ x14 += j14
+ x15 += j15
+
+ out[0] = byte(x0)
+ out[1] = byte(x0 >> 8)
+ out[2] = byte(x0 >> 16)
+ out[3] = byte(x0 >> 24)
+
+ out[4] = byte(x1)
+ out[5] = byte(x1 >> 8)
+ out[6] = byte(x1 >> 16)
+ out[7] = byte(x1 >> 24)
+
+ out[8] = byte(x2)
+ out[9] = byte(x2 >> 8)
+ out[10] = byte(x2 >> 16)
+ out[11] = byte(x2 >> 24)
+
+ out[12] = byte(x3)
+ out[13] = byte(x3 >> 8)
+ out[14] = byte(x3 >> 16)
+ out[15] = byte(x3 >> 24)
+
+ out[16] = byte(x4)
+ out[17] = byte(x4 >> 8)
+ out[18] = byte(x4 >> 16)
+ out[19] = byte(x4 >> 24)
+
+ out[20] = byte(x5)
+ out[21] = byte(x5 >> 8)
+ out[22] = byte(x5 >> 16)
+ out[23] = byte(x5 >> 24)
+
+ out[24] = byte(x6)
+ out[25] = byte(x6 >> 8)
+ out[26] = byte(x6 >> 16)
+ out[27] = byte(x6 >> 24)
+
+ out[28] = byte(x7)
+ out[29] = byte(x7 >> 8)
+ out[30] = byte(x7 >> 16)
+ out[31] = byte(x7 >> 24)
+
+ out[32] = byte(x8)
+ out[33] = byte(x8 >> 8)
+ out[34] = byte(x8 >> 16)
+ out[35] = byte(x8 >> 24)
+
+ out[36] = byte(x9)
+ out[37] = byte(x9 >> 8)
+ out[38] = byte(x9 >> 16)
+ out[39] = byte(x9 >> 24)
+
+ out[40] = byte(x10)
+ out[41] = byte(x10 >> 8)
+ out[42] = byte(x10 >> 16)
+ out[43] = byte(x10 >> 24)
+
+ out[44] = byte(x11)
+ out[45] = byte(x11 >> 8)
+ out[46] = byte(x11 >> 16)
+ out[47] = byte(x11 >> 24)
+
+ out[48] = byte(x12)
+ out[49] = byte(x12 >> 8)
+ out[50] = byte(x12 >> 16)
+ out[51] = byte(x12 >> 24)
+
+ out[52] = byte(x13)
+ out[53] = byte(x13 >> 8)
+ out[54] = byte(x13 >> 16)
+ out[55] = byte(x13 >> 24)
+
+ out[56] = byte(x14)
+ out[57] = byte(x14 >> 8)
+ out[58] = byte(x14 >> 16)
+ out[59] = byte(x14 >> 24)
+
+ out[60] = byte(x15)
+ out[61] = byte(x15 >> 8)
+ out[62] = byte(x15 >> 16)
+ out[63] = byte(x15 >> 24)
+}
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go
new file mode 100644
index 00000000..903c7858
--- /dev/null
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go
@@ -0,0 +1,23 @@
+// Copyright 2012 The Go 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 amd64,!appengine,!gccgo
+
+package salsa
+
+// This function is implemented in salsa2020_amd64.s.
+
+//go:noescape
+
+func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte)
+
+// XORKeyStream crypts bytes from in to out using the given key and counters.
+// In and out may be the same slice but otherwise should not overlap. Counter
+// contains the raw salsa20 counter bytes (both nonce and block counter).
+func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
+ if len(in) == 0 {
+ return
+ }
+ salsa2020XORKeyStream(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0])
+}
diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go
new file mode 100644
index 00000000..95f8ca5b
--- /dev/null
+++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go
@@ -0,0 +1,234 @@
+// Copyright 2012 The Go 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 !amd64 appengine gccgo
+
+package salsa
+
+const rounds = 20
+
+// core applies the Salsa20 core function to 16-byte input in, 32-byte key k,
+// and 16-byte constant c, and puts the result into 64-byte array out.
+func core(out *[64]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
+ j0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24
+ j1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24
+ j2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24
+ j3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24
+ j4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24
+ j5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24
+ j6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
+ j7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
+ j8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
+ j9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
+ j10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24
+ j11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24
+ j12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24
+ j13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24
+ j14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24
+ j15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24
+
+ x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8
+ x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15
+
+ for i := 0; i < rounds; i += 2 {
+ u := x0 + x12
+ x4 ^= u<<7 | u>>(32-7)
+ u = x4 + x0
+ x8 ^= u<<9 | u>>(32-9)
+ u = x8 + x4
+ x12 ^= u<<13 | u>>(32-13)
+ u = x12 + x8
+ x0 ^= u<<18 | u>>(32-18)
+
+ u = x5 + x1
+ x9 ^= u<<7 | u>>(32-7)
+ u = x9 + x5
+ x13 ^= u<<9 | u>>(32-9)
+ u = x13 + x9
+ x1 ^= u<<13 | u>>(32-13)
+ u = x1 + x13
+ x5 ^= u<<18 | u>>(32-18)
+
+ u = x10 + x6
+ x14 ^= u<<7 | u>>(32-7)
+ u = x14 + x10
+ x2 ^= u<<9 | u>>(32-9)
+ u = x2 + x14
+ x6 ^= u<<13 | u>>(32-13)
+ u = x6 + x2
+ x10 ^= u<<18 | u>>(32-18)
+
+ u = x15 + x11
+ x3 ^= u<<7 | u>>(32-7)
+ u = x3 + x15
+ x7 ^= u<<9 | u>>(32-9)
+ u = x7 + x3
+ x11 ^= u<<13 | u>>(32-13)
+ u = x11 + x7
+ x15 ^= u<<18 | u>>(32-18)
+
+ u = x0 + x3
+ x1 ^= u<<7 | u>>(32-7)
+ u = x1 + x0
+ x2 ^= u<<9 | u>>(32-9)
+ u = x2 + x1
+ x3 ^= u<<13 | u>>(32-13)
+ u = x3 + x2
+ x0 ^= u<<18 | u>>(32-18)
+
+ u = x5 + x4
+ x6 ^= u<<7 | u>>(32-7)
+ u = x6 + x5
+ x7 ^= u<<9 | u>>(32-9)
+ u = x7 + x6
+ x4 ^= u<<13 | u>>(32-13)
+ u = x4 + x7
+ x5 ^= u<<18 | u>>(32-18)
+
+ u = x10 + x9
+ x11 ^= u<<7 | u>>(32-7)
+ u = x11 + x10
+ x8 ^= u<<9 | u>>(32-9)
+ u = x8 + x11
+ x9 ^= u<<13 | u>>(32-13)
+ u = x9 + x8
+ x10 ^= u<<18 | u>>(32-18)
+
+ u = x15 + x14
+ x12 ^= u<<7 | u>>(32-7)
+ u = x12 + x15
+ x13 ^= u<<9 | u>>(32-9)
+ u = x13 + x12
+ x14 ^= u<<13 | u>>(32-13)
+ u = x14 + x13
+ x15 ^= u<<18 | u>>(32-18)
+ }
+ x0 += j0
+ x1 += j1
+ x2 += j2
+ x3 += j3
+ x4 += j4
+ x5 += j5
+ x6 += j6
+ x7 += j7
+ x8 += j8
+ x9 += j9
+ x10 += j10
+ x11 += j11
+ x12 += j12
+ x13 += j13
+ x14 += j14
+ x15 += j15
+
+ out[0] = byte(x0)
+ out[1] = byte(x0 >> 8)
+ out[2] = byte(x0 >> 16)
+ out[3] = byte(x0 >> 24)
+
+ out[4] = byte(x1)
+ out[5] = byte(x1 >> 8)
+ out[6] = byte(x1 >> 16)
+ out[7] = byte(x1 >> 24)
+
+ out[8] = byte(x2)
+ out[9] = byte(x2 >> 8)
+ out[10] = byte(x2 >> 16)
+ out[11] = byte(x2 >> 24)
+
+ out[12] = byte(x3)
+ out[13] = byte(x3 >> 8)
+ out[14] = byte(x3 >> 16)
+ out[15] = byte(x3 >> 24)
+
+ out[16] = byte(x4)
+ out[17] = byte(x4 >> 8)
+ out[18] = byte(x4 >> 16)
+ out[19] = byte(x4 >> 24)
+
+ out[20] = byte(x5)
+ out[21] = byte(x5 >> 8)
+ out[22] = byte(x5 >> 16)
+ out[23] = byte(x5 >> 24)
+
+ out[24] = byte(x6)
+ out[25] = byte(x6 >> 8)
+ out[26] = byte(x6 >> 16)
+ out[27] = byte(x6 >> 24)
+
+ out[28] = byte(x7)
+ out[29] = byte(x7 >> 8)
+ out[30] = byte(x7 >> 16)
+ out[31] = byte(x7 >> 24)
+
+ out[32] = byte(x8)
+ out[33] = byte(x8 >> 8)
+ out[34] = byte(x8 >> 16)
+ out[35] = byte(x8 >> 24)
+
+ out[36] = byte(x9)
+ out[37] = byte(x9 >> 8)
+ out[38] = byte(x9 >> 16)
+ out[39] = byte(x9 >> 24)
+
+ out[40] = byte(x10)
+ out[41] = byte(x10 >> 8)
+ out[42] = byte(x10 >> 16)
+ out[43] = byte(x10 >> 24)
+
+ out[44] = byte(x11)
+ out[45] = byte(x11 >> 8)
+ out[46] = byte(x11 >> 16)
+ out[47] = byte(x11 >> 24)
+
+ out[48] = byte(x12)
+ out[49] = byte(x12 >> 8)
+ out[50] = byte(x12 >> 16)
+ out[51] = byte(x12 >> 24)
+
+ out[52] = byte(x13)
+ out[53] = byte(x13 >> 8)
+ out[54] = byte(x13 >> 16)
+ out[55] = byte(x13 >> 24)
+
+ out[56] = byte(x14)
+ out[57] = byte(x14 >> 8)
+ out[58] = byte(x14 >> 16)
+ out[59] = byte(x14 >> 24)
+
+ out[60] = byte(x15)
+ out[61] = byte(x15 >> 8)
+ out[62] = byte(x15 >> 16)
+ out[63] = byte(x15 >> 24)
+}
+
+// XORKeyStream crypts bytes from in to out using the given key and counters.
+// In and out may be the same slice but otherwise should not overlap. Counter
+// contains the raw salsa20 counter bytes (both nonce and block counter).
+func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
+ var block [64]byte
+ var counterCopy [16]byte
+ copy(counterCopy[:], counter[:])
+
+ for len(in) >= 64 {
+ core(&block, &counterCopy, key, &Sigma)
+ for i, x := range block {
+ out[i] = in[i] ^ x
+ }
+ u := uint32(1)
+ for i := 8; i < 16; i++ {
+ u += uint32(counterCopy[i])
+ counterCopy[i] = byte(u)
+ u >>= 8
+ }
+ in = in[64:]
+ out = out[64:]
+ }
+
+ if len(in) > 0 {
+ core(&block, &counterCopy, key, &Sigma)
+ for i, v := range in {
+ out[i] = v ^ block[i]
+ }
+ }
+}
diff --git a/vendor/manifest b/vendor/manifest
index ef9cc7d9..705c463d 100644
--- a/vendor/manifest
+++ b/vendor/manifest
@@ -44,6 +44,14 @@
"notests": true
},
{
+ "importpath": "github.com/bwmarrin/discordgo",
+ "repository": "https://github.com/bwmarrin/discordgo",
+ "vcs": "git",
+ "revision": "11fa9dc906c531a85cd969b7b6b77c2f452a61b3",
+ "branch": "master",
+ "notests": true
+ },
+ {
"importpath": "github.com/gorilla/schema",
"repository": "https://github.com/gorilla/schema",
"vcs": "",
@@ -185,6 +193,33 @@
"notests": true
},
{
+ "importpath": "golang.org/x/crypto/nacl/secretbox",
+ "repository": "https://go.googlesource.com/crypto",
+ "vcs": "git",
+ "revision": "81372b2fc2f10bef2a7f338da115c315a56b2726",
+ "branch": "master",
+ "path": "/nacl/secretbox",
+ "notests": true
+ },
+ {
+ "importpath": "golang.org/x/crypto/poly1305",
+ "repository": "https://go.googlesource.com/crypto",
+ "vcs": "git",
+ "revision": "81372b2fc2f10bef2a7f338da115c315a56b2726",
+ "branch": "master",
+ "path": "/poly1305",
+ "notests": true
+ },
+ {
+ "importpath": "golang.org/x/crypto/salsa20/salsa",
+ "repository": "https://go.googlesource.com/crypto",
+ "vcs": "git",
+ "revision": "81372b2fc2f10bef2a7f338da115c315a56b2726",
+ "branch": "master",
+ "path": "/salsa20/salsa",
+ "notests": true
+ },
+ {
"importpath": "golang.org/x/net/http2/hpack",
"repository": "https://go.googlesource.com/net",
"vcs": "",