From a0b84beb9bae3514940a0cde40948e885184f555 Mon Sep 17 00:00:00 2001
From: Wim <wim@42.be>
Date: Mon, 19 Sep 2016 20:53:26 +0200
Subject: [PATCH] Add Discord support

---
 README.md                                     |    5 +-
 bridge/bridge.go                              |    3 +
 bridge/config/config.go                       |    6 +-
 changelog.md                                  |    1 +
 gateway/gateway.go                            |    2 +
 matterbridge.toml.sample                      |   29 +
 vendor/github.com/bwmarrin/discordgo/LICENSE  |   28 +
 .../github.com/bwmarrin/discordgo/discord.go  |  257 +++
 .../bwmarrin/discordgo/endpoints.go           |   99 ++
 .../github.com/bwmarrin/discordgo/events.go   |  159 ++
 .../discordgo/examples/airhorn/main.go        |  186 ++
 .../discordgo/examples/appmaker/main.go       |   98 ++
 .../examples/avatar/localfile/main.go         |   73 +
 .../discordgo/examples/avatar/url/main.go     |   86 +
 .../discordgo/examples/mytoken/main.go        |   33 +
 .../discordgo/examples/new_basic/main.go      |   58 +
 .../discordgo/examples/pingpong/main.go       |   78 +
 .../github.com/bwmarrin/discordgo/logging.go  |   95 +
 .../github.com/bwmarrin/discordgo/message.go  |   82 +
 .../github.com/bwmarrin/discordgo/oauth2.go   |  120 ++
 .../github.com/bwmarrin/discordgo/restapi.go  | 1403 +++++++++++++++
 vendor/github.com/bwmarrin/discordgo/state.go |  746 ++++++++
 .../github.com/bwmarrin/discordgo/structs.go  |  521 ++++++
 vendor/github.com/bwmarrin/discordgo/voice.go |  853 +++++++++
 vendor/github.com/bwmarrin/discordgo/wsapi.go |  679 ++++++++
 vendor/golang.org/x/crypto/poly1305/LICENSE   |   27 +
 .../x/crypto/poly1305/const_amd64.s           |   45 +
 .../golang.org/x/crypto/poly1305/poly1305.go  |   32 +
 .../x/crypto/poly1305/poly1305_amd64.s        |  497 ++++++
 .../x/crypto/poly1305/poly1305_arm.s          |  379 ++++
 .../golang.org/x/crypto/poly1305/sum_amd64.go |   24 +
 .../golang.org/x/crypto/poly1305/sum_arm.go   |   24 +
 .../golang.org/x/crypto/poly1305/sum_ref.go   | 1531 +++++++++++++++++
 .../golang.org/x/crypto/salsa20/salsa/LICENSE |   27 +
 .../x/crypto/salsa20/salsa/hsalsa20.go        |  144 ++
 .../x/crypto/salsa20/salsa/salsa2020_amd64.s  |  902 ++++++++++
 .../x/crypto/salsa20/salsa/salsa208.go        |  199 +++
 .../x/crypto/salsa20/salsa/salsa20_amd64.go   |   23 +
 .../x/crypto/salsa20/salsa/salsa20_ref.go     |  234 +++
 vendor/manifest                               |   35 +
 40 files changed, 9819 insertions(+), 4 deletions(-)
 create mode 100644 vendor/github.com/bwmarrin/discordgo/LICENSE
 create mode 100644 vendor/github.com/bwmarrin/discordgo/discord.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/endpoints.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/events.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/examples/avatar/localfile/main.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/examples/avatar/url/main.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/logging.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/message.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/oauth2.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/restapi.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/state.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/structs.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/voice.go
 create mode 100644 vendor/github.com/bwmarrin/discordgo/wsapi.go
 create mode 100644 vendor/golang.org/x/crypto/poly1305/LICENSE
 create mode 100644 vendor/golang.org/x/crypto/poly1305/const_amd64.s
 create mode 100644 vendor/golang.org/x/crypto/poly1305/poly1305.go
 create mode 100644 vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s
 create mode 100644 vendor/golang.org/x/crypto/poly1305/poly1305_arm.s
 create mode 100644 vendor/golang.org/x/crypto/poly1305/sum_amd64.go
 create mode 100644 vendor/golang.org/x/crypto/poly1305/sum_arm.go
 create mode 100644 vendor/golang.org/x/crypto/poly1305/sum_ref.go
 create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/LICENSE
 create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go
 create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s
 create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go
 create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go
 create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go

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
@@ -43,6 +43,14 @@
 			"branch": "master",
 			"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",
@@ -184,6 +192,33 @@
 			"path": "/blowfish",
 			"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",