~kris/9p

disco

f35618b1a898c7cf6c64df4ab9ccb8298d31fc43 — Sean Hinchee 8 years ago 00095dd
updating discordgo vending
M DiscordGo/LICENSE => DiscordGo/LICENSE +1 -1
@@ 11,7 11,7 @@ modification, are permitted provided that the following conditions are met:
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of GoDiscord nor the names of its
* 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.


M DiscordGo/README.md => DiscordGo/README.md +34 -32
@@ 1,31 1,33 @@
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">
Discordgo 
====
>If for some reason you stumbled upon this repo, use the official one: https://github.com/bwmarrin/discordgo
This is merely a master fork for stability reasons of discord-cli.
Since their highly unstable develop branch is their default one this can give problems with continious integration tools or even the dependency errors. When 1.0 arrives, and becomes default, this repo gets removed. 
# DiscordGo 

[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23discordgo-blue.svg)](https://discord.gg/0f1SbxBZjYoCtNPP) [![Discord API](https://img.shields.io/badge/Discord%20API-%23go_discordgo-blue.svg)](https://discord.gg/0SBTUU1wZTWT6sqd)

[![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) [![Go report](http://goreportcard.com/badge/bwmarrin/discordgo)](http://goreportcard.com/report/bwmarrin/discordgo) [![Build Status](https://travis-ci.org/bwmarrin/discordgo.svg?branch=master)](https://travis-ci.org/bwmarrin/discordgo)
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

Discordgo is a [Go](https://golang.org/) package that provides low level 
bindings to the [Discord](https://discordapp.com/) chat client API. Discordgo 
has nearly complete support for all of the Discord JSON-API endpoints, websocket
DiscordGo is a [Go](https://golang.org/) package that provides low level 
bindings to the [Discord](https://discordapp.com/) chat client API. DiscordGo 
has nearly complete support for all of the Discord API endpoints, websocket
interface, and voice interface.

* See [dgVoice](https://github.com/bwmarrin/dgvoice) package to extend Discordgo
with additional voice helper functions and features.
If you would like to help the DiscordGo package please use 
[this link](https://discordapp.com/oauth2/authorize?client_id=173113690092994561&scope=bot)
to add the official DiscordGo test bot **dgo** to your server. This provides 
indispensable help to this project.

* See [dgVoice](https://github.com/bwmarrin/dgvoice) package for an example of
additional voice helper functions and features for DiscordGo

* See [dca](https://github.com/bwmarrin/dca) for an **experimental** stand alone
tool that wraps `ffmpeg` to create opus encoded audio appropriate for use with
Discord (and Discordgo)
Discord (and DiscordGo)

Join [#go_discordgo](https://discord.gg/0SBTUU1wZTWT6sqd) Discord chat channel 
for support.
**For help with this package or general Go discussion, please join the [Discord 
Gophers](https://discord.gg/0f1SbxBZjYq9jLBk) chat server.**

## Getting Started

### master vs develop Branch
* The master branch represents the latest released version of Discordgo.  This
* The master branch represents the latest released version of DiscordGo.  This
branch will always have a stable and tested version of the library. Each release
is tagged and you can easily download a specific release and view release notes
on the github [releases](https://github.com/bwmarrin/discordgo/releases) page.


@@ 43,9 45,6 @@ slightly and have a more stable package with documented releases then use *maste

### Installing

Discordgo has been tested to compile on Debian 8 (Go 1.3.3), 
FreeBSD 10 (Go 1.5.1), and Windows 7 (Go 1.5.2).

This assumes you already have a working Go environment, if not please see
[this page](https://golang.org/doc/install) first.



@@ 62,8 61,6 @@ cd $GOPATH/src/github.com/bwmarrin/discordgo
git checkout develop
```



### Usage

Import the package into your project.


@@ 76,7 73,7 @@ Construct a new Discord client which can be used to access the variety of
Discord API functions and to set callback functions for Discord events.

```go
discord, err := discordgo.New("username", "password")
discord, err := discordgo.New("authentication token")
```

See Documentation and Examples below for more detailed information.


@@ 85,29 82,30 @@ See Documentation and Examples below for more detailed information.
## Documentation

**NOTICE** : This library and the Discord API are unfinished.
Because of that there may be major changes to library functions, constants,
and structures.
Because of that there may be major changes to library in the future.

The Discordgo code is fairly well documented at this point and is currently
The DiscordGo code is fairly well documented at this point and is currently
the only documentation available.  Both GoDoc and GoWalker (below) present
that information in a nice format.

- [![GoDoc](https://godoc.org/github.com/bwmarrin/discordgo?status.svg)](https://godoc.org/github.com/bwmarrin/discordgo) 
- [![Go Walker](http://gowalker.org/api/v1/badge)](https://gowalker.org/github.com/bwmarrin/discordgo) 
- [Unofficial Discord API Documentation](https://discordapi.readthedocs.org/en/latest/) 
- Hand crafted documentation coming eventually.


## Examples

Below is a list of examples and other projects using Discordgo.  Please submit 
Below is a list of examples and other projects using DiscordGo.  Please submit 
an issue if you would like your project added or removed from this list 

- [Basic - New](https://github.com/bwmarrin/discordgo/tree/develop/examples/new_basic) A basic example using the easy New() helper function
- [Basic - API](https://github.com/bwmarrin/discordgo/tree/develop/examples/api_basic) A basic example using the low level API functions.
- [Bruxism](https://github.com/iopred/bruxism) A chat bot for YouTube and Discord
- [GoGerard](https://github.com/GoGerard/GoGerard) A modern bot for Discord
- [Digo](https://github.com/sethdmoore/digo) A pluggable bot for your Discord server
- [DiscordGo Examples](https://github.com/bwmarrin/discordgo/tree/master/examples) A collection of example programs written with DiscordGo
- [Awesome DiscordGo](https://github.com/bwmarrin/discordgo/wiki/Awesome-DiscordGo) A curated list of high quality projects using DiscordGo

## Troubleshooting
For help with common problems please reference the 
[Troubleshooting](https://github.com/bwmarrin/discordgo/wiki/Troubleshooting) 
section of the project wiki.


## Contributing
Contributions are very welcomed, however please follow the below guidelines.


@@ 126,3 124,7 @@ discussing it.

See [this chart](https://abal.moe/Discord/Libraries.html) for a feature 
comparison and list of other Discord API libraries.

## Special Thanks

[Chris Rhodes](https://github.com/iopred) - For the DiscordGo logo and tons of PRs

M DiscordGo/discord.go => DiscordGo/discord.go +32 -133
@@ 14,12 14,17 @@
package discordgo

import (
	"errors"
	"fmt"
	"reflect"
	"net/http"
	"time"
)

// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.11.0"
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.17.0"

// ErrMFA will be risen by New when the user has 2FA.
var ErrMFA = errors.New("account has 2FA enabled")

// New creates a new Discord session and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero


@@ 27,23 32,37 @@ const VERSION = "0.11.0"
// There are 3 ways to call New:
//     With a single auth token - All requests will use the token blindly,
//         no verification of the token will be done and requests may fail.
//         IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
//         eg: `"Bot <token>"`
//     With an email and password - Discord will sign in with the provided
//         credentials.
//     With an email, password and auth token - Discord will verify the auth
//         token, if it is invalid it will sign in with the provided
//         credentials. This is the Discord recommended way to sign in.
//
// NOTE: While email/pass authentication is supported by DiscordGo it is
// HIGHLY DISCOURAGED by Discord. Please only use email/pass to obtain a token
// and then use that authentication token for all future connections.
// Also, doing any form of automation with a user (non Bot) account may result
// in that account being permanently banned from Discord.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{
		State:                  NewState(),
		ratelimiter:            NewRatelimiter(),
		StateEnabled:           true,
		Compress:               true,
		ShouldReconnectOnError: true,
		ShardID:                0,
		ShardCount:             1,
		MaxRestRetries:         3,
		Client:                 &http.Client{Timeout: (20 * time.Second)},
		sequence:               new(int64),
		LastHeartbeatAck:       time.Now().UTC(),
	}

	// If no arguments are passed return the empty Session interface.
	// Later I will add default values, if appropriate.
	if args == nil {
		return
	}


@@ 58,7 77,7 @@ func New(args ...interface{}) (s *Session, err error) {

		case []string:
			if len(v) > 3 {
				err = fmt.Errorf("Too many string parameters provided.")
				err = fmt.Errorf("too many string parameters provided")
				return
			}



@@ 89,15 108,15 @@ func New(args ...interface{}) (s *Session, err error) {
			} else if s.Token == "" {
				s.Token = v
			} else {
				err = fmt.Errorf("Too many string parameters provided.")
				err = fmt.Errorf("too many string parameters provided")
				return
			}

			//		case Config:
			// TODO: Parse configuration
			// TODO: Parse configuration struct

		default:
			err = fmt.Errorf("Unsupported parameter type provided.")
			err = fmt.Errorf("unsupported parameter type provided")
			return
		}
	}


@@ 111,7 130,11 @@ func New(args ...interface{}) (s *Session, err error) {
	} else {
		err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			if s.MFA {
				err = ErrMFA
			} else {
				err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			}
			return
		}
	}


@@ 121,127 144,3 @@ func New(args ...interface{}) (s *Session, err error) {

	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)

	handlers := s.handlers[eventType]
	if handlers == nil {
		handlers = []reflect.Value{}
	}
	s.handlers[eventType] = append(handlers, 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[reflect.TypeOf(event)]; ok {
		for _, handler := range handlers {
			handler.Call(handlerParameters)
		}
	}

	if handlers, ok := s.handlers[nil]; ok {
		for _, handler := range handlers {
			handler.Call(handlerParameters)
		}
	}
}

// initialize adds all internal handlers and state tracking handlers.
func (s *Session) initialize() {
	s.handlersMu.Lock()
	if s.handlers != nil {
		s.handlersMu.Unlock()
		return
	}

	s.handlers = map[interface{}][]reflect.Value{}
	s.handlersMu.Unlock()

	s.AddHandler(s.onEvent)
	s.AddHandler(s.onReady)
	s.AddHandler(s.onVoiceServerUpdate)
	s.AddHandler(s.onVoiceStateUpdate)
	s.AddHandler(s.State.onInterface)
}

// onEvent handles events that are unhandled or errored while unmarshalling
func (s *Session) onEvent(se *Session, e *Event) {
	printEvent(e)
}

// onReady handles the ready event.
func (s *Session) onReady(se *Session, r *Ready) {
	go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}

M DiscordGo/discord_test.go => DiscordGo/discord_test.go +50 -62
@@ 3,6 3,7 @@ package discordgo
import (
	"os"
	"runtime"
	"sync/atomic"
	"testing"
	"time"
)


@@ 10,18 11,26 @@ import (
//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////// VARS NEEDED FOR TESTING
var (
	dg *Session // Stores global discordgo session
	dg    *Session // Stores a global discordgo user session
	dgBot *Session // Stores a global discordgo bot session

	envToken    = os.Getenv("DG_TOKEN")    // Token to use when authenticating
	envToken    = os.Getenv("DG_TOKEN")    // Token to use when authenticating the user account
	envBotToken = os.Getenv("DGB_TOKEN")   // Token to use when authenticating the bot account
	envEmail    = os.Getenv("DG_EMAIL")    // Email to use when authenticating
	envPassword = os.Getenv("DG_PASSWORD") // Password to use when authenticating
	//	envGuild    = os.Getenv("DG_GUILD")    // Guild ID to use for tests
	envChannel = os.Getenv("DG_CHANNEL") // Channel ID to use for tests
	envGuild    = os.Getenv("DG_GUILD")    // Guild ID to use for tests
	envChannel  = os.Getenv("DG_CHANNEL")  // Channel ID to use for tests
	//	envUser     = os.Getenv("DG_USER")     // User ID to use for tests
	envAdmin = os.Getenv("DG_ADMIN") // User ID of admin user to use for tests
)

func init() {
	if envBotToken != "" {
		if d, err := New(envBotToken); err == nil {
			dgBot = d
		}
	}

	if envEmail == "" || envPassword == "" || envToken == "" {
		return
	}


@@ 32,46 41,6 @@ func init() {
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////// HELPER FUNCTIONS USED FOR TESTING

// This waits x time for the check bool to be the want bool
func waitBoolEqual(timeout time.Duration, check *bool, want bool) bool {

	start := time.Now()
	for {
		if *check == want {
			return true
		}

		if time.Since(start) > timeout {
			return false
		}

		runtime.Gosched()
	}
}

// Checks if we're connected to Discord
func isConnected() bool {

	if dg == nil {
		return false
	}

	if dg.Token == "" {
		return false
	}

	// Need a way to see if the ws connection is nil

	if !waitBoolEqual(10*time.Second, &dg.DataReady, true) {
		return false
	}

	return true
}

//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////// START OF TESTS

// TestNew tests the New() function without any arguments.  This should return


@@ 203,8 172,21 @@ func TestOpenClose(t *testing.T) {
		t.Fatalf("TestClose, d.Open failed: %+v", err)
	}

	if !waitBoolEqual(10*time.Second, &d.DataReady, true) {
		t.Fatal("DataReady never became true.")
	// We need a better way to know the session is ready for use,
	// this is totally gross.
	start := time.Now()
	for {
		d.RLock()
		if d.DataReady {
			d.RUnlock()
			break
		}
		d.RUnlock()

		if time.Since(start) > 10*time.Second {
			t.Fatal("DataReady never became true.yy")
		}
		runtime.Gosched()
	}

	// TODO find a better way


@@ 226,19 208,20 @@ func TestOpenClose(t *testing.T) {
}

func TestAddHandler(t *testing.T) {
	testHandlerCalled := 0

	testHandlerCalled := int32(0)
	testHandler := func(s *Session, m *MessageCreate) {
		testHandlerCalled++
		atomic.AddInt32(&testHandlerCalled, 1)
	}

	interfaceHandlerCalled := 0
	interfaceHandlerCalled := int32(0)
	interfaceHandler := func(s *Session, i interface{}) {
		interfaceHandlerCalled++
		atomic.AddInt32(&interfaceHandlerCalled, 1)
	}

	bogusHandlerCalled := false
	bogusHandlerCalled := int32(0)
	bogusHandler := func(s *Session, se *Session) {
		bogusHandlerCalled = true
		atomic.AddInt32(&bogusHandlerCalled, 1)
	}

	d := Session{}


@@ 248,41 231,46 @@ func TestAddHandler(t *testing.T) {
	d.AddHandler(interfaceHandler)
	d.AddHandler(bogusHandler)

	d.handle(&MessageCreate{})
	d.handle(&MessageDelete{})
	d.handleEvent(messageCreateEventType, &MessageCreate{})
	d.handleEvent(messageDeleteEventType, &MessageDelete{})

	<-time.After(500 * time.Millisecond)

	// testHandler will be called twice because it was added twice.
	if testHandlerCalled != 2 {
	if atomic.LoadInt32(&testHandlerCalled) != 2 {
		t.Fatalf("testHandler was not called twice.")
	}

	// interfaceHandler will be called twice, once for each event.
	if interfaceHandlerCalled != 2 {
	if atomic.LoadInt32(&interfaceHandlerCalled) != 2 {
		t.Fatalf("interfaceHandler was not called twice.")
	}

	if bogusHandlerCalled {
	if atomic.LoadInt32(&bogusHandlerCalled) != 0 {
		t.Fatalf("bogusHandler was called.")
	}
}

func TestRemoveHandler(t *testing.T) {
	testHandlerCalled := 0

	testHandlerCalled := int32(0)
	testHandler := func(s *Session, m *MessageCreate) {
		testHandlerCalled++
		atomic.AddInt32(&testHandlerCalled, 1)
	}

	d := Session{}
	r := d.AddHandler(testHandler)

	d.handle(&MessageCreate{})
	d.handleEvent(messageCreateEventType, &MessageCreate{})

	r()

	d.handle(&MessageCreate{})
	d.handleEvent(messageCreateEventType, &MessageCreate{})

	<-time.After(500 * time.Millisecond)

	// testHandler will be called once, as it was removed in between calls.
	if testHandlerCalled != 1 {
	if atomic.LoadInt32(&testHandlerCalled) != 1 {
		t.Fatalf("testHandler was not called once.")
	}
}

A DiscordGo/docs/GettingStarted.md => DiscordGo/docs/GettingStarted.md +142 -0
@@ 0,0 1,142 @@
# Getting Started

This page is dedicated to helping you get started on your way to making the
next great Discord bot or client with DiscordGo. Once you've done that please
don't forget to submit it to the 
[Awesome DiscordGo](https://github.com/bwmarrin/discordgo/wiki/Awesome-DiscordGo) list :).


**First, lets cover a few topics so you can make the best choices on how to 
move forward from here.**


### Master vs Develop
**When installing DiscordGo you will need to decide if you want to use the current
master branch or the bleeding edge development branch.**

* The **master** branch represents the latest released version of DiscordGo. This
branch will always have a stable and tested version of the library. Each 
release is tagged and you can easily download a specific release and view the 
release notes on the github [releases](https://github.com/bwmarrin/discordgo/releases) 
page.

* The **develop** branch is where all development happens and almost always has
new features over the master branch.  However breaking changes are frequently
added the develop branch and sometimes bugs are introduced.  Bugs get fixed
and the breaking changes get documented before pushing to master.  

*So, what should you use?*

Due to the how frequently the Discord API is changing there is a high chance
that the *master* branch may be lacking important features.  Because of that, if
you can accept the constant changing nature of the *develop* branch and the 
chance that it may occasionally contain bugs then it is the recommended branch 
to use.  Otherwise, if you want to tail behind development slightly and have a 
more stable package with documented releases then please use the *master* 
branch instead.


### Client vs Bot

You probably already know the answer to this but now is a good time to decide
if your goal is to write a client application or a bot.  DiscordGo aims to fully
support both client applications and bots but there are some differences 
between the two that you should understand.

#### Client Application
A client application is a program that is intended to be used by a normal user 
as a replacement for the official clients that Discord provides. An example of
this would be a terminal client used to read and send messages with your normal
user account or possibly a new desktop client that provides a different set of
features than the official desktop client that Discord already provides.

Client applications work with normal user accounts and you can login with an
email address and password or a special authentication token.  However, normal
user accounts are not allowed to perform any type of automation and doing so can
cause the account to be banned from Discord. Also normal user accounts do not 
support multi-server voice connections and some other features that are 
exclusive to Bot accounts only.

To create a new user account (if you have not done so already) visit the 
[Discord](https://discordapp.com/) website and click on the 
**Try Discord Now, It's Free** button then follow the steps to setup your
new account.


#### Bot Application
A bot application is a special program that interacts with the Discord servers
to perform some form of automation or provide some type of service.  Examples 
are things like number trivia games, music streaming, channel moderation, 
sending reminders, playing loud airhorn sounds, comic generators, YouTube 
integration, Twitch integration.. You're *almost* only limited by your imagination.

Bot applications require the use of a special Bot account.  These accounts are
tied to your personal user account. Bot accounts cannot login with the normal
user clients and they cannot join servers the same way a user does. They do not 
have access to some user client specific features however they gain access to
many Bot specific features.

To create a new bot account first create yourself a normal user account on 
Discord then visit the [My Applications](https://discordapp.com/developers/applications/me)
page and click on the **New Application** box.  Follow the prompts from there
to finish creating your account.


**More information about Bots vs Client accounts can be found [here](https://discordapp.com/developers/docs/topics/oauth2#bot-vs-user-accounts)**

# Requirements

DiscordGo requires Go version 1.4 or higher.  It has been tested to compile and
run successfully on Debian Linux 8, FreeBSD 10, and Windows 7.  It is expected 
that it should work anywhere Go 1.4 or higher works. If you run into problems
please let us know :)

You must already have a working Go environment setup to use DiscordGo.  If you 
are new to Go and have not yet installed and tested it on your computer then 
please visit [this page](https://golang.org/doc/install) first then I highly
recommend you walk though [A Tour of Go](https://tour.golang.org/welcome/1) to
help get your familiar with the Go language.  Also checkout the relevent Go plugin 
for your editor - they are hugely helpful when developing Go code.

* Vim - [vim-go](https://github.com/fatih/vim-go)
* Sublime - [GoSublime](https://github.com/DisposaBoy/GoSublime)
* Atom - [go-plus](https://atom.io/packages/go-plus)
* Visual Studio - [vscode-go](https://github.com/Microsoft/vscode-go)


# Install DiscordGo

Like any other Go package the fist step is to `go get` the package.  This will
always pull the latest released version from the master branch. Then run 
`go install` to compile and install the libraries on your system.

#### Linux/BSD

Run go get to download the package to your GOPATH/src folder.

```sh
go get github.com/bwmarrin/discordgo
```

If you want to use the develop branch, follow these steps next.

```sh
cd $GOPATH/src/github.com/bwmarrin/discordgo
git checkout develop
```

Finally, compile and install the package into the GOPATH/pkg folder. This isn't
absolutely required but doing this will allow the Go plugin for your editor to
provide autocomplete for all DiscordGo functions.

```sh
cd $GOPATH/src/github.com/bwmarrin/discordgo
go install
```

#### Windows
Placeholder.


# Next...
More coming soon.

A DiscordGo/docs/img/discordgo.png => DiscordGo/docs/img/discordgo.png +0 -0
A DiscordGo/docs/index.md => DiscordGo/docs/index.md +33 -0
@@ 0,0 1,33 @@
## DiscordGo
<hr>
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

[Go](https://golang.org/) (golang) interface for the [Discord](https://discordapp.com/) 
chat service.  Provides both low-level direct bindings to the 
Discord API and helper functions that allow you to make custom clients and chat 
bot applications easily.

[Discord](https://discordapp.com/) is an all-in-one voice and text chat for 
gamers that's free, secure, and works on both your desktop and phone. 
 
### Why DiscordGo?
* High Performance
* Minimal Memory & CPU Load
* Low-level bindings to Discord REST API Endpoints
* Support for the data websocket interface
* Multi-Server voice connections (send and receive)
* State tracking and caching

### Learn More
* Check out the [Getting Started](GettingStarted) section
* Read the reference docs on [Godoc](https://godoc.org/github.com/bwmarrin/discordgo) or [GoWalker](https://gowalker.org/github.com/bwmarrin/discordgo)
* Try the [examples](https://github.com/bwmarrin/discordgo/tree/master/examples)
* Explore [Awesome DiscordGo](https://github.com/bwmarrin/discordgo/wiki/Awesome-DiscordGo)

### Join Us!
Both of the below links take you to chat channels where you can get more 
information and support for DiscordGo.  There's also a chance to make some 
friends :)

* Join the [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP) chat server dedicated to Go programming.
* Join the [Discord API](https://discord.gg/0SBTUU1wZTWT6sqd) chat server dedicated to the Discord API.

M DiscordGo/endpoints.go => DiscordGo/endpoints.go +119 -72
@@ 11,78 11,125 @@

package discordgo

// APIVersion is the Discord API version used for the REST and Websocket API.
var APIVersion = "6"

// Known Discord API Endpoints.
var (
	STATUS      = "https://status.discordapp.com/api/v2/"
	SM          = STATUS + "scheduled-maintenances/"
	SM_ACTIVE   = SM + "active.json"
	SM_UPCOMING = SM + "upcoming.json"

	DISCORD  = "https://discordapp.com" // TODO consider removing
	API      = DISCORD + "/api/"
	GUILDS   = API + "guilds/"
	CHANNELS = API + "channels/"
	USERS    = API + "users/"
	GATEWAY  = API + "gateway"

	AUTH            = API + "auth/"
	LOGIN           = AUTH + "login"
	LOGOUT          = AUTH + "logout"
	VERIFY          = AUTH + "verify"
	VERIFY_RESEND   = AUTH + "verify/resend"
	FORGOT_PASSWORD = AUTH + "forgot"
	RESET_PASSWORD  = AUTH + "reset"
	REGISTER        = AUTH + "register"

	VOICE         = API + "/voice/"
	VOICE_REGIONS = VOICE + "regions"
	VOICE_ICE     = VOICE + "ice"

	TUTORIAL            = API + "tutorial/"
	TUTORIAL_INDICATORS = TUTORIAL + "indicators"

	TRACK        = API + "track"
	SSO          = API + "sso"
	REPORT       = API + "report"
	INTEGRATIONS = API + "integrations"

	USER             = func(uID string) string { return USERS + uID }
	USER_AVATAR      = func(uID, aID string) string { return USERS + uID + "/avatars/" + aID + ".jpg" }
	USER_SETTINGS    = func(uID string) string { return USERS + uID + "/settings" }
	USER_GUILDS      = func(uID string) string { return USERS + uID + "/guilds" }
	USER_GUILD       = func(uID, gID string) string { return USERS + uID + "/guilds/" + gID }
	USER_CHANNELS    = func(uID string) string { return USERS + uID + "/channels" }
	USER_DEVICES     = func(uID string) string { return USERS + uID + "/devices" }
	USER_CONNECTIONS = func(uID string) string { return USERS + uID + "/connections" }

	GUILD              = func(gID string) string { return GUILDS + gID }
	GUILD_INIVTES      = func(gID string) string { return GUILDS + gID + "/invites" }
	GUILD_CHANNELS     = func(gID string) string { return GUILDS + gID + "/channels" }
	GUILD_MEMBERS      = func(gID string) string { return GUILDS + gID + "/members" }
	GUILD_MEMBER       = func(gID, uID string) string { return GUILDS + gID + "/members/" + uID }
	GUILD_BANS         = func(gID string) string { return GUILDS + gID + "/bans" }
	GUILD_BAN          = func(gID, uID string) string { return GUILDS + gID + "/bans/" + uID }
	GUILD_INTEGRATIONS = func(gID string) string { return GUILDS + gID + "/integrations" }
	GUILD_ROLES        = func(gID string) string { return GUILDS + gID + "/roles" }
	GUILD_ROLE         = func(gID, rID string) string { return GUILDS + gID + "/roles/" + rID }
	GUILD_INVITES      = func(gID string) string { return GUILDS + gID + "/invites" }
	GUILD_EMBED        = func(gID string) string { return GUILDS + gID + "/embed" }
	GUILD_PRUNE        = func(gID string) string { return GUILDS + gID + "/prune" }
	GUILD_ICON         = func(gID, hash string) string { return GUILDS + gID + "/icons/" + hash + ".jpg" }
	GUILD_SPLASH       = func(gID, hash string) string { return GUILDS + gID + "/splashes/" + hash + ".jpg" }

	CHANNEL             = func(cID string) string { return CHANNELS + cID }
	CHANNEL_PERMISSIONS = func(cID string) string { return CHANNELS + cID + "/permissions" }
	CHANNEL_PERMISSION  = func(cID, tID string) string { return CHANNELS + cID + "/permissions/" + tID }
	CHANNEL_INVITES     = func(cID string) string { return CHANNELS + cID + "/invites" }
	CHANNEL_TYPING      = func(cID string) string { return CHANNELS + cID + "/typing" }
	CHANNEL_MESSAGES    = func(cID string) string { return CHANNELS + cID + "/messages" }
	CHANNEL_MESSAGE     = func(cID, mID string) string { return CHANNELS + cID + "/messages/" + mID }
	CHANNEL_MESSAGE_ACK = func(cID, mID string) string { return CHANNELS + cID + "/messages/" + mID + "/ack" }

	INVITE = func(iID string) string { return API + "invite/" + iID }

	INTEGRATIONS_JOIN = func(iID string) string { return API + "integrations/" + iID + "/join" }

	EMOJI = func(eID string) string { return API + "emojis/" + eID + ".png" }
	EndpointStatus     = "https://status.discordapp.com/api/v2/"
	EndpointSm         = EndpointStatus + "scheduled-maintenances/"
	EndpointSmActive   = EndpointSm + "active.json"
	EndpointSmUpcoming = EndpointSm + "upcoming.json"

	EndpointDiscord    = "https://discordapp.com/"
	EndpointAPI        = EndpointDiscord + "api/v" + APIVersion + "/"
	EndpointGuilds     = EndpointAPI + "guilds/"
	EndpointChannels   = EndpointAPI + "channels/"
	EndpointUsers      = EndpointAPI + "users/"
	EndpointGateway    = EndpointAPI + "gateway"
	EndpointGatewayBot = EndpointGateway + "/bot"
	EndpointWebhooks   = EndpointAPI + "webhooks/"

	EndpointCDN             = "https://cdn.discordapp.com/"
	EndpointCDNAttachments  = EndpointCDN + "attachments/"
	EndpointCDNAvatars      = EndpointCDN + "avatars/"
	EndpointCDNIcons        = EndpointCDN + "icons/"
	EndpointCDNSplashes     = EndpointCDN + "splashes/"
	EndpointCDNChannelIcons = EndpointCDN + "channel-icons/"

	EndpointAuth           = EndpointAPI + "auth/"
	EndpointLogin          = EndpointAuth + "login"
	EndpointLogout         = EndpointAuth + "logout"
	EndpointVerify         = EndpointAuth + "verify"
	EndpointVerifyResend   = EndpointAuth + "verify/resend"
	EndpointForgotPassword = EndpointAuth + "forgot"
	EndpointResetPassword  = EndpointAuth + "reset"
	EndpointRegister       = EndpointAuth + "register"

	EndpointVoice        = EndpointAPI + "/voice/"
	EndpointVoiceRegions = EndpointVoice + "regions"
	EndpointVoiceIce     = EndpointVoice + "ice"

	EndpointTutorial           = EndpointAPI + "tutorial/"
	EndpointTutorialIndicators = EndpointTutorial + "indicators"

	EndpointTrack        = EndpointAPI + "track"
	EndpointSso          = EndpointAPI + "sso"
	EndpointReport       = EndpointAPI + "report"
	EndpointIntegrations = EndpointAPI + "integrations"

	EndpointUser               = func(uID string) string { return EndpointUsers + uID }
	EndpointUserAvatar         = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
	EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" }
	EndpointUserSettings       = func(uID string) string { return EndpointUsers + uID + "/settings" }
	EndpointUserGuilds         = func(uID string) string { return EndpointUsers + uID + "/guilds" }
	EndpointUserGuild          = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
	EndpointUserGuildSettings  = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID + "/settings" }
	EndpointUserChannels       = func(uID string) string { return EndpointUsers + uID + "/channels" }
	EndpointUserDevices        = func(uID string) string { return EndpointUsers + uID + "/devices" }
	EndpointUserConnections    = func(uID string) string { return EndpointUsers + uID + "/connections" }
	EndpointUserNotes          = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }

	EndpointGuild                = func(gID string) string { return EndpointGuilds + gID }
	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 }
	EndpointGuildMemberRole      = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID }
	EndpointGuildBans            = func(gID string) string { return EndpointGuilds + gID + "/bans" }
	EndpointGuildBan             = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID }
	EndpointGuildIntegrations    = func(gID string) string { return EndpointGuilds + gID + "/integrations" }
	EndpointGuildIntegration     = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID }
	EndpointGuildIntegrationSync = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID + "/sync" }
	EndpointGuildRoles           = func(gID string) string { return EndpointGuilds + gID + "/roles" }
	EndpointGuildRole            = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID }
	EndpointGuildInvites         = func(gID string) string { return EndpointGuilds + gID + "/invites" }
	EndpointGuildEmbed           = func(gID string) string { return EndpointGuilds + gID + "/embed" }
	EndpointGuildPrune           = func(gID string) string { return EndpointGuilds + gID + "/prune" }
	EndpointGuildIcon            = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" }
	EndpointGuildSplash          = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" }
	EndpointGuildWebhooks        = func(gID string) string { return EndpointGuilds + gID + "/webhooks" }

	EndpointChannel                   = func(cID string) string { return EndpointChannels + cID }
	EndpointChannelPermissions        = func(cID string) string { return EndpointChannels + cID + "/permissions" }
	EndpointChannelPermission         = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID }
	EndpointChannelInvites            = func(cID string) string { return EndpointChannels + cID + "/invites" }
	EndpointChannelTyping             = func(cID string) string { return EndpointChannels + cID + "/typing" }
	EndpointChannelMessages           = func(cID string) string { return EndpointChannels + cID + "/messages" }
	EndpointChannelMessage            = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
	EndpointChannelMessageAck         = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" }
	EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk_delete" }
	EndpointChannelMessagesPins       = func(cID string) string { return EndpointChannel(cID) + "/pins" }
	EndpointChannelMessagePin         = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }

	EndpointGroupIcon = func(cID, hash string) string { return EndpointCDNChannelIcons + cID + "/" + hash + ".png" }

	EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" }
	EndpointWebhook         = func(wID string) string { return EndpointWebhooks + wID }
	EndpointWebhookToken    = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }

	EndpointMessageReactionsAll = func(cID, mID string) string {
		return EndpointChannelMessage(cID, mID) + "/reactions"
	}
	EndpointMessageReactions = func(cID, mID, eID string) string {
		return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
	}
	EndpointMessageReaction = func(cID, mID, eID, uID string) string {
		return EndpointMessageReactions(cID, mID, eID) + "/" + uID
	}

	EndpointRelationships       = func() string { return EndpointUsers + "@me" + "/relationships" }
	EndpointRelationship        = func(uID string) string { return EndpointRelationships() + "/" + uID }
	EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }

	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" }
)

A DiscordGo/event.go => DiscordGo/event.go +238 -0
@@ 0,0 1,238 @@
package discordgo

// EventHandler is an interface for Discord events.
type EventHandler interface {
	// Type returns the type of event this handler belongs to.
	Type() string

	// Handle is called whenever an event of Type() happens.
	// It is the recievers responsibility to type assert that the interface
	// is the expected struct.
	Handle(*Session, interface{})
}

// EventInterfaceProvider is an interface for providing empty interfaces for
// Discord events.
type EventInterfaceProvider interface {
	// Type is the type of event this handler belongs to.
	Type() string

	// New returns a new instance of the struct this event handler handles.
	// This is called once per event.
	// The struct is provided to all handlers of the same Type().
	New() interface{}
}

// interfaceEventType is the event handler type for interface{} events.
const interfaceEventType = "__INTERFACE__"

// interfaceEventHandler is an event handler for interface{} events.
type interfaceEventHandler func(*Session, interface{})

// Type returns the event type for interface{} events.
func (eh interfaceEventHandler) Type() string {
	return interfaceEventType
}

// Handle is the handler for an interface{} event.
func (eh interfaceEventHandler) Handle(s *Session, i interface{}) {
	eh(s, i)
}

var registeredInterfaceProviders = map[string]EventInterfaceProvider{}

// registerInterfaceProvider registers a provider so that DiscordGo can
// access it's New() method.
func registerInterfaceProvider(eh EventInterfaceProvider) {
	if _, ok := registeredInterfaceProviders[eh.Type()]; ok {
		return
		// XXX:
		// if we should error here, we need to do something with it.
		// fmt.Errorf("event %s already registered", eh.Type())
	}
	registeredInterfaceProviders[eh.Type()] = eh
	return
}

// eventHandlerInstance is a wrapper around an event handler, as functions
// cannot be compared directly.
type eventHandlerInstance struct {
	eventHandler EventHandler
}

// addEventHandler adds an event handler that will be fired anytime
// the Discord WSAPI matching eventHandler.Type() fires.
func (s *Session) addEventHandler(eventHandler EventHandler) func() {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	if s.handlers == nil {
		s.handlers = map[string][]*eventHandlerInstance{}
	}

	ehi := &eventHandlerInstance{eventHandler}
	s.handlers[eventHandler.Type()] = append(s.handlers[eventHandler.Type()], ehi)

	return func() {
		s.removeEventHandlerInstance(eventHandler.Type(), ehi)
	}
}

// addEventHandler adds an event handler that will be fired the next time
// the Discord WSAPI matching eventHandler.Type() fires.
func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	if s.onceHandlers == nil {
		s.onceHandlers = map[string][]*eventHandlerInstance{}
	}

	ehi := &eventHandlerInstance{eventHandler}
	s.onceHandlers[eventHandler.Type()] = append(s.onceHandlers[eventHandler.Type()], ehi)

	return func() {
		s.removeEventHandlerInstance(eventHandler.Type(), ehi)
	}
}

// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the function fires.
// events.go contains all the Discord WSAPI events that can be fired.
// 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() {
	eh := handlerForInterface(handler)

	if eh == nil {
		s.log(LogError, "Invalid handler type, handler will never be called")
		return func() {}
	}

	return s.addEventHandler(eh)
}

// AddHandlerOnce allows you to add an event handler that will be fired the next time
// the Discord WSAPI event that matches the function fires.
// See AddHandler for more details.
func (s *Session) AddHandlerOnce(handler interface{}) func() {
	eh := handlerForInterface(handler)

	if eh == nil {
		s.log(LogError, "Invalid handler type, handler will never be called")
		return func() {}
	}

	return s.addEventHandlerOnce(eh)
}

// removeEventHandler instance removes an event handler instance.
func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	handlers := s.handlers[t]
	for i := range handlers {
		if handlers[i] == ehi {
			s.handlers[t] = append(handlers[:i], handlers[i+1:]...)
		}
	}

	onceHandlers := s.onceHandlers[t]
	for i := range onceHandlers {
		if onceHandlers[i] == ehi {
			s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...)
		}
	}
}

// Handles calling permanent and once handlers for an event type.
func (s *Session) handle(t string, i interface{}) {
	for _, eh := range s.handlers[t] {
		if s.SyncEvents {
			eh.eventHandler.Handle(s, i)
		} else {
			go eh.eventHandler.Handle(s, i)
		}
	}

	if len(s.onceHandlers[t]) > 0 {
		for _, eh := range s.onceHandlers[t] {
			if s.SyncEvents {
				eh.eventHandler.Handle(s, i)
			} else {
				go eh.eventHandler.Handle(s, i)
			}
		}
		s.onceHandlers[t] = nil
	}
}

// Handles an event type by calling internal methods, firing handlers and firing the
// interface{} event.
func (s *Session) handleEvent(t string, i interface{}) {
	s.handlersMu.RLock()
	defer s.handlersMu.RUnlock()

	// All events are dispatched internally first.
	s.onInterface(i)

	// Then they are dispatched to anyone handling interface{} events.
	s.handle(interfaceEventType, i)

	// Finally they are dispatched to any typed handlers.
	s.handle(t, i)
}

// setGuildIds will set the GuildID on all the members of a guild.
// This is done as event data does not have it set.
func setGuildIds(g *Guild) {
	for _, c := range g.Channels {
		c.GuildID = g.ID
	}

	for _, m := range g.Members {
		m.GuildID = g.ID
	}

	for _, vs := range g.VoiceStates {
		vs.GuildID = g.ID
	}
}

// onInterface handles all internal events and routes them to the appropriate internal handler.
func (s *Session) onInterface(i interface{}) {
	switch t := i.(type) {
	case *Ready:
		for _, g := range t.Guilds {
			setGuildIds(g)
		}
		s.onReady(t)
	case *GuildCreate:
		setGuildIds(t.Guild)
	case *GuildUpdate:
		setGuildIds(t.Guild)
	case *VoiceServerUpdate:
		go s.onVoiceServerUpdate(t)
	case *VoiceStateUpdate:
		go s.onVoiceStateUpdate(t)
	}
	err := s.State.OnInterface(s, i)
	if err != nil {
		s.log(LogDebug, "error dispatching internal event, %s", err)
	}
}

// onReady handles the ready event.
func (s *Session) onReady(r *Ready) {

	// Store the SessionID within the Session struct.
	s.sessionID = r.SessionID
}

A DiscordGo/eventhandlers.go => DiscordGo/eventhandlers.go +1030 -0
@@ 0,0 1,1030 @@
// Code generated by \"eventhandlers\"; DO NOT EDIT
// See events.go

package discordgo

// Following are all the event types.
// Event type values are used to match the events returned by Discord.
// EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
const (
	channelCreateEventType            = "CHANNEL_CREATE"
	channelDeleteEventType            = "CHANNEL_DELETE"
	channelPinsUpdateEventType        = "CHANNEL_PINS_UPDATE"
	channelUpdateEventType            = "CHANNEL_UPDATE"
	connectEventType                  = "__CONNECT__"
	disconnectEventType               = "__DISCONNECT__"
	eventEventType                    = "__EVENT__"
	guildBanAddEventType              = "GUILD_BAN_ADD"
	guildBanRemoveEventType           = "GUILD_BAN_REMOVE"
	guildCreateEventType              = "GUILD_CREATE"
	guildDeleteEventType              = "GUILD_DELETE"
	guildEmojisUpdateEventType        = "GUILD_EMOJIS_UPDATE"
	guildIntegrationsUpdateEventType  = "GUILD_INTEGRATIONS_UPDATE"
	guildMemberAddEventType           = "GUILD_MEMBER_ADD"
	guildMemberRemoveEventType        = "GUILD_MEMBER_REMOVE"
	guildMemberUpdateEventType        = "GUILD_MEMBER_UPDATE"
	guildMembersChunkEventType        = "GUILD_MEMBERS_CHUNK"
	guildRoleCreateEventType          = "GUILD_ROLE_CREATE"
	guildRoleDeleteEventType          = "GUILD_ROLE_DELETE"
	guildRoleUpdateEventType          = "GUILD_ROLE_UPDATE"
	guildUpdateEventType              = "GUILD_UPDATE"
	messageAckEventType               = "MESSAGE_ACK"
	messageCreateEventType            = "MESSAGE_CREATE"
	messageDeleteEventType            = "MESSAGE_DELETE"
	messageDeleteBulkEventType        = "MESSAGE_DELETE_BULK"
	messageReactionAddEventType       = "MESSAGE_REACTION_ADD"
	messageReactionRemoveEventType    = "MESSAGE_REACTION_REMOVE"
	messageReactionRemoveAllEventType = "MESSAGE_REACTION_REMOVE_ALL"
	messageUpdateEventType            = "MESSAGE_UPDATE"
	presenceUpdateEventType           = "PRESENCE_UPDATE"
	presencesReplaceEventType         = "PRESENCES_REPLACE"
	rateLimitEventType                = "__RATE_LIMIT__"
	readyEventType                    = "READY"
	relationshipAddEventType          = "RELATIONSHIP_ADD"
	relationshipRemoveEventType       = "RELATIONSHIP_REMOVE"
	resumedEventType                  = "RESUMED"
	typingStartEventType              = "TYPING_START"
	userGuildSettingsUpdateEventType  = "USER_GUILD_SETTINGS_UPDATE"
	userNoteUpdateEventType           = "USER_NOTE_UPDATE"
	userSettingsUpdateEventType       = "USER_SETTINGS_UPDATE"
	userUpdateEventType               = "USER_UPDATE"
	voiceServerUpdateEventType        = "VOICE_SERVER_UPDATE"
	voiceStateUpdateEventType         = "VOICE_STATE_UPDATE"
)

// channelCreateEventHandler is an event handler for ChannelCreate events.
type channelCreateEventHandler func(*Session, *ChannelCreate)

// Type returns the event type for ChannelCreate events.
func (eh channelCreateEventHandler) Type() string {
	return channelCreateEventType
}

// New returns a new instance of ChannelCreate.
func (eh channelCreateEventHandler) New() interface{} {
	return &ChannelCreate{}
}

// Handle is the handler for ChannelCreate events.
func (eh channelCreateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*ChannelCreate); ok {
		eh(s, t)
	}
}

// channelDeleteEventHandler is an event handler for ChannelDelete events.
type channelDeleteEventHandler func(*Session, *ChannelDelete)

// Type returns the event type for ChannelDelete events.
func (eh channelDeleteEventHandler) Type() string {
	return channelDeleteEventType
}

// New returns a new instance of ChannelDelete.
func (eh channelDeleteEventHandler) New() interface{} {
	return &ChannelDelete{}
}

// Handle is the handler for ChannelDelete events.
func (eh channelDeleteEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*ChannelDelete); ok {
		eh(s, t)
	}
}

// channelPinsUpdateEventHandler is an event handler for ChannelPinsUpdate events.
type channelPinsUpdateEventHandler func(*Session, *ChannelPinsUpdate)

// Type returns the event type for ChannelPinsUpdate events.
func (eh channelPinsUpdateEventHandler) Type() string {
	return channelPinsUpdateEventType
}

// New returns a new instance of ChannelPinsUpdate.
func (eh channelPinsUpdateEventHandler) New() interface{} {
	return &ChannelPinsUpdate{}
}

// Handle is the handler for ChannelPinsUpdate events.
func (eh channelPinsUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*ChannelPinsUpdate); ok {
		eh(s, t)
	}
}

// channelUpdateEventHandler is an event handler for ChannelUpdate events.
type channelUpdateEventHandler func(*Session, *ChannelUpdate)

// Type returns the event type for ChannelUpdate events.
func (eh channelUpdateEventHandler) Type() string {
	return channelUpdateEventType
}

// New returns a new instance of ChannelUpdate.
func (eh channelUpdateEventHandler) New() interface{} {
	return &ChannelUpdate{}
}

// Handle is the handler for ChannelUpdate events.
func (eh channelUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*ChannelUpdate); ok {
		eh(s, t)
	}
}

// connectEventHandler is an event handler for Connect events.
type connectEventHandler func(*Session, *Connect)

// Type returns the event type for Connect events.
func (eh connectEventHandler) Type() string {
	return connectEventType
}

// Handle is the handler for Connect events.
func (eh connectEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*Connect); ok {
		eh(s, t)
	}
}

// disconnectEventHandler is an event handler for Disconnect events.
type disconnectEventHandler func(*Session, *Disconnect)

// Type returns the event type for Disconnect events.
func (eh disconnectEventHandler) Type() string {
	return disconnectEventType
}

// Handle is the handler for Disconnect events.
func (eh disconnectEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*Disconnect); ok {
		eh(s, t)
	}
}

// eventEventHandler is an event handler for Event events.
type eventEventHandler func(*Session, *Event)

// Type returns the event type for Event events.
func (eh eventEventHandler) Type() string {
	return eventEventType
}

// Handle is the handler for Event events.
func (eh eventEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*Event); ok {
		eh(s, t)
	}
}

// guildBanAddEventHandler is an event handler for GuildBanAdd events.
type guildBanAddEventHandler func(*Session, *GuildBanAdd)

// Type returns the event type for GuildBanAdd events.
func (eh guildBanAddEventHandler) Type() string {
	return guildBanAddEventType
}

// New returns a new instance of GuildBanAdd.
func (eh guildBanAddEventHandler) New() interface{} {
	return &GuildBanAdd{}
}

// Handle is the handler for GuildBanAdd events.
func (eh guildBanAddEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildBanAdd); ok {
		eh(s, t)
	}
}

// guildBanRemoveEventHandler is an event handler for GuildBanRemove events.
type guildBanRemoveEventHandler func(*Session, *GuildBanRemove)

// Type returns the event type for GuildBanRemove events.
func (eh guildBanRemoveEventHandler) Type() string {
	return guildBanRemoveEventType
}

// New returns a new instance of GuildBanRemove.
func (eh guildBanRemoveEventHandler) New() interface{} {
	return &GuildBanRemove{}
}

// Handle is the handler for GuildBanRemove events.
func (eh guildBanRemoveEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildBanRemove); ok {
		eh(s, t)
	}
}

// guildCreateEventHandler is an event handler for GuildCreate events.
type guildCreateEventHandler func(*Session, *GuildCreate)

// Type returns the event type for GuildCreate events.
func (eh guildCreateEventHandler) Type() string {
	return guildCreateEventType
}

// New returns a new instance of GuildCreate.
func (eh guildCreateEventHandler) New() interface{} {
	return &GuildCreate{}
}

// Handle is the handler for GuildCreate events.
func (eh guildCreateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildCreate); ok {
		eh(s, t)
	}
}

// guildDeleteEventHandler is an event handler for GuildDelete events.
type guildDeleteEventHandler func(*Session, *GuildDelete)

// Type returns the event type for GuildDelete events.
func (eh guildDeleteEventHandler) Type() string {
	return guildDeleteEventType
}

// New returns a new instance of GuildDelete.
func (eh guildDeleteEventHandler) New() interface{} {
	return &GuildDelete{}
}

// Handle is the handler for GuildDelete events.
func (eh guildDeleteEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildDelete); ok {
		eh(s, t)
	}
}

// guildEmojisUpdateEventHandler is an event handler for GuildEmojisUpdate events.
type guildEmojisUpdateEventHandler func(*Session, *GuildEmojisUpdate)

// Type returns the event type for GuildEmojisUpdate events.
func (eh guildEmojisUpdateEventHandler) Type() string {
	return guildEmojisUpdateEventType
}

// New returns a new instance of GuildEmojisUpdate.
func (eh guildEmojisUpdateEventHandler) New() interface{} {
	return &GuildEmojisUpdate{}
}

// Handle is the handler for GuildEmojisUpdate events.
func (eh guildEmojisUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildEmojisUpdate); ok {
		eh(s, t)
	}
}

// guildIntegrationsUpdateEventHandler is an event handler for GuildIntegrationsUpdate events.
type guildIntegrationsUpdateEventHandler func(*Session, *GuildIntegrationsUpdate)

// Type returns the event type for GuildIntegrationsUpdate events.
func (eh guildIntegrationsUpdateEventHandler) Type() string {
	return guildIntegrationsUpdateEventType
}

// New returns a new instance of GuildIntegrationsUpdate.
func (eh guildIntegrationsUpdateEventHandler) New() interface{} {
	return &GuildIntegrationsUpdate{}
}

// Handle is the handler for GuildIntegrationsUpdate events.
func (eh guildIntegrationsUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildIntegrationsUpdate); ok {
		eh(s, t)
	}
}

// guildMemberAddEventHandler is an event handler for GuildMemberAdd events.
type guildMemberAddEventHandler func(*Session, *GuildMemberAdd)

// Type returns the event type for GuildMemberAdd events.
func (eh guildMemberAddEventHandler) Type() string {
	return guildMemberAddEventType
}

// New returns a new instance of GuildMemberAdd.
func (eh guildMemberAddEventHandler) New() interface{} {
	return &GuildMemberAdd{}
}

// Handle is the handler for GuildMemberAdd events.
func (eh guildMemberAddEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildMemberAdd); ok {
		eh(s, t)
	}
}

// guildMemberRemoveEventHandler is an event handler for GuildMemberRemove events.
type guildMemberRemoveEventHandler func(*Session, *GuildMemberRemove)

// Type returns the event type for GuildMemberRemove events.
func (eh guildMemberRemoveEventHandler) Type() string {
	return guildMemberRemoveEventType
}

// New returns a new instance of GuildMemberRemove.
func (eh guildMemberRemoveEventHandler) New() interface{} {
	return &GuildMemberRemove{}
}

// Handle is the handler for GuildMemberRemove events.
func (eh guildMemberRemoveEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildMemberRemove); ok {
		eh(s, t)
	}
}

// guildMemberUpdateEventHandler is an event handler for GuildMemberUpdate events.
type guildMemberUpdateEventHandler func(*Session, *GuildMemberUpdate)

// Type returns the event type for GuildMemberUpdate events.
func (eh guildMemberUpdateEventHandler) Type() string {
	return guildMemberUpdateEventType
}

// New returns a new instance of GuildMemberUpdate.
func (eh guildMemberUpdateEventHandler) New() interface{} {
	return &GuildMemberUpdate{}
}

// Handle is the handler for GuildMemberUpdate events.
func (eh guildMemberUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildMemberUpdate); ok {
		eh(s, t)
	}
}

// guildMembersChunkEventHandler is an event handler for GuildMembersChunk events.
type guildMembersChunkEventHandler func(*Session, *GuildMembersChunk)

// Type returns the event type for GuildMembersChunk events.
func (eh guildMembersChunkEventHandler) Type() string {
	return guildMembersChunkEventType
}

// New returns a new instance of GuildMembersChunk.
func (eh guildMembersChunkEventHandler) New() interface{} {
	return &GuildMembersChunk{}
}

// Handle is the handler for GuildMembersChunk events.
func (eh guildMembersChunkEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildMembersChunk); ok {
		eh(s, t)
	}
}

// guildRoleCreateEventHandler is an event handler for GuildRoleCreate events.
type guildRoleCreateEventHandler func(*Session, *GuildRoleCreate)

// Type returns the event type for GuildRoleCreate events.
func (eh guildRoleCreateEventHandler) Type() string {
	return guildRoleCreateEventType
}

// New returns a new instance of GuildRoleCreate.
func (eh guildRoleCreateEventHandler) New() interface{} {
	return &GuildRoleCreate{}
}

// Handle is the handler for GuildRoleCreate events.
func (eh guildRoleCreateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildRoleCreate); ok {
		eh(s, t)
	}
}

// guildRoleDeleteEventHandler is an event handler for GuildRoleDelete events.
type guildRoleDeleteEventHandler func(*Session, *GuildRoleDelete)

// Type returns the event type for GuildRoleDelete events.
func (eh guildRoleDeleteEventHandler) Type() string {
	return guildRoleDeleteEventType
}

// New returns a new instance of GuildRoleDelete.
func (eh guildRoleDeleteEventHandler) New() interface{} {
	return &GuildRoleDelete{}
}

// Handle is the handler for GuildRoleDelete events.
func (eh guildRoleDeleteEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildRoleDelete); ok {
		eh(s, t)
	}
}

// guildRoleUpdateEventHandler is an event handler for GuildRoleUpdate events.
type guildRoleUpdateEventHandler func(*Session, *GuildRoleUpdate)

// Type returns the event type for GuildRoleUpdate events.
func (eh guildRoleUpdateEventHandler) Type() string {
	return guildRoleUpdateEventType
}

// New returns a new instance of GuildRoleUpdate.
func (eh guildRoleUpdateEventHandler) New() interface{} {
	return &GuildRoleUpdate{}
}

// Handle is the handler for GuildRoleUpdate events.
func (eh guildRoleUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildRoleUpdate); ok {
		eh(s, t)
	}
}

// guildUpdateEventHandler is an event handler for GuildUpdate events.
type guildUpdateEventHandler func(*Session, *GuildUpdate)

// Type returns the event type for GuildUpdate events.
func (eh guildUpdateEventHandler) Type() string {
	return guildUpdateEventType
}

// New returns a new instance of GuildUpdate.
func (eh guildUpdateEventHandler) New() interface{} {
	return &GuildUpdate{}
}

// Handle is the handler for GuildUpdate events.
func (eh guildUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*GuildUpdate); ok {
		eh(s, t)
	}
}

// messageAckEventHandler is an event handler for MessageAck events.
type messageAckEventHandler func(*Session, *MessageAck)

// Type returns the event type for MessageAck events.
func (eh messageAckEventHandler) Type() string {
	return messageAckEventType
}

// New returns a new instance of MessageAck.
func (eh messageAckEventHandler) New() interface{} {
	return &MessageAck{}
}

// Handle is the handler for MessageAck events.
func (eh messageAckEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*MessageAck); ok {
		eh(s, t)
	}
}

// messageCreateEventHandler is an event handler for MessageCreate events.
type messageCreateEventHandler func(*Session, *MessageCreate)

// Type returns the event type for MessageCreate events.
func (eh messageCreateEventHandler) Type() string {
	return messageCreateEventType
}

// New returns a new instance of MessageCreate.
func (eh messageCreateEventHandler) New() interface{} {
	return &MessageCreate{}
}

// Handle is the handler for MessageCreate events.
func (eh messageCreateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*MessageCreate); ok {
		eh(s, t)
	}
}

// messageDeleteEventHandler is an event handler for MessageDelete events.
type messageDeleteEventHandler func(*Session, *MessageDelete)

// Type returns the event type for MessageDelete events.
func (eh messageDeleteEventHandler) Type() string {
	return messageDeleteEventType
}

// New returns a new instance of MessageDelete.
func (eh messageDeleteEventHandler) New() interface{} {
	return &MessageDelete{}
}

// Handle is the handler for MessageDelete events.
func (eh messageDeleteEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*MessageDelete); ok {
		eh(s, t)
	}
}

// messageDeleteBulkEventHandler is an event handler for MessageDeleteBulk events.
type messageDeleteBulkEventHandler func(*Session, *MessageDeleteBulk)

// Type returns the event type for MessageDeleteBulk events.
func (eh messageDeleteBulkEventHandler) Type() string {
	return messageDeleteBulkEventType
}

// New returns a new instance of MessageDeleteBulk.
func (eh messageDeleteBulkEventHandler) New() interface{} {
	return &MessageDeleteBulk{}
}

// Handle is the handler for MessageDeleteBulk events.
func (eh messageDeleteBulkEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*MessageDeleteBulk); ok {
		eh(s, t)
	}
}

// messageReactionAddEventHandler is an event handler for MessageReactionAdd events.
type messageReactionAddEventHandler func(*Session, *MessageReactionAdd)

// Type returns the event type for MessageReactionAdd events.
func (eh messageReactionAddEventHandler) Type() string {
	return messageReactionAddEventType
}

// New returns a new instance of MessageReactionAdd.
func (eh messageReactionAddEventHandler) New() interface{} {
	return &MessageReactionAdd{}
}

// Handle is the handler for MessageReactionAdd events.
func (eh messageReactionAddEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*MessageReactionAdd); ok {
		eh(s, t)
	}
}

// messageReactionRemoveEventHandler is an event handler for MessageReactionRemove events.
type messageReactionRemoveEventHandler func(*Session, *MessageReactionRemove)

// Type returns the event type for MessageReactionRemove events.
func (eh messageReactionRemoveEventHandler) Type() string {
	return messageReactionRemoveEventType
}

// New returns a new instance of MessageReactionRemove.
func (eh messageReactionRemoveEventHandler) New() interface{} {
	return &MessageReactionRemove{}
}

// Handle is the handler for MessageReactionRemove events.
func (eh messageReactionRemoveEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*MessageReactionRemove); ok {
		eh(s, t)
	}
}

// messageReactionRemoveAllEventHandler is an event handler for MessageReactionRemoveAll events.
type messageReactionRemoveAllEventHandler func(*Session, *MessageReactionRemoveAll)

// Type returns the event type for MessageReactionRemoveAll events.
func (eh messageReactionRemoveAllEventHandler) Type() string {
	return messageReactionRemoveAllEventType
}

// New returns a new instance of MessageReactionRemoveAll.
func (eh messageReactionRemoveAllEventHandler) New() interface{} {
	return &MessageReactionRemoveAll{}
}

// Handle is the handler for MessageReactionRemoveAll events.
func (eh messageReactionRemoveAllEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*MessageReactionRemoveAll); ok {
		eh(s, t)
	}
}

// messageUpdateEventHandler is an event handler for MessageUpdate events.
type messageUpdateEventHandler func(*Session, *MessageUpdate)

// Type returns the event type for MessageUpdate events.
func (eh messageUpdateEventHandler) Type() string {
	return messageUpdateEventType
}

// New returns a new instance of MessageUpdate.
func (eh messageUpdateEventHandler) New() interface{} {
	return &MessageUpdate{}
}

// Handle is the handler for MessageUpdate events.
func (eh messageUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*MessageUpdate); ok {
		eh(s, t)
	}
}

// presenceUpdateEventHandler is an event handler for PresenceUpdate events.
type presenceUpdateEventHandler func(*Session, *PresenceUpdate)

// Type returns the event type for PresenceUpdate events.
func (eh presenceUpdateEventHandler) Type() string {
	return presenceUpdateEventType
}

// New returns a new instance of PresenceUpdate.
func (eh presenceUpdateEventHandler) New() interface{} {
	return &PresenceUpdate{}
}

// Handle is the handler for PresenceUpdate events.
func (eh presenceUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*PresenceUpdate); ok {
		eh(s, t)
	}
}

// presencesReplaceEventHandler is an event handler for PresencesReplace events.
type presencesReplaceEventHandler func(*Session, *PresencesReplace)

// Type returns the event type for PresencesReplace events.
func (eh presencesReplaceEventHandler) Type() string {
	return presencesReplaceEventType
}

// New returns a new instance of PresencesReplace.
func (eh presencesReplaceEventHandler) New() interface{} {
	return &PresencesReplace{}
}

// Handle is the handler for PresencesReplace events.
func (eh presencesReplaceEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*PresencesReplace); ok {
		eh(s, t)
	}
}

// rateLimitEventHandler is an event handler for RateLimit events.
type rateLimitEventHandler func(*Session, *RateLimit)

// Type returns the event type for RateLimit events.
func (eh rateLimitEventHandler) Type() string {
	return rateLimitEventType
}

// Handle is the handler for RateLimit events.
func (eh rateLimitEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*RateLimit); ok {
		eh(s, t)
	}
}

// readyEventHandler is an event handler for Ready events.
type readyEventHandler func(*Session, *Ready)

// Type returns the event type for Ready events.
func (eh readyEventHandler) Type() string {
	return readyEventType
}

// New returns a new instance of Ready.
func (eh readyEventHandler) New() interface{} {
	return &Ready{}
}

// Handle is the handler for Ready events.
func (eh readyEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*Ready); ok {
		eh(s, t)
	}
}

// relationshipAddEventHandler is an event handler for RelationshipAdd events.
type relationshipAddEventHandler func(*Session, *RelationshipAdd)

// Type returns the event type for RelationshipAdd events.
func (eh relationshipAddEventHandler) Type() string {
	return relationshipAddEventType
}

// New returns a new instance of RelationshipAdd.
func (eh relationshipAddEventHandler) New() interface{} {
	return &RelationshipAdd{}
}

// Handle is the handler for RelationshipAdd events.
func (eh relationshipAddEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*RelationshipAdd); ok {
		eh(s, t)
	}
}

// relationshipRemoveEventHandler is an event handler for RelationshipRemove events.
type relationshipRemoveEventHandler func(*Session, *RelationshipRemove)

// Type returns the event type for RelationshipRemove events.
func (eh relationshipRemoveEventHandler) Type() string {
	return relationshipRemoveEventType
}

// New returns a new instance of RelationshipRemove.
func (eh relationshipRemoveEventHandler) New() interface{} {
	return &RelationshipRemove{}
}

// Handle is the handler for RelationshipRemove events.
func (eh relationshipRemoveEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*RelationshipRemove); ok {
		eh(s, t)
	}
}

// resumedEventHandler is an event handler for Resumed events.
type resumedEventHandler func(*Session, *Resumed)

// Type returns the event type for Resumed events.
func (eh resumedEventHandler) Type() string {
	return resumedEventType
}

// New returns a new instance of Resumed.
func (eh resumedEventHandler) New() interface{} {
	return &Resumed{}
}

// Handle is the handler for Resumed events.
func (eh resumedEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*Resumed); ok {
		eh(s, t)
	}
}

// typingStartEventHandler is an event handler for TypingStart events.
type typingStartEventHandler func(*Session, *TypingStart)

// Type returns the event type for TypingStart events.
func (eh typingStartEventHandler) Type() string {
	return typingStartEventType
}

// New returns a new instance of TypingStart.
func (eh typingStartEventHandler) New() interface{} {
	return &TypingStart{}
}

// Handle is the handler for TypingStart events.
func (eh typingStartEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*TypingStart); ok {
		eh(s, t)
	}
}

// userGuildSettingsUpdateEventHandler is an event handler for UserGuildSettingsUpdate events.
type userGuildSettingsUpdateEventHandler func(*Session, *UserGuildSettingsUpdate)

// Type returns the event type for UserGuildSettingsUpdate events.
func (eh userGuildSettingsUpdateEventHandler) Type() string {
	return userGuildSettingsUpdateEventType
}

// New returns a new instance of UserGuildSettingsUpdate.
func (eh userGuildSettingsUpdateEventHandler) New() interface{} {
	return &UserGuildSettingsUpdate{}
}

// Handle is the handler for UserGuildSettingsUpdate events.
func (eh userGuildSettingsUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*UserGuildSettingsUpdate); ok {
		eh(s, t)
	}
}

// userNoteUpdateEventHandler is an event handler for UserNoteUpdate events.
type userNoteUpdateEventHandler func(*Session, *UserNoteUpdate)

// Type returns the event type for UserNoteUpdate events.
func (eh userNoteUpdateEventHandler) Type() string {
	return userNoteUpdateEventType
}

// New returns a new instance of UserNoteUpdate.
func (eh userNoteUpdateEventHandler) New() interface{} {
	return &UserNoteUpdate{}
}

// Handle is the handler for UserNoteUpdate events.
func (eh userNoteUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*UserNoteUpdate); ok {
		eh(s, t)
	}
}

// userSettingsUpdateEventHandler is an event handler for UserSettingsUpdate events.
type userSettingsUpdateEventHandler func(*Session, *UserSettingsUpdate)

// Type returns the event type for UserSettingsUpdate events.
func (eh userSettingsUpdateEventHandler) Type() string {
	return userSettingsUpdateEventType
}

// New returns a new instance of UserSettingsUpdate.
func (eh userSettingsUpdateEventHandler) New() interface{} {
	return &UserSettingsUpdate{}
}

// Handle is the handler for UserSettingsUpdate events.
func (eh userSettingsUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*UserSettingsUpdate); ok {
		eh(s, t)
	}
}

// userUpdateEventHandler is an event handler for UserUpdate events.
type userUpdateEventHandler func(*Session, *UserUpdate)

// Type returns the event type for UserUpdate events.
func (eh userUpdateEventHandler) Type() string {
	return userUpdateEventType
}

// New returns a new instance of UserUpdate.
func (eh userUpdateEventHandler) New() interface{} {
	return &UserUpdate{}
}

// Handle is the handler for UserUpdate events.
func (eh userUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*UserUpdate); ok {
		eh(s, t)
	}
}

// voiceServerUpdateEventHandler is an event handler for VoiceServerUpdate events.
type voiceServerUpdateEventHandler func(*Session, *VoiceServerUpdate)

// Type returns the event type for VoiceServerUpdate events.
func (eh voiceServerUpdateEventHandler) Type() string {
	return voiceServerUpdateEventType
}

// New returns a new instance of VoiceServerUpdate.
func (eh voiceServerUpdateEventHandler) New() interface{} {
	return &VoiceServerUpdate{}
}

// Handle is the handler for VoiceServerUpdate events.
func (eh voiceServerUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*VoiceServerUpdate); ok {
		eh(s, t)
	}
}

// voiceStateUpdateEventHandler is an event handler for VoiceStateUpdate events.
type voiceStateUpdateEventHandler func(*Session, *VoiceStateUpdate)

// Type returns the event type for VoiceStateUpdate events.
func (eh voiceStateUpdateEventHandler) Type() string {
	return voiceStateUpdateEventType
}

// New returns a new instance of VoiceStateUpdate.
func (eh voiceStateUpdateEventHandler) New() interface{} {
	return &VoiceStateUpdate{}
}

// Handle is the handler for VoiceStateUpdate events.
func (eh voiceStateUpdateEventHandler) Handle(s *Session, i interface{}) {
	if t, ok := i.(*VoiceStateUpdate); ok {
		eh(s, t)
	}
}

func handlerForInterface(handler interface{}) EventHandler {
	switch v := handler.(type) {
	case func(*Session, interface{}):
		return interfaceEventHandler(v)
	case func(*Session, *ChannelCreate):
		return channelCreateEventHandler(v)
	case func(*Session, *ChannelDelete):
		return channelDeleteEventHandler(v)
	case func(*Session, *ChannelPinsUpdate):
		return channelPinsUpdateEventHandler(v)
	case func(*Session, *ChannelUpdate):
		return channelUpdateEventHandler(v)
	case func(*Session, *Connect):
		return connectEventHandler(v)
	case func(*Session, *Disconnect):
		return disconnectEventHandler(v)
	case func(*Session, *Event):
		return eventEventHandler(v)
	case func(*Session, *GuildBanAdd):
		return guildBanAddEventHandler(v)
	case func(*Session, *GuildBanRemove):
		return guildBanRemoveEventHandler(v)
	case func(*Session, *GuildCreate):
		return guildCreateEventHandler(v)
	case func(*Session, *GuildDelete):
		return guildDeleteEventHandler(v)
	case func(*Session, *GuildEmojisUpdate):
		return guildEmojisUpdateEventHandler(v)
	case func(*Session, *GuildIntegrationsUpdate):
		return guildIntegrationsUpdateEventHandler(v)
	case func(*Session, *GuildMemberAdd):
		return guildMemberAddEventHandler(v)
	case func(*Session, *GuildMemberRemove):
		return guildMemberRemoveEventHandler(v)
	case func(*Session, *GuildMemberUpdate):
		return guildMemberUpdateEventHandler(v)
	case func(*Session, *GuildMembersChunk):
		return guildMembersChunkEventHandler(v)
	case func(*Session, *GuildRoleCreate):
		return guildRoleCreateEventHandler(v)
	case func(*Session, *GuildRoleDelete):
		return guildRoleDeleteEventHandler(v)
	case func(*Session, *GuildRoleUpdate):
		return guildRoleUpdateEventHandler(v)
	case func(*Session, *GuildUpdate):
		return guildUpdateEventHandler(v)
	case func(*Session, *MessageAck):
		return messageAckEventHandler(v)
	case func(*Session, *MessageCreate):
		return messageCreateEventHandler(v)
	case func(*Session, *MessageDelete):
		return messageDeleteEventHandler(v)
	case func(*Session, *MessageDeleteBulk):
		return messageDeleteBulkEventHandler(v)
	case func(*Session, *MessageReactionAdd):
		return messageReactionAddEventHandler(v)
	case func(*Session, *MessageReactionRemove):
		return messageReactionRemoveEventHandler(v)
	case func(*Session, *MessageReactionRemoveAll):
		return messageReactionRemoveAllEventHandler(v)
	case func(*Session, *MessageUpdate):
		return messageUpdateEventHandler(v)
	case func(*Session, *PresenceUpdate):
		return presenceUpdateEventHandler(v)
	case func(*Session, *PresencesReplace):
		return presencesReplaceEventHandler(v)
	case func(*Session, *RateLimit):
		return rateLimitEventHandler(v)
	case func(*Session, *Ready):
		return readyEventHandler(v)
	case func(*Session, *RelationshipAdd):
		return relationshipAddEventHandler(v)
	case func(*Session, *RelationshipRemove):
		return relationshipRemoveEventHandler(v)
	case func(*Session, *Resumed):
		return resumedEventHandler(v)
	case func(*Session, *TypingStart):
		return typingStartEventHandler(v)
	case func(*Session, *UserGuildSettingsUpdate):
		return userGuildSettingsUpdateEventHandler(v)
	case func(*Session, *UserNoteUpdate):
		return userNoteUpdateEventHandler(v)
	case func(*Session, *UserSettingsUpdate):
		return userSettingsUpdateEventHandler(v)
	case func(*Session, *UserUpdate):
		return userUpdateEventHandler(v)
	case func(*Session, *VoiceServerUpdate):
		return voiceServerUpdateEventHandler(v)
	case func(*Session, *VoiceStateUpdate):
		return voiceStateUpdateEventHandler(v)
	}

	return nil
}

func init() {
	registerInterfaceProvider(channelCreateEventHandler(nil))
	registerInterfaceProvider(channelDeleteEventHandler(nil))
	registerInterfaceProvider(channelPinsUpdateEventHandler(nil))
	registerInterfaceProvider(channelUpdateEventHandler(nil))
	registerInterfaceProvider(guildBanAddEventHandler(nil))
	registerInterfaceProvider(guildBanRemoveEventHandler(nil))
	registerInterfaceProvider(guildCreateEventHandler(nil))
	registerInterfaceProvider(guildDeleteEventHandler(nil))
	registerInterfaceProvider(guildEmojisUpdateEventHandler(nil))
	registerInterfaceProvider(guildIntegrationsUpdateEventHandler(nil))
	registerInterfaceProvider(guildMemberAddEventHandler(nil))
	registerInterfaceProvider(guildMemberRemoveEventHandler(nil))
	registerInterfaceProvider(guildMemberUpdateEventHandler(nil))
	registerInterfaceProvider(guildMembersChunkEventHandler(nil))
	registerInterfaceProvider(guildRoleCreateEventHandler(nil))
	registerInterfaceProvider(guildRoleDeleteEventHandler(nil))
	registerInterfaceProvider(guildRoleUpdateEventHandler(nil))
	registerInterfaceProvider(guildUpdateEventHandler(nil))
	registerInterfaceProvider(messageAckEventHandler(nil))
	registerInterfaceProvider(messageCreateEventHandler(nil))
	registerInterfaceProvider(messageDeleteEventHandler(nil))
	registerInterfaceProvider(messageDeleteBulkEventHandler(nil))
	registerInterfaceProvider(messageReactionAddEventHandler(nil))
	registerInterfaceProvider(messageReactionRemoveEventHandler(nil))
	registerInterfaceProvider(messageReactionRemoveAllEventHandler(nil))
	registerInterfaceProvider(messageUpdateEventHandler(nil))
	registerInterfaceProvider(presenceUpdateEventHandler(nil))
	registerInterfaceProvider(presencesReplaceEventHandler(nil))
	registerInterfaceProvider(readyEventHandler(nil))
	registerInterfaceProvider(relationshipAddEventHandler(nil))
	registerInterfaceProvider(relationshipRemoveEventHandler(nil))
	registerInterfaceProvider(resumedEventHandler(nil))
	registerInterfaceProvider(typingStartEventHandler(nil))
	registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil))
	registerInterfaceProvider(userNoteUpdateEventHandler(nil))
	registerInterfaceProvider(userSettingsUpdateEventHandler(nil))
	registerInterfaceProvider(userUpdateEventHandler(nil))
	registerInterfaceProvider(voiceServerUpdateEventHandler(nil))
	registerInterfaceProvider(voiceStateUpdateEventHandler(nil))
}

M DiscordGo/events.go => DiscordGo/events.go +191 -88
@@ 1,150 1,253 @@
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{},
	"READY":                     Ready{},
	"USER_UPDATE":               UserUpdate{},
	"USER_SETTINGS_UPDATE":      UserSettingsUpdate{},
	"TYPING_START":              TypingStart{},
	"VOICE_SERVER_UPDATE":       VoiceServerUpdate{},
	"VOICE_STATE_UPDATE":        VoiceStateUpdate{},
	"MESSAGE_REACTION_ADD":      ReactionRemove{},
	"MESSAGE_REACTION_REMOVE":   ReactionAdd{},
}

// ReactionRemove is an empty struct for an event.
type ReactionRemove struct{}

// ReactionAdd is an empty struct for an event.
type ReactionAdd struct{}

// Connect is an empty struct for an event.
type Connect struct{}
import (
	"encoding/json"
)

// Disconnect is an empty struct for an event.
type Disconnect struct{}
// This file contains all the possible structs that can be
// handled by AddHandler/EventHandler.
// DO NOT ADD ANYTHING BUT EVENT HANDLER STRUCTS TO THIS FILE.
//go:generate go run tools/cmd/eventhandlers/main.go

// MessageCreate is a wrapper struct for an event.
type MessageCreate struct {
	*Message
}

// MessageUpdate is a wrapper struct for an event.
type MessageUpdate struct {
	*Message
}
// Connect is the data for a Connect event.
// This is a sythetic event and is not dispatched by Discord.
type Connect struct{}

// MessageDelete is a wrapper struct for an event.
type MessageDelete struct {
	*Message
}
// Disconnect is the data for a Disconnect event.
// This is a sythetic event and is not dispatched by Discord.
type Disconnect struct{}

// ChannelCreate is a wrapper struct for an event.
// RateLimit is the data for a RateLimit event.
// This is a sythetic event and is not dispatched by Discord.
type RateLimit struct {
	*TooManyRequests
	URL string
}

// Event provides a basic initial struct for all websocket events.
type Event struct {
	Operation int             `json:"op"`
	Sequence  int64           `json:"s"`
	Type      string          `json:"t"`
	RawData   json.RawMessage `json:"d"`
	// Struct contains one of the other types in this file.
	Struct interface{} `json:"-"`
}

// A Ready stores all data for the websocket READY event.
type Ready struct {
	Version         int          `json:"v"`
	SessionID       string       `json:"session_id"`
	User            *User        `json:"user"`
	ReadState       []*ReadState `json:"read_state"`
	PrivateChannels []*Channel   `json:"private_channels"`
	Guilds          []*Guild     `json:"guilds"`

	// Undocumented fields
	Settings          *Settings            `json:"user_settings"`
	UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
	Relationships     []*Relationship      `json:"relationships"`
	Presences         []*Presence          `json:"presences"`
	Notes             map[string]string    `json:"notes"`
}

// ChannelCreate is the data for a ChannelCreate event.
type ChannelCreate struct {
	*Channel
}

// ChannelUpdate is a wrapper struct for an event.
// ChannelUpdate is the data for a ChannelUpdate event.
type ChannelUpdate struct {
	*Channel
}

// ChannelDelete is a wrapper struct for an event.
// ChannelDelete is the data for a ChannelDelete event.
type ChannelDelete struct {
	*Channel
}

// GuildCreate is a wrapper struct for an event.
// ChannelPinsUpdate stores data for a ChannelPinsUpdate event.
type ChannelPinsUpdate struct {
	LastPinTimestamp string `json:"last_pin_timestamp"`
	ChannelID        string `json:"channel_id"`
}

// GuildCreate is the data for a GuildCreate event.
type GuildCreate struct {
	*Guild
}

// GuildUpdate is a wrapper struct for an event.
// GuildUpdate is the data for a GuildUpdate event.
type GuildUpdate struct {
	*Guild
}

// GuildDelete is a wrapper struct for an event.
// GuildDelete is the data for a GuildDelete event.
type GuildDelete struct {
	*Guild
}

// GuildBanAdd is a wrapper struct for an event.
// GuildBanAdd is the data for a GuildBanAdd event.
type GuildBanAdd struct {
	*GuildBan
	User    *User  `json:"user"`
	GuildID string `json:"guild_id"`
}

// GuildBanRemove is a wrapper struct for an event.
// GuildBanRemove is the data for a GuildBanRemove event.
type GuildBanRemove struct {
	*GuildBan
	User    *User  `json:"user"`
	GuildID string `json:"guild_id"`
}

// GuildMemberAdd is a wrapper struct for an event.
// GuildMemberAdd is the data for a GuildMemberAdd event.
type GuildMemberAdd struct {
	*Member
}

// GuildMemberUpdate is a wrapper struct for an event.
// GuildMemberUpdate is the data for a GuildMemberUpdate event.
type GuildMemberUpdate struct {
	*Member
}

// GuildMemberRemove is a wrapper struct for an event.
// GuildMemberRemove is the data for a GuildMemberRemove event.
type GuildMemberRemove struct {
	*Member
}

// GuildRoleCreate is a wrapper struct for an event.
// GuildRoleCreate is the data for a GuildRoleCreate event.
type GuildRoleCreate struct {
	*GuildRole
}

// GuildRoleUpdate is a wrapper struct for an event.
// GuildRoleUpdate is the data for a GuildRoleUpdate event.
type GuildRoleUpdate struct {
	*GuildRole
}

// VoiceStateUpdate is a wrapper struct for an event.
type VoiceStateUpdate struct {
	*VoiceState
// A GuildRoleDelete is the data for a GuildRoleDelete event.
type GuildRoleDelete struct {
	RoleID  string `json:"role_id"`
	GuildID string `json:"guild_id"`
}

// A GuildEmojisUpdate is the data for a guild emoji update event.
type GuildEmojisUpdate struct {
	GuildID string   `json:"guild_id"`
	Emojis  []*Emoji `json:"emojis"`
}

// A GuildMembersChunk is the data for a GuildMembersChunk event.
type GuildMembersChunk struct {
	GuildID string    `json:"guild_id"`
	Members []*Member `json:"members"`
}

// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
type GuildIntegrationsUpdate struct {
	GuildID string `json:"guild_id"`
}

// MessageAck is the data for a MessageAck event.
type MessageAck struct {
	MessageID string `json:"message_id"`
	ChannelID string `json:"channel_id"`
}

// MessageCreate is the data for a MessageCreate event.
type MessageCreate struct {
	*Message
}

// MessageUpdate is the data for a MessageUpdate event.
type MessageUpdate struct {
	*Message
}

// UserUpdate is a wrapper struct for an event.
// MessageDelete is the data for a MessageDelete event.
type MessageDelete struct {
	*Message
}

// MessageReactionAdd is the data for a MessageReactionAdd event.
type MessageReactionAdd struct {
	*MessageReaction
}

// MessageReactionRemove is the data for a MessageReactionRemove event.
type MessageReactionRemove struct {
	*MessageReaction
}

// MessageReactionRemoveAll is the data for a MessageReactionRemoveAll event.
type MessageReactionRemoveAll struct {
	*MessageReaction
}

// PresencesReplace is the data for a PresencesReplace event.
type PresencesReplace []*Presence

// PresenceUpdate is the data for a PresenceUpdate event.
type PresenceUpdate struct {
	Presence
	GuildID string   `json:"guild_id"`
	Roles   []string `json:"roles"`
}

// Resumed is the data for a Resumed event.
type Resumed struct {
	Trace []string `json:"_trace"`
}

// RelationshipAdd is the data for a RelationshipAdd event.
type RelationshipAdd struct {
	*Relationship
}

// RelationshipRemove is the data for a RelationshipRemove event.
type RelationshipRemove struct {
	*Relationship
}

// TypingStart is the data for a TypingStart event.
type TypingStart struct {
	UserID    string `json:"user_id"`
	ChannelID string `json:"channel_id"`
	Timestamp int    `json:"timestamp"`
}

// UserUpdate is the data for a UserUpdate event.
type UserUpdate struct {
	*UserUpdate
	*User
}

// UserSettingsUpdate is a map for an event.
// UserSettingsUpdate is the data for a UserSettingsUpdate event.
type UserSettingsUpdate map[string]interface{}

// UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event.
type UserGuildSettingsUpdate struct {
	*UserGuildSettings
}

// UserNoteUpdate is the data for a UserNoteUpdate event.
type UserNoteUpdate struct {
	ID   string `json:"id"`
	Note string `json:"note"`
}

// VoiceServerUpdate is the data for a VoiceServerUpdate event.
type VoiceServerUpdate struct {
	Token    string `json:"token"`
	GuildID  string `json:"guild_id"`
	Endpoint string `json:"endpoint"`
}

// VoiceStateUpdate is the data for a VoiceStateUpdate event.
type VoiceStateUpdate struct {
	*VoiceState
}

// MessageDeleteBulk is the data for a MessageDeleteBulk event
type MessageDeleteBulk struct {
	Messages  []string `json:"ids"`
	ChannelID string   `json:"channel_id"`
}

A DiscordGo/examples/README.md => DiscordGo/examples/README.md +11 -0
@@ 0,0 1,11 @@
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

# DiscordGo Examples

These examples demonstrate how to utilize DiscordGo.

Please explore the individual folders and give them a try!

**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
Discord chat channel for support.**


A DiscordGo/examples/airhorn/README.md => DiscordGo/examples/airhorn/README.md +51 -0
@@ 0,0 1,51 @@
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

## DiscordGo Airhorn Example

This example demonstrates how to utilize DiscordGo to listen for an !airhorn
command in a channel and then play a sound to that user's current voice channel.

**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
Discord chat channel for support.**

### Build

This assumes you already have a working Go environment setup and that
DiscordGo is correctly installed on your system.

From within the airhorn example folder, run the below command to compile the
example.

```sh
go build
```

### Usage

```
Usage of ./airhorn:
  -t string
        Bot Token
```

The below example shows how to start the bot from the airhorn example folder.

```sh
./airhorn -t YOUR_BOT_TOKEN
```

### Creating sounds

Airhorn bot uses [DCA](https://github.com/bwmarrin/dca) files, which are 
pre-computed files that are easy to send to Discord.

If you would like to create your own DCA files, please use:
* [dca-rs](https://github.com/nstafie/dca-rs)

See the below example of creating a DCA file from a WAV file.  This also works 
with MP3, FLAC, and many other file formats. Of course, you will need to 
[install](https://github.com/nstafie/dca-rs#installation) dca-rs first :)

```sh
./dca-rs -i <input wav file> --raw > <output file>
```

A DiscordGo/examples/airhorn/airhorn.dca => DiscordGo/examples/airhorn/airhorn.dca +0 -0
A DiscordGo/examples/airhorn/main.go => DiscordGo/examples/airhorn/main.go +211 -0
@@ 0,0 1,211 @@
package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/bwmarrin/discordgo"
)

func init() {
	flag.StringVar(&token, "t", "", "Bot 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 bot token.
	dg, err := discordgo.New("Bot " + 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)
	}

	// Wait here until CTRL-C or other term signal is received.
	fmt.Println("Airhorn is now running.  Press CTRL-C to exit.")
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc

	// Cleanly close down the Discord session.
	dg.Close()
}

// This function will be called (due to AddHandler above) when the bot receives
// the "ready" event from Discord.
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) {

	// Ignore all messages created by the bot itself
	// This isn't required in this specific example but it's a good practice.
	if m.Author.ID == s.State.User.ID {
		return
	}

	// check if the message is "!airhorn"
	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 guild's 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 {
		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 {
			err := file.Close()
			if err != nil {
				return err
			}
			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
}

A DiscordGo/examples/appmaker/README.md => DiscordGo/examples/appmaker/README.md +57 -0
@@ 0,0 1,57 @@
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

## DiscordGo AppMaker Example

This example demonstrates how to utilize DiscordGo to create, view, and delete
Bot Applications on your account.

These tasks are normally accomplished from the 
[Discord Developers](https://discordapp.com/developers/applications/me) site.

**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
Discord chat channel for support.**

### Build

This assumes you already have a working Go environment setup and that
DiscordGo is correctly installed on your system.

From within the appmaker example folder, run the below command to compile the
example.

```sh
go build
```

### Usage

This example only uses authentication tokens for authentication. While 
user email/password is supported by DiscordGo, it is not recommended.

```
./appmaker --help
Usage of ./appmaker:
  -d string
        Application ID to delete
  -l    List Applications Only
  -n string
        Name to give App/Bot
  -t string
        Owner Account Token
```

* Account Token is required.  The account will be the "owner" of any bot 
applications created.

* If you provide the **-l** flag than appmaker will only display a list of 
applications on the provided account.

* If you provide a **-d** flag with a valid application ID then that application
will be deleted.

Below example will create a new Bot Application under the given account.
The Bot will be named **DiscordGoRocks**

```sh
./appmaker -t YOUR_USER_TOKEN -n DiscordGoRocks
```

A DiscordGo/examples/appmaker/main.go => DiscordGo/examples/appmaker/main.go +103 -0
@@ 0,0 1,103 @@
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"

	"github.com/bwmarrin/discordgo"
)

// Variables used for command line options
var (
	Token    string
	Name     string
	DeleteID string
	ListOnly bool
)

func init() {

	flag.StringVar(&Token, "t", "", "Owner Account Token")
	flag.StringVar(&Name, "n", "", "Name to give App/Bot")
	flag.StringVar(&DeleteID, "d", "", "Application ID to delete")
	flag.BoolVar(&ListOnly, "l", false, "List Applications Only")
	flag.Parse()

	if Token == "" {
		flag.Usage()
		os.Exit(1)
	}
}

func main() {

	var err error

	// Create a new Discord session using the provided login information.
	dg, err := discordgo.New(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, err := dg.Applications()
		if err != nil {
			fmt.Println("error fetching applications,", err)
			return
		}

		for _, v := range aps {
			fmt.Println("-----------------------------------------------------")
			b, _ := json.MarshalIndent(v, "", " ")
			fmt.Println(string(b))
		}
		return
	}

	// if -d set, delete the given Application
	if DeleteID != "" {
		err = dg.ApplicationDelete(DeleteID)
		if err != nil {
			fmt.Println("error deleting application,", err)
		}
		return
	}

	if Name == "" {
		flag.Usage()
		os.Exit(1)
	}

	// Create a new application.
	ap := &discordgo.Application{}
	ap.Name = Name
	ap, err = dg.ApplicationCreate(ap)
	if err != nil {
		fmt.Println("error creating new applicaiton,", err)
		return
	}

	fmt.Printf("Application created successfully:\n")
	b, _ := json.MarshalIndent(ap, "", " ")
	fmt.Println(string(b))

	// 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")
	b, _ = json.MarshalIndent(bot, "", " ")
	fmt.Println(string(b))

	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.")
}

A DiscordGo/examples/avatar/README.md => DiscordGo/examples/avatar/README.md +49 -0
@@ 0,0 1,49 @@
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

## DiscordGo Avatar Example

This example demonstrates how to utilize DiscordGo to change the avatar for
a Discord account.  This example works both with a local file or the URL of
an image.

**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
Discord chat channel for support.**

### Build

This assumes you already have a working Go environment setup and that
DiscordGo is correctly installed on your system.

From within the avatar example folder, run the below command to compile the
example.

```sh
go build
```

### Usage

This example uses bot tokens for authentication only. While email/password is 
supported by DiscordGo, it is not recommended to use them.

```
./avatar --help
Usage of ./avatar:
  -f string
        Avatar File Name
  -t string
        Bot Token
  -u string
        URL to the avatar image
```

The below example shows how to set your Avatar from a local file.

```sh
./avatar -t TOKEN -f avatar.png
```
The below example shows how to set your Avatar from a URL.

```sh
./avatar -t TOKEN -u http://bwmarrin.github.io/discordgo/img/discordgo.png
```

A DiscordGo/examples/avatar/main.go => DiscordGo/examples/avatar/main.go +89 -0
@@ 0,0 1,89 @@
package main

import (
	"encoding/base64"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/bwmarrin/discordgo"
)

// Variables used for command line parameters
var (
	Token      string
	AvatarFile string
	AvatarURL  string
)

func init() {

	flag.StringVar(&Token, "t", "", "Bot Token")
	flag.StringVar(&AvatarFile, "f", "", "Avatar File Name")
	flag.StringVar(&AvatarURL, "u", "", "URL to the avatar image")
	flag.Parse()

	if Token == "" || (AvatarFile == "" && AvatarURL == "") {
		flag.Usage()
		os.Exit(1)
	}
}

func main() {

	// Create a new Discord session using the provided login information.
	dg, err := discordgo.New("Bot " + Token)
	if err != nil {
		fmt.Println("error creating Discord session,", err)
		return
	}

	// Declare these here so they can be used in the below two if blocks and
	// still carry over to the end of this function.
	var base64img string
	var contentType string

	// If we're using a URL link for the Avatar
	if AvatarURL != "" {

		resp, err := http.Get(AvatarURL)
		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
		}

		contentType = http.DetectContentType(img)
		base64img = base64.StdEncoding.EncodeToString(img)
	}

	// If we're using a local file for the Avatar
	if AvatarFile != "" {
		img, err := ioutil.ReadFile(AvatarFile)
		if err != nil {
			fmt.Println(err)
		}

		contentType = http.DetectContentType(img)
		base64img = base64.StdEncoding.EncodeToString(img)
	}

	// Now lets format our base64 image into the proper format Discord wants
	// and then call UserUpdate to set it as our user's Avatar.
	avatar := fmt.Sprintf("data:%s;base64,%s", contentType, base64img)
	_, err = dg.UserUpdate("", "", "", avatar, "")
	if err != nil {
		fmt.Println(err)
	}
}

A DiscordGo/examples/mytoken/README.md => DiscordGo/examples/mytoken/README.md +47 -0
@@ 0,0 1,47 @@
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

## DiscordGo MyToken Example

This example demonstrates how to utilize DiscordGo to login with an email and
password then to print out the Authentication Token for that user's account.

Everytime this application is run a new authentication token is generated 
for your account.  Logging you in via email and password then creating a new
token is a cpu/mem expensive task for Discord.  Because of that, it is highly
recommended to avoid doing this very often.  Please only use this once to get a 
token for your use and then always just your token.

**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
Discord chat channel for support.**

### Build

This assumes you already have a working Go environment setup and that
DiscordGo is correctly installed on your system.

From within the mytoken example folder, run the below command to compile the
example.

```sh
go build
```

### Usage

You must authenticate using both Email and Password for an account.

```
./mytoken --help
Usage of ./mytoken:
  -e string
        Account Email
  -p string
        Account Password
```

The below example shows how to start the program using an Email and Password for
authentication.

```sh
./mytoken -e youremail@here.com -p MySecretPassword
```

A DiscordGo/examples/mytoken/main.go => DiscordGo/examples/mytoken/main.go +40 -0
@@ 0,0 1,40 @@
package main

import (
	"flag"
	"fmt"
	"os"

	"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()

	if Email == "" || Password == "" {
		flag.Usage()
		os.Exit(1)
	}
}

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
	}

	// Print out your token.
	fmt.Printf("Your Authentication Token is:\n\n%s\n", dg.Token)
}

A DiscordGo/examples/pingpong/README.md => DiscordGo/examples/pingpong/README.md +42 -0
@@ 0,0 1,42 @@
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">

## DiscordGo Ping Pong Example

This example demonstrates how to utilize DiscordGo to create a Ping Pong Bot.

This Bot will respond to "ping" with "Pong!" and "pong" with "Ping!".

**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
Discord chat channel for support.**

### Build

This assumes you already have a working Go environment setup and that
DiscordGo is correctly installed on your system.


From within the pingpong example folder, run the below command to compile the
example.

```sh
go build
```

### Usage

This example uses bot tokens for authentication only. While user/password is 
supported by DiscordGo, it is not recommended for bots.

```
./pingpong --help
Usage of ./pingpong:
  -t string
        Bot Token
```

The below example shows how to start the bot

```sh
./pingpong -t YOUR_BOT_TOKEN
Bot is now running.  Press CTRL-C to exit.
```

A DiscordGo/examples/pingpong/main.go => DiscordGo/examples/pingpong/main.go +71 -0
@@ 0,0 1,71 @@
package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/bwmarrin/discordgo"
)

// Variables used for command line parameters
var (
	Token string
)

func init() {

	flag.StringVar(&Token, "t", "", "Bot Token")
	flag.Parse()
}

func main() {

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + Token)
	if err != nil {
		fmt.Println("error creating Discord session,", err)
		return
	}

	// Register the messageCreate func as a callback for MessageCreate events.
	dg.AddHandler(messageCreate)

	// Open a websocket connection to Discord and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("error opening connection,", err)
		return
	}

	// Wait here until CTRL-C or other term signal is received.
	fmt.Println("Bot is now running.  Press CTRL-C to exit.")
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-sc

	// Cleanly close down the Discord session.
	dg.Close()
}

// 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
	// This isn't required in this specific example but it's a good practice.
	if m.Author.ID == s.State.User.ID {
		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!")
	}
}

A DiscordGo/logging.go => DiscordGo/logging.go +95 -0
@@ 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()))
}
*/

M DiscordGo/message.go => DiscordGo/message.go +219 -49
@@ 10,73 10,243 @@
package discordgo

import (
	"fmt"
	"io"
	"regexp"
	"strings"
)

// MessageType is the type of Message
type MessageType int

// Block contains the valid known MessageType values
const (
	MessageTypeDefault MessageType = iota
	MessageTypeRecipientAdd
	MessageTypeRecipientRemove
	MessageTypeCall
	MessageTypeChannelNameChange
	MessageTypeChannelIconChange
	MessageTypeChannelPinnedMessage
	MessageTypeGuildMemberJoin
)

// 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"`
	Tts             bool          `json:"tts"`
	MentionEveryone bool          `json:"mention_everyone"`
	Author          *User         `json:"author"`
	Attachments     []*Attachment `json:"attachments"`
	Embeds          []*Embed      `json:"embeds"`
	Mentions        []*User       `json:"mentions"`
}

// An Attachment stores data for message attachments.
type Attachment struct {
	ID              string               `json:"id"`
	ChannelID       string               `json:"channel_id"`
	Content         string               `json:"content"`
	Timestamp       Timestamp            `json:"timestamp"`
	EditedTimestamp Timestamp            `json:"edited_timestamp"`
	MentionRoles    []string             `json:"mention_roles"`
	Tts             bool                 `json:"tts"`
	MentionEveryone bool                 `json:"mention_everyone"`
	Author          *User                `json:"author"`
	Attachments     []*MessageAttachment `json:"attachments"`
	Embeds          []*MessageEmbed      `json:"embeds"`
	Mentions        []*User              `json:"mentions"`
	Reactions       []*MessageReactions  `json:"reactions"`
	Type            MessageType          `json:"type"`
}

// File stores info about files you e.g. send in messages.
type File struct {
	Name        string
	ContentType string
	Reader      io.Reader
}

// MessageSend stores all parameters you can send with ChannelMessageSendComplex.
type MessageSend struct {
	Content string        `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`
	Tts     bool          `json:"tts"`
	Files   []*File       `json:"-"`

	// TODO: Remove this when compatibility is not required.
	File *File `json:"-"`
}

// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
// is also where you should get the instance from.
type MessageEdit struct {
	Content *string       `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`

	ID      string
	Channel string
}

// NewMessageEdit returns a MessageEdit struct, initialized
// with the Channel and ID.
func NewMessageEdit(channelID string, messageID string) *MessageEdit {
	return &MessageEdit{
		Channel: channelID,
		ID:      messageID,
	}
}

// SetContent is the same as setting the variable Content,
// except it doesn't take a pointer.
func (m *MessageEdit) SetContent(str string) *MessageEdit {
	m.Content = &str
	return m
}

// SetEmbed is a convenience function for setting the embed,
// so you can chain commands.
func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
	m.Embed = embed
	return m
}

// A MessageAttachment stores data for message attachments.
type MessageAttachment struct {
	ID       string `json:"id"`
	URL      string `json:"url"`
	ProxyURL string `json:"proxy_url"`
	Filename string `json:"filename"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	Filename string `json:"filename"`
	Size     int    `json:"size"`
}

// An Embed stores data for message embeds.
type Embed 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"`
// MessageEmbedFooter is a part of a MessageEmbed struct.
type MessageEmbedFooter struct {
	Text         string `json:"text,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedImage is a part of a MessageEmbed struct.
type MessageEmbedImage struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedThumbnail is a part of a MessageEmbed struct.
type MessageEmbedThumbnail struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedVideo is a part of a MessageEmbed struct.
type MessageEmbedVideo struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedProvider is a part of a MessageEmbed struct.
type MessageEmbedProvider struct {
	URL  string `json:"url,omitempty"`
	Name string `json:"name,omitempty"`
}

// MessageEmbedAuthor is a part of a MessageEmbed struct.
type MessageEmbedAuthor struct {
	URL          string `json:"url,omitempty"`
	Name         string `json:"name,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedField is a part of a MessageEmbed struct.
type MessageEmbedField struct {
	Name   string `json:"name,omitempty"`
	Value  string `json:"value,omitempty"`
	Inline bool   `json:"inline,omitempty"`
}

// An MessageEmbed stores data for message embeds.
type MessageEmbed struct {
	URL         string                 `json:"url,omitempty"`
	Type        string                 `json:"type,omitempty"`
	Title       string                 `json:"title,omitempty"`
	Description string                 `json:"description,omitempty"`
	Timestamp   string                 `json:"timestamp,omitempty"`
	Color       int                    `json:"color,omitempty"`
	Footer      *MessageEmbedFooter    `json:"footer,omitempty"`
	Image       *MessageEmbedImage     `json:"image,omitempty"`
	Thumbnail   *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
	Video       *MessageEmbedVideo     `json:"video,omitempty"`
	Provider    *MessageEmbedProvider  `json:"provider,omitempty"`
	Author      *MessageEmbedAuthor    `json:"author,omitempty"`
	Fields      []*MessageEmbedField   `json:"fields,omitempty"`
}

// MessageReactions holds a reactions object for a message.
type MessageReactions struct {
	Count int    `json:"count"`
	Me    bool   `json:"me"`
	Emoji *Emoji `json:"emoji"`
}

// ContentWithMentionsReplaced will replace all @<id> mentions with the
// username of the mention.
func (m *Message) ContentWithMentionsReplaced() string {
	if m.Mentions == nil {
		return m.Content
func (m *Message) ContentWithMentionsReplaced() (content string) {
	content = m.Content

	for _, user := range m.Mentions {
		content = strings.NewReplacer(
			"<@"+user.ID+">", "@"+user.Username,
			"<@!"+user.ID+">", "@"+user.Username,
		).Replace(content)
	}
	content := m.Content
	return
}

var patternChannels = regexp.MustCompile("<#[^>]*>")

// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the
// username of the mention, but also role IDs and more.
func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) {
	content = m.Content

	if !s.StateEnabled {
		content = m.ContentWithMentionsReplaced()
		return
	}

	channel, err := s.State.Channel(m.ChannelID)
	if err != nil {
		content = m.ContentWithMentionsReplaced()
		return
	}

	for _, user := range m.Mentions {
		content = strings.Replace(content, fmt.Sprintf("<@%s>", user.ID),
			fmt.Sprintf("@%s", user.Username), -1)
		nick := user.Username

		member, err := s.State.Member(channel.GuildID, user.ID)
		if err == nil && member.Nick != "" {
			nick = member.Nick
		}

		content = strings.NewReplacer(
			"<@"+user.ID+">", "@"+user.Username,
			"<@!"+user.ID+">", "@"+nick,
		).Replace(content)
	}
	for _, roleID := range m.MentionRoles {
		role, err := s.State.Role(channel.GuildID, roleID)
		if err != nil || !role.Mentionable {
			continue
		}

		content = strings.Replace(content, "<&"+role.ID+">", "@"+role.Name, -1)
	}
	return content

	content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
		channel, err := s.State.Channel(mention[2 : len(mention)-1])
		if err != nil || channel.Type == ChannelTypeGuildVoice {
			return mention
		}

		return "#" + channel.Name
	})
	return
}

A DiscordGo/message_test.go => DiscordGo/message_test.go +41 -0
@@ 0,0 1,41 @@
package discordgo

import (
	"testing"
)

func TestContentWithMoreMentionsReplaced(t *testing.T) {
	s := &Session{StateEnabled: true, State: NewState()}

	user := &User{
		ID:       "user",
		Username: "User Name",
	}

	s.StateEnabled = true
	s.State.GuildAdd(&Guild{ID: "guild"})
	s.State.RoleAdd("guild", &Role{
		ID:          "role",
		Name:        "Role Name",
		Mentionable: true,
	})
	s.State.MemberAdd(&Member{
		User:    user,
		Nick:    "User Nick",
		GuildID: "guild",
	})
	s.State.ChannelAdd(&Channel{
		Name:    "Channel Name",
		GuildID: "guild",
		ID:      "channel",
	})
	m := &Message{
		Content:      "<&role> <@!user> <@user> <#channel>",
		ChannelID:    "channel",
		MentionRoles: []string{"role"},
		Mentions:     []*User{user},
	}
	if result, _ := m.ContentWithMoreMentionsReplaced(s); result != "@Role Name @User Nick @User Name #Channel Name" {
		t.Error(result)
	}
}

A DiscordGo/mkdocs.yml => DiscordGo/mkdocs.yml +17 -0
@@ 0,0 1,17 @@
site_name: DiscordGo
site_author: Bruce Marriner
site_url: http://bwmarrin.github.io/discordgo/
repo_url: https://github.com/bwmarrin/discordgo

dev_addr: 0.0.0.0:8000
theme: yeti

markdown_extensions:
    - smarty
    - toc:
        permalink: True
    - sane_lists

pages:
    - 'Home': 'index.md'
    - 'Getting Started': 'GettingStarted.md'

A DiscordGo/oauth2.go => DiscordGo/oauth2.go +126 -0
@@ 0,0 1,126 @@
// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains functions related to Discord OAuth2 endpoints

package discordgo

// ------------------------------------------------------------------------------------------------
// Code specific to Discord OAuth2 Applications
// ------------------------------------------------------------------------------------------------

// An Application struct stores values for a Discord OAuth2 Application
type Application struct {
	ID                  string    `json:"id,omitempty"`
	Name                string    `json:"name"`
	Description         string    `json:"description,omitempty"`
	Icon                string    `json:"icon,omitempty"`
	Secret              string    `json:"secret,omitempty"`
	RedirectURIs        *[]string `json:"redirect_uris,omitempty"`
	BotRequireCodeGrant bool      `json:"bot_require_code_grant,omitempty"`
	BotPublic           bool      `json:"bot_public,omitempty"`
	RPCApplicationState int       `json:"rpc_application_state,omitempty"`
	Flags               int       `json:"flags,omitempty"`
	Owner               *User     `json:"owner"`
	Bot                 *User     `json:"bot"`
}

// Application returns an Application structure of a specific Application
//   appID : The ID of an Application
func (s *Session) Application(appID string) (st *Application, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointApplication(appID), nil, EndpointApplication(""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

// Applications returns all applications for the authenticated user
func (s *Session) Applications() (st []*Application, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointApplications, nil, EndpointApplications)
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

// ApplicationCreate creates a new Application
//    name : Name of Application / Bot
//    uris : Redirect URIs (Not required)
func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error) {

	data := struct {
		Name         string    `json:"name"`
		Description  string    `json:"description"`
		RedirectURIs *[]string `json:"redirect_uris,omitempty"`
	}{ap.Name, ap.Description, ap.RedirectURIs}

	body, err := s.RequestWithBucketID("POST", EndpointApplications, data, EndpointApplications)
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

// ApplicationUpdate updates an existing Application
//   var : desc
func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Application, err error) {

	data := struct {
		Name         string    `json:"name"`
		Description  string    `json:"description"`
		RedirectURIs *[]string `json:"redirect_uris,omitempty"`
	}{ap.Name, ap.Description, ap.RedirectURIs}

	body, err := s.RequestWithBucketID("PUT", EndpointApplication(appID), data, EndpointApplication(""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

// ApplicationDelete deletes an existing Application
//   appID : The ID of an Application
func (s *Session) ApplicationDelete(appID string) (err error) {

	_, err = s.RequestWithBucketID("DELETE", EndpointApplication(appID), nil, EndpointApplication(""))
	if err != nil {
		return
	}

	return
}

// ------------------------------------------------------------------------------------------------
// Code specific to Discord OAuth2 Application Bots
// ------------------------------------------------------------------------------------------------

// ApplicationBotCreate creates an Application Bot Account
//
//   appID : The ID of an Application
//
// NOTE: func name may change, if I can think up something better.
func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) {

	body, err := s.RequestWithBucketID("POST", EndpointApplicationsBot(appID), nil, EndpointApplicationsBot(""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

A DiscordGo/oauth2_test.go => DiscordGo/oauth2_test.go +57 -0
@@ 0,0 1,57 @@
package discordgo_test

import (
	"log"
	"os"

	"github.com/bwmarrin/discordgo"
)

func ExampleApplication() {

	// Authentication Token pulled from environment variable DG_TOKEN
	Token := os.Getenv("DG_TOKEN")
	if Token == "" {
		return
	}

	// Create a new Discordgo session
	dg, err := discordgo.New(Token)
	if err != nil {
		log.Println(err)
		return
	}

	// Create an new Application
	ap := &discordgo.Application{}
	ap.Name = "TestApp"
	ap.Description = "TestDesc"
	ap, err = dg.ApplicationCreate(ap)
	log.Printf("ApplicationCreate: err: %+v, app: %+v\n", err, ap)

	// Get a specific Application by it's ID
	ap, err = dg.Application(ap.ID)
	log.Printf("Application: err: %+v, app: %+v\n", err, ap)

	// Update an existing Application with new values
	ap.Description = "Whooooa"
	ap, err = dg.ApplicationUpdate(ap.ID, ap)
	log.Printf("ApplicationUpdate: err: %+v, app: %+v\n", err, ap)

	// create a new bot account for this application
	bot, err := dg.ApplicationBotCreate(ap.ID)
	log.Printf("BotCreate: err: %+v, bot: %+v\n", err, bot)

	// Get a list of all applications for the authenticated user
	apps, err := dg.Applications()
	log.Printf("Applications: err: %+v, apps : %+v\n", err, apps)
	for k, v := range apps {
		log.Printf("Applications: %d : %+v\n", k, v)
	}

	// Delete the application we created.
	err = dg.ApplicationDelete(ap.ID)
	log.Printf("Delete: err: %+v\n", err)

	return
}

A DiscordGo/ratelimit.go => DiscordGo/ratelimit.go +183 -0
@@ 0,0 1,183 @@
package discordgo

import (
	"net/http"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

// customRateLimit holds information for defining a custom rate limit
type customRateLimit struct {
	suffix   string
	requests int
	reset    time.Duration
}

// RateLimiter holds all ratelimit buckets
type RateLimiter struct {
	sync.Mutex
	global           *int64
	buckets          map[string]*Bucket
	globalRateLimit  time.Duration
	customRateLimits []*customRateLimit
}

// NewRatelimiter returns a new RateLimiter
func NewRatelimiter() *RateLimiter {

	return &RateLimiter{
		buckets: make(map[string]*Bucket),
		global:  new(int64),
		customRateLimits: []*customRateLimit{
			&customRateLimit{
				suffix:   "//reactions//",
				requests: 1,
				reset:    200 * time.Millisecond,
			},
		},
	}
}

// getBucket retrieves or creates a bucket
func (r *RateLimiter) getBucket(key string) *Bucket {
	r.Lock()
	defer r.Unlock()

	if bucket, ok := r.buckets[key]; ok {
		return bucket
	}

	b := &Bucket{
		remaining: 1,
		Key:       key,
		global:    r.global,
	}

	// Check if there is a custom ratelimit set for this bucket ID.
	for _, rl := range r.customRateLimits {
		if strings.HasSuffix(b.Key, rl.suffix) {
			b.customRateLimit = rl
			break
		}
	}

	r.buckets[key] = b
	return b
}

// LockBucket Locks until a request can be made
func (r *RateLimiter) LockBucket(bucketID string) *Bucket {

	b := r.getBucket(bucketID)

	b.Lock()

	// If we ran out of calls and the reset time is still ahead of us
	// then we need to take it easy and relax a little
	if b.remaining < 1 && b.reset.After(time.Now()) {
		time.Sleep(b.reset.Sub(time.Now()))

	}

	// Check for global ratelimits
	sleepTo := time.Unix(0, atomic.LoadInt64(r.global))
	if now := time.Now(); now.Before(sleepTo) {
		time.Sleep(sleepTo.Sub(now))
	}

	b.remaining--
	return b
}

// Bucket represents a ratelimit bucket, each bucket gets ratelimited individually (-global ratelimits)
type Bucket struct {
	sync.Mutex
	Key       string
	remaining int
	limit     int
	reset     time.Time
	global    *int64

	lastReset       time.Time
	customRateLimit *customRateLimit
}

// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
// and locks up the whole thing in case if there's a global ratelimit.
func (b *Bucket) Release(headers http.Header) error {
	defer b.Unlock()

	// Check if the bucket uses a custom ratelimiter
	if rl := b.customRateLimit; rl != nil {
		if time.Now().Sub(b.lastReset) >= rl.reset {
			b.remaining = rl.requests - 1
			b.lastReset = time.Now()
		}
		if b.remaining < 1 {
			b.reset = time.Now().Add(rl.reset)
		}
		return nil
	}

	if headers == nil {
		return nil
	}

	remaining := headers.Get("X-RateLimit-Remaining")
	reset := headers.Get("X-RateLimit-Reset")
	global := headers.Get("X-RateLimit-Global")
	retryAfter := headers.Get("Retry-After")

	// Update global and per bucket reset time if the proper headers are available
	// If global is set, then it will block all buckets until after Retry-After
	// If Retry-After without global is provided it will use that for the new reset
	// time since it's more accurate than X-RateLimit-Reset.
	// If Retry-After after is not proided, it will update the reset time from X-RateLimit-Reset
	if retryAfter != "" {
		parsedAfter, err := strconv.ParseInt(retryAfter, 10, 64)
		if err != nil {
			return err
		}

		resetAt := time.Now().Add(time.Duration(parsedAfter) * time.Millisecond)

		// Lock either this single bucket or all buckets
		if global != "" {
			atomic.StoreInt64(b.global, resetAt.UnixNano())
		} else {
			b.reset = resetAt
		}
	} else if reset != "" {
		// Calculate the reset time by using the date header returned from discord
		discordTime, err := http.ParseTime(headers.Get("Date"))
		if err != nil {
			return err
		}

		unix, err := strconv.ParseInt(reset, 10, 64)
		if err != nil {
			return err
		}

		// Calculate the time until reset and add it to the current local time
		// some extra time is added because without it i still encountered 429's.
		// The added amount is the lowest amount that gave no 429's
		// in 1k requests
		delta := time.Unix(unix, 0).Sub(discordTime) + time.Millisecond*250
		b.reset = time.Now().Add(delta)
	}

	// Udpate remaining if header is present
	if remaining != "" {
		parsedRemaining, err := strconv.ParseInt(remaining, 10, 32)
		if err != nil {
			return err
		}
		b.remaining = int(parsedRemaining)
	}

	return nil
}

A DiscordGo/ratelimit_test.go => DiscordGo/ratelimit_test.go +112 -0
@@ 0,0 1,112 @@
package discordgo

import (
	"net/http"
	"strconv"
	"testing"
	"time"
)

// This test takes ~2 seconds to run
func TestRatelimitReset(t *testing.T) {
	rl := NewRatelimiter()

	sendReq := func(endpoint string) {
		bucket := rl.LockBucket(endpoint)

		headers := http.Header(make(map[string][]string))

		headers.Set("X-RateLimit-Remaining", "0")
		// Reset for approx 2 seconds from now
		headers.Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Add(time.Second*2).Unix(), 10))
		headers.Set("Date", time.Now().Format(time.RFC850))

		err := bucket.Release(headers)
		if err != nil {
			t.Errorf("Release returned error: %v", err)
		}
	}

	sent := time.Now()
	sendReq("/guilds/99/channels")
	sendReq("/guilds/55/channels")
	sendReq("/guilds/66/channels")

	sendReq("/guilds/99/channels")
	sendReq("/guilds/55/channels")
	sendReq("/guilds/66/channels")

	// We hit the same endpoint 2 times, so we should only be ratelimited 2 second
	// And always less than 4 seconds (unless you're on a stoneage computer or using swap or something...)
	if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*4 {
		t.Log("OK", time.Since(sent))
	} else {
		t.Error("Did not ratelimit correctly, got:", time.Since(sent))
	}
}

// This test takes ~1 seconds to run
func TestRatelimitGlobal(t *testing.T) {
	rl := NewRatelimiter()

	sendReq := func(endpoint string) {
		bucket := rl.LockBucket(endpoint)

		headers := http.Header(make(map[string][]string))

		headers.Set("X-RateLimit-Global", "1")
		// Reset for approx 1 seconds from now
		headers.Set("Retry-After", "1000")

		err := bucket.Release(headers)
		if err != nil {
			t.Errorf("Release returned error: %v", err)
		}
	}

	sent := time.Now()

	// This should trigger a global ratelimit
	sendReq("/guilds/99/channels")
	time.Sleep(time.Millisecond * 100)

	// This shouldn't go through in less than 1 second
	sendReq("/guilds/55/channels")

	if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*2 {
		t.Log("OK", time.Since(sent))
	} else {
		t.Error("Did not ratelimit correctly, got:", time.Since(sent))
	}
}

func BenchmarkRatelimitSingleEndpoint(b *testing.B) {
	rl := NewRatelimiter()
	for i := 0; i < b.N; i++ {
		sendBenchReq("/guilds/99/channels", rl)
	}
}

func BenchmarkRatelimitParallelMultiEndpoints(b *testing.B) {
	rl := NewRatelimiter()
	b.RunParallel(func(pb *testing.PB) {
		i := 0
		for pb.Next() {
			sendBenchReq("/guilds/"+strconv.Itoa(i)+"/channels", rl)
			i++
		}
	})
}

// Does not actually send requests, but locks the bucket and releases it with made-up headers
func sendBenchReq(endpoint string, rl *RateLimiter) {
	bucket := rl.LockBucket(endpoint)

	headers := http.Header(make(map[string][]string))

	headers.Set("X-RateLimit-Remaining", "10")
	headers.Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Unix(), 10))
	headers.Set("Date", time.Now().Format(time.RFC850))

	bucket.Release(headers)
}

M DiscordGo/restapi.go => DiscordGo/restapi.go +1139 -158
@@ 20,24 20,33 @@ import (
	_ "image/png"  // For PNG decoding
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"net/url"
	"strconv"
	"strings"
	"time"
)

// ErrJSONUnmarshal is returned for JSON Unmarshall errors.
var ErrJSONUnmarshal = errors.New("json unmarshal")
// All error constants
var (
	ErrJSONUnmarshal           = errors.New("json unmarshal")
	ErrStatusOffline           = errors.New("You can't set your Status to offline")
	ErrVerificationLevelBounds = errors.New("VerificationLevel out of bounds, should be between 0 and 3")
	ErrPruneDaysBounds         = errors.New("the number of days should be more than or equal to 1")
	ErrGuildNoIcon             = errors.New("guild does not have an icon set")
	ErrGuildNoSplash           = errors.New("guild does not have a splash set")
)

// 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.
// Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr
func (s *Session) Request(method, urlStr string, data interface{}) (response []byte, err error) {
	return s.RequestWithBucketID(method, urlStr, data, strings.SplitN(urlStr, "?", 2)[0])
}

	if s.Debug {
		fmt.Println("API REQUEST  PAYLOAD :: [" + fmt.Sprintf("%+v", data) + "]")
	}

// RequestWithBucketID makes a (GET/POST/...) Requests to Discord REST API with JSON data.
func (s *Session) RequestWithBucketID(method, urlStr string, data interface{}, bucketID string) (response []byte, err error) {
	var body []byte
	if data != nil {
		body, err = json.Marshal(data)


@@ 46,18 55,27 @@ func (s *Session) Request(method, urlStr string, data interface{}) (response []b
		}
	}

	return s.request(method, urlStr, "application/json", body)
	return s.request(method, urlStr, "application/json", body, bucketID, 0)
}

// request makes a (GET/POST/...) Requests to Discord REST API.
func (s *Session) request(method, urlStr, contentType string, b []byte) (response []byte, err error) {
// Sequence is the sequence number, if it fails with a 502 it will
// retry with sequence+1 until it either succeeds or sequence >= session.MaxRestRetries
func (s *Session) request(method, urlStr, contentType string, b []byte, bucketID string, sequence int) (response []byte, err error) {
	if bucketID == "" {
		bucketID = strings.SplitN(urlStr, "?", 2)[0]
	}

	bucket := s.ratelimiter.LockBucket(bucketID)

	if s.Debug {
		fmt.Printf("API REQUEST %8s :: %s\n", method, urlStr)
		log.Printf("API REQUEST %8s :: %s\n", method, urlStr)
		log.Printf("API REQUEST  PAYLOAD :: [%s]\n", string(b))
	}

	req, err := http.NewRequest(method, urlStr, bytes.NewBuffer(b))
	if err != nil {
		bucket.Release(nil)
		return
	}



@@ 73,23 91,27 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons

	if s.Debug {
		for k, v := range req.Header {
			fmt.Printf("API REQUEST   HEADER :: [%s] = %+v\n", k, v)
			log.Printf("API REQUEST   HEADER :: [%s] = %+v\n", k, v)
		}
	}

	client := &http.Client{Timeout: (20 * time.Second)}

	resp, err := client.Do(req)
	resp, err := s.Client.Do(req)
	if err != nil {
		bucket.Release(nil)
		return
	}
	defer func() {
		err := resp.Body.Close()
		if err != nil {
			fmt.Println("error closing resp body")
		err2 := resp.Body.Close()
		if err2 != nil {
			log.Println("error closing resp body")
		}
	}()

	err = bucket.Release(resp.Header)
	if err != nil {
		return
	}

	response, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return


@@ 97,33 119,49 @@ func (s *Session) request(method, urlStr, contentType string, b []byte) (respons

	if s.Debug {

		fmt.Printf("API RESPONSE  STATUS :: %s\n", resp.Status)
		log.Printf("API RESPONSE  STATUS :: %s\n", resp.Status)
		for k, v := range resp.Header {
			fmt.Printf("API RESPONSE  HEADER :: [%s] = %+v\n", k, v)
			log.Printf("API RESPONSE  HEADER :: [%s] = %+v\n", k, v)
		}
		fmt.Printf("API RESPONSE    BODY :: [%s]\n", response)
		log.Printf("API RESPONSE    BODY :: [%s]\n\n\n", response)
	}

	// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
	switch resp.StatusCode {

	case 200: // OK
	case 204: // No Content
	case http.StatusOK:
	case http.StatusCreated:
	case http.StatusNoContent:

		// TODO check for 401 response, invalidate token if we get one.

	case http.StatusBadGateway:
		// Retry sending request if possible
		if sequence < s.MaxRestRetries {

			s.log(LogInformational, "%s Failed (%s), Retrying...", urlStr, resp.Status)
			response, err = s.request(method, urlStr, contentType, b, bucketID, sequence+1)
		} else {
			err = fmt.Errorf("Exceeded Max retries HTTP %s, %s", resp.Status, response)
		}

	case 429: // TOO MANY REQUESTS - Rate limiting
		rl := RateLimit{}
		rl := TooManyRequests{}
		err = json.Unmarshal(response, &rl)
		if err != nil {
			err = fmt.Errorf("Request unmarshal rate limit error : %+v", err)
			s.log(LogError, "rate limit unmarshal error, %s", err)
			return
		}
		time.Sleep(rl.RetryAfter)
		response, err = s.request(method, urlStr, contentType, b)
		s.log(LogInformational, "Rate Limiting %s, retry in %d", urlStr, rl.RetryAfter)
		s.handleEvent(rateLimitEventType, RateLimit{TooManyRequests: &rl, URL: urlStr})

		time.Sleep(rl.RetryAfter * time.Millisecond)
		// we can make the above smarter
		// this method can cause longer delays than required

		response, err = s.request(method, urlStr, contentType, b, bucketID, sequence)

	default: // Error condition
		err = fmt.Errorf("HTTP %s, %s", resp.Status, response)
		err = newRestError(req, resp, response)
	}

	return


@@ 143,6 181,12 @@ func unmarshal(data []byte, v interface{}) error {
// ------------------------------------------------------------------------------------------------

// Login asks the Discord server for an authentication token.
//
// NOTE: While email/pass authentication is supported by DiscordGo it is
// HIGHLY DISCOURAGED by Discord. Please only use email/pass to obtain a token
// and then use that authentication token for all future connections.
// Also, doing any form of automation with a user (non Bot) account may result
// in that account being permanently banned from Discord.
func (s *Session) Login(email, password string) (err error) {

	data := struct {


@@ 150,13 194,14 @@ func (s *Session) Login(email, password string) (err error) {
		Password string `json:"password"`
	}{email, password}

	response, err := s.Request("POST", LOGIN, data)
	response, err := s.RequestWithBucketID("POST", EndpointLogin, data, EndpointLogin)
	if err != nil {
		return
	}

	temp := struct {
		Token string `json:"token"`
		MFA   bool   `json:"mfa"`
	}{}

	err = unmarshal(response, &temp)


@@ 165,6 210,7 @@ func (s *Session) Login(email, password string) (err error) {
	}

	s.Token = temp.Token
	s.MFA = temp.MFA
	return
}



@@ 177,7 223,7 @@ func (s *Session) Register(username string) (token string, err error) {
		Username string `json:"username"`
	}{username}

	response, err := s.Request("POST", REGISTER, data)
	response, err := s.RequestWithBucketID("POST", EndpointRegister, data, EndpointRegister)
	if err != nil {
		return
	}


@@ 211,7 257,7 @@ func (s *Session) Logout() (err error) {
		Token string `json:"token"`
	}{s.Token}

	_, err = s.Request("POST", LOGOUT, data)
	_, err = s.RequestWithBucketID("POST", EndpointLogout, data, EndpointLogout)
	return
}



@@ 223,7 269,7 @@ func (s *Session) Logout() (err error) {
// 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", USER(userID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointUser(userID), nil, EndpointUsers)
	if err != nil {
		return
	}


@@ 232,15 278,21 @@ func (s *Session) User(userID string) (st *User, err error) {
	return
}

// UserAvatar returns an image.Image of a users Avatar.
// UserAvatar is deprecated. Please use UserAvatarDecode
// userID    : A user ID or "@me" which is a shortcut of current user ID
func (s *Session) UserAvatar(userID string) (img image.Image, err error) {
	u, err := s.User(userID)
	if err != nil {
		return
	}
	img, err = s.UserAvatarDecode(u)
	return
}

	body, err := s.Request("GET", USER_AVATAR(userID, u.Avatar), nil)
// UserAvatarDecode returns an image.Image of a user's Avatar
// user : The user which avatar should be retrieved
func (s *Session) UserAvatarDecode(u *User) (img image.Image, err error) {
	body, err := s.RequestWithBucketID("GET", EndpointUserAvatar(u.ID, u.Avatar), nil, EndpointUserAvatar("", ""))
	if err != nil {
		return
	}


@@ 258,14 310,14 @@ func (s *Session) UserUpdate(email, password, username, avatar, newPassword stri
	// If left blank, avatar will be set to null/blank

	data := struct {
		Email       string `json:"email"`
		Password    string `json:"password"`
		Username    string `json:"username"`
		Email       string `json:"email,omitempty"`
		Password    string `json:"password,omitempty"`
		Username    string `json:"username,omitempty"`
		Avatar      string `json:"avatar,omitempty"`
		NewPassword string `json:"new_password,omitempty"`
	}{email, password, username, avatar, newPassword}

	body, err := s.Request("PATCH", USER("@me"), data)
	body, err := s.RequestWithBucketID("PATCH", EndpointUser("@me"), data, EndpointUsers)
	if err != nil {
		return
	}


@@ 277,7 329,28 @@ func (s *Session) UserUpdate(email, password, username, avatar, newPassword stri
// UserSettings returns the settings for a given user
func (s *Session) UserSettings() (st *Settings, err error) {

	body, err := s.Request("GET", USER_SETTINGS("@me"), nil)
	body, err := s.RequestWithBucketID("GET", EndpointUserSettings("@me"), nil, EndpointUserSettings(""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

// UserUpdateStatus update the user status
// status   : The new status (Actual valid status are 'online','idle','dnd','invisible')
func (s *Session) UserUpdateStatus(status Status) (st *Settings, err error) {
	if status == StatusOffline {
		err = ErrStatusOffline
		return
	}

	data := struct {
		Status Status `json:"status"`
	}{status}

	body, err := s.RequestWithBucketID("PATCH", EndpointUserSettings("@me"), data, EndpointUserSettings(""))
	if err != nil {
		return
	}


@@ 290,7 363,7 @@ func (s *Session) UserSettings() (st *Settings, err error) {
// channels.
func (s *Session) UserChannels() (st []*Channel, err error) {

	body, err := s.Request("GET", USER_CHANNELS("@me"), nil)
	body, err := s.RequestWithBucketID("GET", EndpointUserChannels("@me"), nil, EndpointUserChannels(""))
	if err != nil {
		return
	}


@@ 307,7 380,40 @@ func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error)
		RecipientID string `json:"recipient_id"`
	}{recipientID}

	body, err := s.Request("POST", USER_CHANNELS("@me"), data)
	body, err := s.RequestWithBucketID("POST", EndpointUserChannels("@me"), data, EndpointUserChannels(""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

// UserGuilds returns an array of UserGuild structures for all guilds.
// limit     : The number guilds that can be returned. (max 100)
// beforeID  : If provided all guilds returned will be before given ID.
// afterID   : If provided all guilds returned will be after given ID.
func (s *Session) UserGuilds(limit int, beforeID, afterID string) (st []*UserGuild, err error) {

	v := url.Values{}

	if limit > 0 {
		v.Set("limit", strconv.Itoa(limit))
	}
	if afterID != "" {
		v.Set("after", afterID)
	}
	if beforeID != "" {
		v.Set("before", beforeID)
	}

	uri := EndpointUserGuilds("@me")

	if len(v) > 0 {
		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
	}

	body, err := s.RequestWithBucketID("GET", uri, nil, EndpointUserGuilds(""))
	if err != nil {
		return
	}


@@ 316,10 422,12 @@ func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error)
	return
}

// UserGuilds returns an array of Guild structures for all guilds.
func (s *Session) UserGuilds() (st []*Guild, err error) {
// 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("GET", USER_GUILDS("@me"), nil)
	body, err := s.RequestWithBucketID("PATCH", EndpointUserGuildSettings("@me", guildID), settings, EndpointUserGuildSettings("", guildID))
	if err != nil {
		return
	}


@@ 328,6 436,123 @@ func (s *Session) UserGuilds() (st []*Guild, err error) {
	return
}

// UserChannelPermissions returns the permission of a user in a channel.
// userID    : The ID of the user to calculate permissions for.
// channelID : The ID of the channel to calculate permission for.
//
// NOTE: This function is now deprecated and will be removed in the future.
// Please see the same function inside state.go
func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions int, err error) {
	// Try to just get permissions from state.
	apermissions, err = s.State.UserChannelPermissions(userID, channelID)
	if err == nil {
		return
	}

	// Otherwise try get as much data from state as possible, falling back to the network.
	channel, err := s.State.Channel(channelID)
	if err != nil || channel == nil {
		channel, err = s.Channel(channelID)
		if err != nil {
			return
		}
	}

	guild, err := s.State.Guild(channel.GuildID)
	if err != nil || guild == nil {
		guild, err = s.Guild(channel.GuildID)
		if err != nil {
			return
		}
	}

	if userID == guild.OwnerID {
		apermissions = PermissionAll
		return
	}

	member, err := s.State.Member(guild.ID, userID)
	if err != nil || member == nil {
		member, err = s.GuildMember(guild.ID, userID)
		if err != nil {
			return
		}
	}

	return memberPermissions(guild, channel, member), nil
}

// Calculates the permissions for a member.
// https://support.discordapp.com/hc/en-us/articles/206141927-How-is-the-permission-hierarchy-structured-
func memberPermissions(guild *Guild, channel *Channel, member *Member) (apermissions int) {
	userID := member.User.ID

	if userID == guild.OwnerID {
		apermissions = PermissionAll
		return
	}

	for _, role := range guild.Roles {
		if role.ID == guild.ID {
			apermissions |= role.Permissions
			break
		}
	}

	for _, role := range guild.Roles {
		for _, roleID := range member.Roles {
			if role.ID == roleID {
				apermissions |= role.Permissions
				break
			}
		}
	}

	if apermissions&PermissionAdministrator == PermissionAdministrator {
		apermissions |= PermissionAll
	}

	// Apply @everyone overrides from the channel.
	for _, overwrite := range channel.PermissionOverwrites {
		if guild.ID == overwrite.ID {
			apermissions &= ^overwrite.Deny
			apermissions |= overwrite.Allow
			break
		}
	}

	denies := 0
	allows := 0

	// Member overwrites can override role overrides, so do two passes
	for _, overwrite := range channel.PermissionOverwrites {
		for _, roleID := range member.Roles {
			if overwrite.Type == "role" && roleID == overwrite.ID {
				denies |= overwrite.Deny
				allows |= overwrite.Allow
				break
			}
		}
	}

	apermissions &= ^denies
	apermissions |= allows

	for _, overwrite := range channel.PermissionOverwrites {
		if overwrite.Type == "member" && overwrite.ID == userID {
			apermissions &= ^overwrite.Deny
			apermissions |= overwrite.Allow
			break
		}
	}

	if apermissions&PermissionAdministrator == PermissionAdministrator {
		apermissions |= PermissionAllChannel
	}

	return apermissions
}

// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Guilds
// ------------------------------------------------------------------------------------------------


@@ 343,7 568,7 @@ func (s *Session) Guild(guildID string) (st *Guild, err error) {
		}
	}

	body, err := s.Request("GET", GUILD(guildID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID), nil, EndpointGuild(guildID))
	if err != nil {
		return
	}


@@ 360,7 585,7 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) {
		Name string `json:"name"`
	}{name}

	body, err := s.Request("POST", GUILDS, data)
	body, err := s.RequestWithBucketID("POST", EndpointGuilds, data, EndpointGuilds)
	if err != nil {
		return
	}


@@ 371,14 596,38 @@ func (s *Session) GuildCreate(name string) (st *Guild, err error) {

// GuildEdit edits a new Guild
// guildID   : The ID of a Guild
// name      : A name for the Guild (2-100 characters)
func (s *Session) GuildEdit(guildID, name string) (st *Guild, err error) {
// 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 = ErrVerificationLevelBounds
			return
		}
	}

	data := struct {
		Name string `json:"name"`
	}{name}
	//Bounds checking for regions
	if g.Region != "" {
		isValid := false
		regions, _ := s.VoiceRegions()
		for _, r := range regions {
			if g.Region == r.ID {
				isValid = true
			}
		}
		if !isValid {
			var valid []string
			for _, r := range regions {
				valid = append(valid, r.ID)
			}
			err = fmt.Errorf("Region not a valid region (%q)", valid)
			return
		}
	}

	body, err := s.Request("POST", GUILD(guildID), data)
	body, err := s.RequestWithBucketID("PATCH", EndpointGuild(guildID), g, EndpointGuild(guildID))
	if err != nil {
		return
	}


@@ 391,7 640,7 @@ func (s *Session) GuildEdit(guildID, name string) (st *Guild, err error) {
// guildID   : The ID of a Guild
func (s *Session) GuildDelete(guildID string) (st *Guild, err error) {

	body, err := s.Request("DELETE", GUILD(guildID), nil)
	body, err := s.RequestWithBucketID("DELETE", EndpointGuild(guildID), nil, EndpointGuild(guildID))
	if err != nil {
		return
	}


@@ 404,16 653,16 @@ func (s *Session) GuildDelete(guildID string) (st *Guild, err error) {
// guildID   : The ID of a Guild
func (s *Session) GuildLeave(guildID string) (err error) {

	_, err = s.Request("DELETE", USER_GUILD("@me", guildID), nil)
	_, err = s.RequestWithBucketID("DELETE", EndpointUserGuild("@me", guildID), nil, EndpointUserGuild("", guildID))
	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) {
func (s *Session) GuildBans(guildID string) (st []*GuildBan, err error) {

	body, err := s.Request("GET", GUILD_BANS(guildID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointGuildBans(guildID), nil, EndpointGuildBans(guildID))
	if err != nil {
		return
	}


@@ 428,14 677,31 @@ func (s *Session) GuildBans(guildID string) (st []*User, err error) {
// userID    : The ID of a User
// days      : The number of days of previous comments to delete.
func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) {
	return s.GuildBanCreateWithReason(guildID, userID, "", days)
}

// GuildBanCreateWithReason bans the given user from the given guild also providing a reaso.
// guildID   : The ID of a Guild.
// userID    : The ID of a User
// reason    : The reason for this ban
// days      : The number of days of previous comments to delete.
func (s *Session) GuildBanCreateWithReason(guildID, userID, reason string, days int) (err error) {

	uri := GUILD_BAN(guildID, userID)
	uri := EndpointGuildBan(guildID, userID)

	queryParams := url.Values{}
	if days > 0 {
		uri = fmt.Sprintf("%s?delete-message-days=%d", uri, days)
		queryParams.Set("delete-message-days", strconv.Itoa(days))
	}
	if reason != "" {
		queryParams.Set("reason", reason)
	}

	if len(queryParams) > 0 {
		uri += "?" + queryParams.Encode()
	}

	_, err = s.Request("PUT", uri, nil)
	_, err = s.RequestWithBucketID("PUT", uri, nil, EndpointGuildBan(guildID, ""))
	return
}



@@ 444,22 710,22 @@ func (s *Session) GuildBanCreate(guildID, userID string, days int) (err error) {
// userID    : The ID of a User
func (s *Session) GuildBanDelete(guildID, userID string) (err error) {

	_, err = s.Request("DELETE", GUILD_BAN(guildID, userID), nil)
	_, err = s.RequestWithBucketID("DELETE", EndpointGuildBan(guildID, userID), nil, EndpointGuildBan(guildID, ""))
	return
}

// GuildMembers returns a list of members for a guild.
//  guildID  : The ID of a Guild.
//  offset   : A number of members to skip
//  limit    : max number of members to return
func (s *Session) GuildMembers(guildID string, offset, limit int) (st []*Member, err error) {
//  after    : The id of the member to return members after
//  limit    : max number of members to return (max 1000)
func (s *Session) GuildMembers(guildID string, after string, limit int) (st []*Member, err error) {

	uri := GUILD_MEMBERS(guildID)
	uri := EndpointGuildMembers(guildID)

	v := url.Values{}

	if offset > 0 {
		v.Set("offset", strconv.Itoa(offset))
	if after != "" {
		v.Set("after", after)
	}

	if limit > 0 {


@@ 470,7 736,7 @@ func (s *Session) GuildMembers(guildID string, offset, limit int) (st []*Member,
		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
	}

	body, err := s.Request("GET", uri, nil)
	body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildMembers(guildID))
	if err != nil {
		return
	}


@@ 484,7 750,7 @@ func (s *Session) GuildMembers(guildID string, offset, limit int) (st []*Member,
//  userID    : The ID of a User
func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) {

	body, err := s.Request("GET", GUILD_MEMBER(guildID, userID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointGuildMember(guildID, userID), nil, EndpointGuildMember(guildID, ""))
	if err != nil {
		return
	}


@@ 498,7 764,21 @@ func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) {
// userID    : The ID of a User
func (s *Session) GuildMemberDelete(guildID, userID string) (err error) {

	_, err = s.Request("DELETE", GUILD_MEMBER(guildID, userID), nil)
	return s.GuildMemberDeleteWithReason(guildID, userID, "")
}

// GuildMemberDeleteWithReason removes the given user from the given guild.
// guildID   : The ID of a Guild.
// userID    : The ID of a User
// reason    : The reason for the kick
func (s *Session) GuildMemberDeleteWithReason(guildID, userID, reason string) (err error) {

	uri := EndpointGuildMember(guildID, userID)
	if reason != "" {
		uri += "?reason=" + url.QueryEscape(reason)
	}

	_, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointGuildMember(guildID, ""))
	return
}



@@ 512,7 792,7 @@ func (s *Session) GuildMemberEdit(guildID, userID string, roles []string) (err e
		Roles []string `json:"roles"`
	}{roles}

	_, err = s.Request("PATCH", GUILD_MEMBER(guildID, userID), data)
	_, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""))
	if err != nil {
		return
	}


@@ 532,7 812,7 @@ func (s *Session) GuildMemberMove(guildID, userID, channelID string) (err error)
		ChannelID string `json:"channel_id"`
	}{channelID}

	_, err = s.Request("PATCH", GUILD_MEMBER(guildID, userID), data)
	_, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""))
	if err != nil {
		return
	}


@@ 540,12 820,52 @@ func (s *Session) GuildMemberMove(guildID, userID, channelID string) (err error)
	return
}

// GuildMemberNickname updates the nickname of a guild member
// guildID   : The ID of a guild
// userID    : The ID of a user
// userID    : The ID of a user or "@me" which is a shortcut of the current user ID
func (s *Session) GuildMemberNickname(guildID, userID, nickname string) (err error) {

	data := struct {
		Nick string `json:"nick"`
	}{nickname}

	if userID == "@me" {
		userID += "/nick"
	}

	_, err = s.RequestWithBucketID("PATCH", EndpointGuildMember(guildID, userID), data, EndpointGuildMember(guildID, ""))
	return
}

// GuildMemberRoleAdd adds the specified role to a given member
//  guildID   : The ID of a Guild.
//  userID    : The ID of a User.
//  roleID 	  : The ID of a Role to be assigned to the user.
func (s *Session) GuildMemberRoleAdd(guildID, userID, roleID string) (err error) {

	_, err = s.RequestWithBucketID("PUT", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, "", ""))

	return
}

// GuildMemberRoleRemove removes the specified role to a given member
//  guildID   : The ID of a Guild.
//  userID    : The ID of a User.
//  roleID 	  : The ID of a Role to be removed from the user.
func (s *Session) GuildMemberRoleRemove(guildID, userID, roleID string) (err error) {

	_, err = s.RequestWithBucketID("DELETE", EndpointGuildMemberRole(guildID, userID, roleID), nil, EndpointGuildMemberRole(guildID, "", ""))

	return
}

// GuildChannels returns an array of Channel structures for all channels of a
// given guild.
// guildID   : The ID of a Guild.
func (s *Session) GuildChannels(guildID string) (st []*Channel, err error) {

	body, err := s.Request("GET", GUILD_CHANNELS(guildID), nil)
	body, err := s.request("GET", EndpointGuildChannels(guildID), "", nil, EndpointGuildChannels(guildID), 0)
	if err != nil {
		return
	}


@@ 566,7 886,7 @@ func (s *Session) GuildChannelCreate(guildID, name, ctype string) (st *Channel, 
		Type string `json:"type"`
	}{name, ctype}

	body, err := s.Request("POST", GUILD_CHANNELS(guildID), data)
	body, err := s.RequestWithBucketID("POST", EndpointGuildChannels(guildID), data, EndpointGuildChannels(guildID))
	if err != nil {
		return
	}


@@ 575,10 895,19 @@ func (s *Session) GuildChannelCreate(guildID, name, ctype string) (st *Channel, 
	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.RequestWithBucketID("PATCH", EndpointGuildChannels(guildID), channels, EndpointGuildChannels(guildID))
	return
}

// GuildInvites returns an array of Invite structures for the given guild
// guildID   : The ID of a Guild.
func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) {
	body, err := s.Request("GET", GUILD_INVITES(guildID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointGuildInvites(guildID), nil, EndpointGuildInivtes(guildID))
	if err != nil {
		return
	}


@@ 591,7 920,7 @@ func (s *Session) GuildInvites(guildID string) (st []*Invite, err error) {
// guildID   : The ID of a Guild.
func (s *Session) GuildRoles(guildID string) (st []*Role, err error) {

	body, err := s.Request("GET", GUILD_ROLES(guildID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointGuildRoles(guildID), nil, EndpointGuildRoles(guildID))
	if err != nil {
		return
	}


@@ 605,7 934,7 @@ func (s *Session) GuildRoles(guildID string) (st []*Role, err error) {
// guildID: The ID of a Guild.
func (s *Session) GuildRoleCreate(guildID string) (st *Role, err error) {

	body, err := s.Request("POST", GUILD_ROLES(guildID), nil)
	body, err := s.RequestWithBucketID("POST", EndpointGuildRoles(guildID), nil, EndpointGuildRoles(guildID))
	if err != nil {
		return
	}


@@ 622,16 951,23 @@ func (s *Session) GuildRoleCreate(guildID string) (st *Role, err error) {
// 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) {
// mention   : Whether this role is mentionable
func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist bool, perm int, mention bool) (st *Role, err error) {

	// Prevent sending a color int that is too big.
	if color > 0xFFFFFF {
		err = fmt.Errorf("color value cannot be larger than 0xFFFFFF")
	}

	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)
		Name        string `json:"name"`        // The role's name (overwrites existing)
		Color       int    `json:"color"`       // The color the role should have (as a decimal, not hex)
		Hoist       bool   `json:"hoist"`       // Whether to display the role's users separately
		Permissions int    `json:"permissions"` // The overall permissions number of the role (overwrites existing)
	}{name, color, hoist, perm}
		Mentionable bool   `json:"mentionable"` // Whether this role is mentionable
	}{name, color, hoist, perm, mention}

	body, err := s.Request("PATCH", GUILD_ROLE(guildID, roleID), data)
	body, err := s.RequestWithBucketID("PATCH", EndpointGuildRole(guildID, roleID), data, EndpointGuildRole(guildID, ""))
	if err != nil {
		return
	}


@@ 646,7 982,7 @@ func (s *Session) GuildRoleEdit(guildID, roleID, name string, color int, hoist b
// roles     : A list of ordered roles.
func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, err error) {

	body, err := s.Request("PATCH", GUILD_ROLES(guildID), roles)
	body, err := s.RequestWithBucketID("PATCH", EndpointGuildRoles(guildID), roles, EndpointGuildRoles(guildID))
	if err != nil {
		return
	}


@@ 661,8 997,139 @@ func (s *Session) GuildRoleReorder(guildID string, roles []*Role) (st []*Role, e
// roleID    : The ID of a Role.
func (s *Session) GuildRoleDelete(guildID, roleID string) (err error) {

	_, err = s.Request("DELETE", GUILD_ROLE(guildID, roleID), nil)
	_, err = s.RequestWithBucketID("DELETE", EndpointGuildRole(guildID, roleID), nil, EndpointGuildRole(guildID, ""))

	return
}

// GuildPruneCount Returns the number of members that would be removed in a prune operation.
// Requires 'KICK_MEMBER' permission.
// guildID	: The ID of a Guild.
// days		: The number of days to count prune for (1 or more).
func (s *Session) GuildPruneCount(guildID string, days uint32) (count uint32, err error) {
	count = 0

	if days <= 0 {
		err = ErrPruneDaysBounds
		return
	}

	p := struct {
		Pruned uint32 `json:"pruned"`
	}{}

	uri := EndpointGuildPrune(guildID) + fmt.Sprintf("?days=%d", days)
	body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildPrune(guildID))

	err = unmarshal(body, &p)
	if err != nil {
		return
	}

	count = p.Pruned

	return
}

// GuildPrune Begin as prune operation. Requires the 'KICK_MEMBERS' permission.
// Returns an object with one 'pruned' key indicating the number of members that were removed in the prune operation.
// guildID	: The ID of a Guild.
// days		: The number of days to count prune for (1 or more).
func (s *Session) GuildPrune(guildID string, days uint32) (count uint32, err error) {

	count = 0

	if days <= 0 {
		err = ErrPruneDaysBounds
		return
	}

	data := struct {
		days uint32
	}{days}

	p := struct {
		Pruned uint32 `json:"pruned"`
	}{}

	body, err := s.RequestWithBucketID("POST", EndpointGuildPrune(guildID), data, EndpointGuildPrune(guildID))
	if err != nil {
		return
	}

	err = unmarshal(body, &p)
	if err != nil {
		return
	}

	count = p.Pruned

	return
}

// GuildIntegrations returns an array of Integrations for a guild.
// guildID   : The ID of a Guild.
func (s *Session) GuildIntegrations(guildID string) (st []*GuildIntegration, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointGuildIntegrations(guildID), nil, EndpointGuildIntegrations(guildID))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// GuildIntegrationCreate creates a Guild Integration.
// guildID          : The ID of a Guild.
// integrationType  : The Integration type.
// integrationID    : The ID of an integration.
func (s *Session) GuildIntegrationCreate(guildID, integrationType, integrationID string) (err error) {

	data := struct {
		Type string `json:"type"`
		ID   string `json:"id"`
	}{integrationType, integrationID}

	_, err = s.RequestWithBucketID("POST", EndpointGuildIntegrations(guildID), data, EndpointGuildIntegrations(guildID))
	return
}

// GuildIntegrationEdit edits a Guild Integration.
// guildID              : The ID of a Guild.
// integrationType      : The Integration type.
// integrationID        : The ID of an integration.
// expireBehavior	      : The behavior when an integration subscription lapses (see the integration object documentation).
// expireGracePeriod    : Period (in seconds) where the integration will ignore lapsed subscriptions.
// enableEmoticons	    : Whether emoticons should be synced for this integration (twitch only currently).
func (s *Session) GuildIntegrationEdit(guildID, integrationID string, expireBehavior, expireGracePeriod int, enableEmoticons bool) (err error) {

	data := struct {
		ExpireBehavior    int  `json:"expire_behavior"`
		ExpireGracePeriod int  `json:"expire_grace_period"`
		EnableEmoticons   bool `json:"enable_emoticons"`
	}{expireBehavior, expireGracePeriod, enableEmoticons}

	_, err = s.RequestWithBucketID("PATCH", EndpointGuildIntegration(guildID, integrationID), data, EndpointGuildIntegration(guildID, ""))
	return
}

// GuildIntegrationDelete removes the given integration from the Guild.
// guildID          : The ID of a Guild.
// integrationID    : The ID of an integration.
func (s *Session) GuildIntegrationDelete(guildID, integrationID string) (err error) {

	_, err = s.RequestWithBucketID("DELETE", EndpointGuildIntegration(guildID, integrationID), nil, EndpointGuildIntegration(guildID, ""))
	return
}

// GuildIntegrationSync syncs an integration.
// guildID          : The ID of a Guild.
// integrationID    : The ID of an integration.
func (s *Session) GuildIntegrationSync(guildID, integrationID string) (err error) {

	_, err = s.RequestWithBucketID("POST", EndpointGuildIntegrationSync(guildID, integrationID), nil, EndpointGuildIntegration(guildID, ""))
	return
}



@@ 675,11 1142,11 @@ func (s *Session) GuildIcon(guildID string) (img image.Image, err error) {
	}

	if g.Icon == "" {
		err = errors.New("Guild does not have an icon set.")
		err = ErrGuildNoIcon
		return
	}

	body, err := s.Request("GET", GUILD_ICON(guildID, g.Icon), nil)
	body, err := s.RequestWithBucketID("GET", EndpointGuildIcon(guildID, g.Icon), nil, EndpointGuildIcon(guildID, ""))
	if err != nil {
		return
	}


@@ 697,11 1164,11 @@ func (s *Session) GuildSplash(guildID string) (img image.Image, err error) {
	}

	if g.Splash == "" {
		err = errors.New("Guild does not have a splash set.")
		err = ErrGuildNoSplash
		return
	}

	body, err := s.Request("GET", GUILD_SPLASH(guildID, g.Splash), nil)
	body, err := s.RequestWithBucketID("GET", EndpointGuildSplash(guildID, g.Splash), nil, EndpointGuildSplash(guildID, ""))
	if err != nil {
		return
	}


@@ 710,6 1177,29 @@ func (s *Session) GuildSplash(guildID string) (img image.Image, err error) {
	return
}

// GuildEmbed returns the embed for a Guild.
// guildID   : The ID of a Guild.
func (s *Session) GuildEmbed(guildID string) (st *GuildEmbed, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointGuildEmbed(guildID), nil, EndpointGuildEmbed(guildID))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

// GuildEmbedEdit returns the embed for a Guild.
// guildID   : The ID of a Guild.
func (s *Session) GuildEmbedEdit(guildID string, enabled bool, channelID string) (err error) {

	data := GuildEmbed{enabled, channelID}

	_, err = s.RequestWithBucketID("PATCH", EndpointGuildEmbed(guildID), data, EndpointGuildEmbed(guildID))
	return
}

// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Channels
// ------------------------------------------------------------------------------------------------


@@ 717,7 1207,7 @@ func (s *Session) GuildSplash(guildID string) (img image.Image, err error) {
// 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", CHANNEL(channelID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointChannel(channelID), nil, EndpointChannel(channelID))
	if err != nil {
		return
	}


@@ 735,7 1225,7 @@ func (s *Session) ChannelEdit(channelID, name string) (st *Channel, err error) {
		Name string `json:"name"`
	}{name}

	body, err := s.Request("PATCH", CHANNEL(channelID), data)
	body, err := s.RequestWithBucketID("PATCH", EndpointChannel(channelID), data, EndpointChannel(channelID))
	if err != nil {
		return
	}


@@ 748,7 1238,7 @@ func (s *Session) ChannelEdit(channelID, name string) (st *Channel, err error) {
// channelID  : The ID of a Channel
func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) {

	body, err := s.Request("DELETE", CHANNEL(channelID), nil)
	body, err := s.RequestWithBucketID("DELETE", EndpointChannel(channelID), nil, EndpointChannel(channelID))
	if err != nil {
		return
	}


@@ 762,19 1252,20 @@ func (s *Session) ChannelDelete(channelID string) (st *Channel, err error) {
// channelID  : The ID of a Channel
func (s *Session) ChannelTyping(channelID string) (err error) {

	_, err = s.Request("POST", CHANNEL_TYPING(channelID), nil)
	_, err = s.RequestWithBucketID("POST", EndpointChannelTyping(channelID), nil, EndpointChannelTyping(channelID))
	return
}

// ChannelMessages returns an array of Message structures for messages within
// a given channel.
// channelID : The ID of a Channel.
// limit     : The number messages that can be returned.
// 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) {
// aroundID  : If provided all messages returned will be around given ID.
func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID, aroundID string) (st []*Message, err error) {

	uri := CHANNEL_MESSAGES(channelID)
	uri := EndpointChannelMessages(channelID)

	v := url.Values{}
	if limit > 0 {


@@ 786,11 1277,14 @@ func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID
	if beforeID != "" {
		v.Set("before", beforeID)
	}
	if aroundID != "" {
		v.Set("around", aroundID)
	}
	if len(v) > 0 {
		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
	}

	body, err := s.Request("GET", uri, nil)
	body, err := s.RequestWithBucketID("GET", uri, nil, EndpointChannelMessages(channelID))
	if err != nil {
		return
	}


@@ 799,64 1293,165 @@ func (s *Session) ChannelMessages(channelID string, limit int, beforeID, afterID
	return
}

// ChannelMessageAck acknowledges and marks the given message as read
// 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) ChannelMessageAck(channelID, messageID string) (err error) {
func (s *Session) ChannelMessage(channelID, messageID string) (st *Message, err error) {

	response, err := s.RequestWithBucketID("GET", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, ""))
	if err != nil {
		return
	}

	_, err = s.Request("POST", CHANNEL_MESSAGE_ACK(channelID, messageID), nil)
	err = unmarshal(response, &st)
	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}
// ChannelMessageAck acknowledges and marks the given message as read
// channeld  : The ID of a Channel
// messageID : the ID of a Message
// lastToken : token returned by last ack
func (s *Session) ChannelMessageAck(channelID, messageID, lastToken string) (st *Ack, err error) {

	// Send the message to the given channel
	response, err := s.Request("POST", CHANNEL_MESSAGES(channelID), data)
	body, err := s.RequestWithBucketID("POST", EndpointChannelMessageAck(channelID, messageID), &Ack{Token: lastToken}, EndpointChannelMessageAck(channelID, ""))
	if err != nil {
		return
	}

	err = unmarshal(response, &st)
	err = unmarshal(body, &st)
	return
}

// ChannelMessageSend sends a message to the given channel.
// channelID : The ID of a Channel.
// content   : The message to send.
func (s *Session) ChannelMessageSend(channelID string, content string) (st *Message, err error) {
func (s *Session) ChannelMessageSend(channelID string, content string) (*Message, error) {
	return s.ChannelMessageSendComplex(channelID, &MessageSend{
		Content: content,
	})
}

var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

// ChannelMessageSendComplex sends a message to the given channel.
// channelID : The ID of a Channel.
// data      : The message struct to send.
func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend) (st *Message, err error) {
	if data.Embed != nil && data.Embed.Type == "" {
		data.Embed.Type = "rich"
	}

	endpoint := EndpointChannelMessages(channelID)

	// TODO: Remove this when compatibility is not required.
	files := data.Files
	if data.File != nil {
		if files == nil {
			files = []*File{data.File}
		} else {
			err = fmt.Errorf("cannot specify both File and Files")
			return
		}
	}

	return s.channelMessageSend(channelID, content, false)
	var response []byte
	if len(files) > 0 {
		body := &bytes.Buffer{}
		bodywriter := multipart.NewWriter(body)

		var payload []byte
		payload, err = json.Marshal(data)
		if err != nil {
			return
		}

		var p io.Writer

		h := make(textproto.MIMEHeader)
		h.Set("Content-Disposition", `form-data; name="payload_json"`)
		h.Set("Content-Type", "application/json")

		p, err = bodywriter.CreatePart(h)
		if err != nil {
			return
		}

		if _, err = p.Write(payload); err != nil {
			return
		}

		for i, file := range files {
			h := make(textproto.MIMEHeader)
			h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file%d"; filename="%s"`, i, quoteEscaper.Replace(file.Name)))
			contentType := file.ContentType
			if contentType == "" {
				contentType = "application/octet-stream"
			}
			h.Set("Content-Type", contentType)

			p, err = bodywriter.CreatePart(h)
			if err != nil {
				return
			}

			if _, err = io.Copy(p, file.Reader); err != nil {
				return
			}
		}

		err = bodywriter.Close()
		if err != nil {
			return
		}

		response, err = s.request("POST", endpoint, bodywriter.FormDataContentType(), body.Bytes(), endpoint, 0)
	} else {
		response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint)
	}
	if err != nil {
		return
	}

	err = unmarshal(response, &st)
	return
}

// ChannelMessageSendTTS sends a message to the given channel with Text to Speech.
// channelID : The ID of a Channel.
// content   : The message to send.
func (s *Session) ChannelMessageSendTTS(channelID string, content string) (st *Message, err error) {
func (s *Session) ChannelMessageSendTTS(channelID string, content string) (*Message, error) {
	return s.ChannelMessageSendComplex(channelID, &MessageSend{
		Content: content,
		Tts:     true,
	})
}

	return s.channelMessageSend(channelID, content, true)
// ChannelMessageSendEmbed sends a message to the given channel with embedded data.
// channelID : The ID of a Channel.
// embed     : The embed data to send.
func (s *Session) ChannelMessageSendEmbed(channelID string, embed *MessageEmbed) (*Message, error) {
	return s.ChannelMessageSendComplex(channelID, &MessageSend{
		Embed: embed,
	})
}

// ChannelMessageEdit edits an existing message, replacing it entirely with
// the given content.
// channeld  : The ID of a Channel
// messageID : the ID of a Message
func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (st *Message, err error) {
// channelID  : The ID of a Channel
// messageID  : The ID of a Message
// content    : The contents of the message
func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (*Message, error) {
	return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetContent(content))
}

	data := struct {
		Content string `json:"content"`
	}{content}
// ChannelMessageEditComplex edits an existing message, replacing it entirely with
// the given MessageEdit struct
func (s *Session) ChannelMessageEditComplex(m *MessageEdit) (st *Message, err error) {
	if m.Embed != nil && m.Embed.Type == "" {
		m.Embed.Type = "rich"
	}

	response, err := s.Request("PATCH", CHANNEL_MESSAGE(channelID, messageID), data)
	response, err := s.RequestWithBucketID("PATCH", EndpointChannelMessage(m.Channel, m.ID), m, EndpointChannelMessage(m.Channel, ""))
	if err != nil {
		return
	}


@@ 865,50 1460,105 @@ func (s *Session) ChannelMessageEdit(channelID, messageID, content string) (st *
	return
}

// ChannelMessageEditEmbed edits an existing message with embedded data.
// channelID : The ID of a Channel
// messageID : The ID of a Message
// embed     : The embed data to send
func (s *Session) ChannelMessageEditEmbed(channelID, messageID string, embed *MessageEmbed) (*Message, error) {
	return s.ChannelMessageEditComplex(NewMessageEdit(channelID, messageID).SetEmbed(embed))
}

// ChannelMessageDelete deletes a message from the Channel.
func (s *Session) ChannelMessageDelete(channelID, messageID string) (err error) {

	_, err = s.Request("DELETE", CHANNEL_MESSAGE(channelID, messageID), nil)
	_, err = s.RequestWithBucketID("DELETE", EndpointChannelMessage(channelID, messageID), nil, EndpointChannelMessage(channelID, ""))
	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)
// 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) {

	writer, err := bodywriter.CreateFormFile("file", name)
	if err != nil {
		return nil, err
	if len(messages) == 0 {
		return
	}

	_, err = io.Copy(writer, r)
	if err != nil {
	if len(messages) == 1 {
		err = s.ChannelMessageDelete(channelID, messages[0])
		return
	}

	err = bodywriter.Close()
	if err != nil {
		return
	if len(messages) > 100 {
		messages = messages[:100]
	}

	response, err := s.request("POST", CHANNEL_MESSAGES(channelID), bodywriter.FormDataContentType(), body.Bytes())
	data := struct {
		Messages []string `json:"messages"`
	}{messages}

	_, err = s.RequestWithBucketID("POST", EndpointChannelMessagesBulkDelete(channelID), data, EndpointChannelMessagesBulkDelete(channelID))
	return
}

// ChannelMessagePin pins a message within a given channel.
// channelID: The ID of a channel.
// messageID: The ID of a message.
func (s *Session) ChannelMessagePin(channelID, messageID string) (err error) {

	_, err = s.RequestWithBucketID("PUT", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, ""))
	return
}

// ChannelMessageUnpin unpins a message within a given channel.
// channelID: The ID of a channel.
// messageID: The ID of a message.
func (s *Session) ChannelMessageUnpin(channelID, messageID string) (err error) {

	_, err = s.RequestWithBucketID("DELETE", EndpointChannelMessagePin(channelID, messageID), nil, EndpointChannelMessagePin(channelID, ""))
	return
}

// ChannelMessagesPinned returns an array of Message structures for pinned messages
// within a given channel
// channelID : The ID of a Channel.
func (s *Session) ChannelMessagesPinned(channelID string) (st []*Message, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointChannelMessagesPins(channelID), nil, EndpointChannelMessagesPins(channelID))

	if err != nil {
		return
	}

	err = unmarshal(response, &st)
	err = unmarshal(body, &st)
	return
}

// ChannelFileSend sends a file to the given channel.
// channelID : The ID of a Channel.
// name: The name of the file.
// io.Reader : A reader for the file contents.
func (s *Session) ChannelFileSend(channelID, name string, r io.Reader) (*Message, error) {
	return s.ChannelMessageSendComplex(channelID, &MessageSend{File: &File{Name: name, Reader: r}})
}

// ChannelFileSendWithMessage sends a file to the given channel with an message.
// DEPRECATED. Use ChannelMessageSendComplex instead.
// channelID : The ID of a Channel.
// content: Optional Message content.
// name: The name of the file.
// io.Reader : A reader for the file contents.
func (s *Session) ChannelFileSendWithMessage(channelID, content string, name string, r io.Reader) (*Message, error) {
	return s.ChannelMessageSendComplex(channelID, &MessageSend{File: &File{Name: name, Reader: r}, Content: content})
}

// ChannelInvites returns an array of Invite structures for the given channel
// channelID   : The ID of a Channel
func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) {

	body, err := s.Request("GET", CHANNEL_INVITES(channelID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointChannelInvites(channelID), nil, EndpointChannelInvites(channelID))
	if err != nil {
		return
	}


@@ 924,13 1574,13 @@ func (s *Session) ChannelInvites(channelID string) (st []*Invite, err error) {
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  bool `json:"xkcdpass"`
		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", CHANNEL_INVITES(channelID), data)
	body, err := s.RequestWithBucketID("POST", EndpointChannelInvites(channelID), data, EndpointChannelInvites(channelID))
	if err != nil {
		return
	}


@@ 951,7 1601,7 @@ func (s *Session) ChannelPermissionSet(channelID, targetID, targetType string, a
		Deny  int    `json:"deny"`
	}{targetID, targetType, allow, deny}

	_, err = s.Request("PUT", CHANNEL_PERMISSION(channelID, targetID), data)
	_, err = s.RequestWithBucketID("PUT", EndpointChannelPermission(channelID, targetID), data, EndpointChannelPermission(channelID, ""))
	return
}



@@ 959,7 1609,7 @@ func (s *Session) ChannelPermissionSet(channelID, targetID, targetType string, a
// NOTE: Name of this func may change.
func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error) {

	_, err = s.Request("DELETE", CHANNEL_PERMISSION(channelID, targetID), nil)
	_, err = s.RequestWithBucketID("DELETE", EndpointChannelPermission(channelID, targetID), nil, EndpointChannelPermission(channelID, ""))
	return
}



@@ 971,7 1621,7 @@ func (s *Session) ChannelPermissionDelete(channelID, targetID string) (err error
// inviteID : The invite code (or maybe xkcdpass?)
func (s *Session) Invite(inviteID string) (st *Invite, err error) {

	body, err := s.Request("GET", INVITE(inviteID), nil)
	body, err := s.RequestWithBucketID("GET", EndpointInvite(inviteID), nil, EndpointInvite(""))
	if err != nil {
		return
	}


@@ 984,7 1634,7 @@ func (s *Session) Invite(inviteID string) (st *Invite, err error) {
// inviteID   : the code (or maybe xkcdpass?) of an invite
func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) {

	body, err := s.Request("DELETE", INVITE(inviteID), nil)
	body, err := s.RequestWithBucketID("DELETE", EndpointInvite(inviteID), nil, EndpointInvite(""))
	if err != nil {
		return
	}


@@ 997,7 1647,7 @@ func (s *Session) InviteDelete(inviteID string) (st *Invite, err error) {
// inviteID : The invite code (or maybe xkcdpass?)
func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) {

	body, err := s.Request("POST", INVITE(inviteID), nil)
	body, err := s.RequestWithBucketID("POST", EndpointInvite(inviteID), nil, EndpointInvite(""))
	if err != nil {
		return
	}


@@ 1013,7 1663,7 @@ func (s *Session) InviteAccept(inviteID string) (st *Invite, err error) {
// VoiceRegions returns the voice server regions
func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) {

	body, err := s.Request("GET", VOICE_REGIONS, nil)
	body, err := s.RequestWithBucketID("GET", EndpointVoiceRegions, nil, EndpointVoiceRegions)
	if err != nil {
		return
	}


@@ 1025,7 1675,7 @@ func (s *Session) VoiceRegions() (st []*VoiceRegion, err error) {
// VoiceICE returns the voice server ICE information
func (s *Session) VoiceICE() (st *VoiceICE, err error) {

	body, err := s.Request("GET", VOICE_ICE, nil)
	body, err := s.RequestWithBucketID("GET", EndpointVoiceIce, nil, EndpointVoiceIce)
	if err != nil {
		return
	}


@@ 1038,10 1688,10 @@ func (s *Session) VoiceICE() (st *VoiceICE, err error) {
// Functions specific to Discord Websockets
// ------------------------------------------------------------------------------------------------

// Gateway returns the a websocket Gateway address
// Gateway returns the websocket Gateway address
func (s *Session) Gateway() (gateway string, err error) {

	response, err := s.Request("GET", GATEWAY, nil)
	response, err := s.RequestWithBucketID("GET", EndpointGateway, nil, EndpointGateway)
	if err != nil {
		return
	}


@@ 1056,5 1706,336 @@ func (s *Session) Gateway() (gateway string, err error) {
	}

	gateway = temp.URL

	// Ensure the gateway always has a trailing slash.
	// MacOS will fail to connect if we add query params without a trailing slash on the base domain.
	if !strings.HasSuffix(gateway, "/") {
		gateway += "/"
	}

	return
}

// GatewayBot returns the websocket Gateway address and the recommended number of shards
func (s *Session) GatewayBot() (st *GatewayBotResponse, err error) {

	response, err := s.RequestWithBucketID("GET", EndpointGatewayBot, nil, EndpointGatewayBot)
	if err != nil {
		return
	}

	err = unmarshal(response, &st)
	if err != nil {
		return
	}

	// Ensure the gateway always has a trailing slash.
	// MacOS will fail to connect if we add query params without a trailing slash on the base domain.
	if !strings.HasSuffix(st.URL, "/") {
		st.URL += "/"
	}

	return
}

// Functions specific to Webhooks

// WebhookCreate returns a new Webhook.
// channelID: The ID of a Channel.
// name     : The name of the webhook.
// avatar   : The avatar of the webhook.
func (s *Session) WebhookCreate(channelID, name, avatar string) (st *Webhook, err error) {

	data := struct {
		Name   string `json:"name"`
		Avatar string `json:"avatar,omitempty"`
	}{name, avatar}

	body, err := s.RequestWithBucketID("POST", EndpointChannelWebhooks(channelID), data, EndpointChannelWebhooks(channelID))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// ChannelWebhooks returns all webhooks for a given channel.
// channelID: The ID of a channel.
func (s *Session) ChannelWebhooks(channelID string) (st []*Webhook, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointChannelWebhooks(channelID), nil, EndpointChannelWebhooks(channelID))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// GuildWebhooks returns all webhooks for a given guild.
// guildID: The ID of a Guild.
func (s *Session) GuildWebhooks(guildID string) (st []*Webhook, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointGuildWebhooks(guildID), nil, EndpointGuildWebhooks(guildID))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// Webhook returns a webhook for a given ID
// webhookID: The ID of a webhook.
func (s *Session) Webhook(webhookID string) (st *Webhook, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointWebhook(webhookID), nil, EndpointWebhooks)
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// WebhookWithToken returns a webhook for a given ID
// webhookID: The ID of a webhook.
// token    : The auth token for the webhook.
func (s *Session) WebhookWithToken(webhookID, token string) (st *Webhook, err error) {

	body, err := s.RequestWithBucketID("GET", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", ""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// WebhookEdit updates an existing Webhook.
// webhookID: The ID of a webhook.
// name     : The name of the webhook.
// avatar   : The avatar of the webhook.
func (s *Session) WebhookEdit(webhookID, name, avatar string) (st *Role, err error) {

	data := struct {
		Name   string `json:"name,omitempty"`
		Avatar string `json:"avatar,omitempty"`
	}{name, avatar}

	body, err := s.RequestWithBucketID("PATCH", EndpointWebhook(webhookID), data, EndpointWebhooks)
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// WebhookEditWithToken updates an existing Webhook with an auth token.
// webhookID: The ID of a webhook.
// token    : The auth token for the webhook.
// name     : The name of the webhook.
// avatar   : The avatar of the webhook.
func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string) (st *Role, err error) {

	data := struct {
		Name   string `json:"name,omitempty"`
		Avatar string `json:"avatar,omitempty"`
	}{name, avatar}

	body, err := s.RequestWithBucketID("PATCH", EndpointWebhookToken(webhookID, token), data, EndpointWebhookToken("", ""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// WebhookDelete deletes a webhook for a given ID
// webhookID: The ID of a webhook.
func (s *Session) WebhookDelete(webhookID string) (err error) {

	_, err = s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks)

	return
}

// WebhookDeleteWithToken deletes a webhook for a given ID with an auth token.
// webhookID: The ID of a webhook.
// token    : The auth token for the webhook.
func (s *Session) WebhookDeleteWithToken(webhookID, token string) (st *Webhook, err error) {

	body, err := s.RequestWithBucketID("DELETE", EndpointWebhookToken(webhookID, token), nil, EndpointWebhookToken("", ""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)

	return
}

// WebhookExecute executes a webhook.
// webhookID: The ID of a webhook.
// token    : The auth token for the webhook
func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *WebhookParams) (err error) {
	uri := EndpointWebhookToken(webhookID, token)

	if wait {
		uri += "?wait=true"
	}

	_, err = s.RequestWithBucketID("POST", uri, data, EndpointWebhookToken("", ""))

	return
}

// MessageReactionAdd creates an emoji reaction to a message.
// channelID : The channel ID.
// messageID : The message ID.
// emojiID   : Either the unicode emoji for the reaction, or a guild emoji identifier.
func (s *Session) MessageReactionAdd(channelID, messageID, emojiID string) error {

	_, err := s.RequestWithBucketID("PUT", EndpointMessageReaction(channelID, messageID, emojiID, "@me"), nil, EndpointMessageReaction(channelID, "", "", ""))

	return err
}

// MessageReactionRemove deletes an emoji reaction to a message.
// channelID : The channel ID.
// messageID : The message ID.
// emojiID   : Either the unicode emoji for the reaction, or a guild emoji identifier.
// userID	 : @me or ID of the user to delete the reaction for.
func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID string) error {

	_, err := s.RequestWithBucketID("DELETE", EndpointMessageReaction(channelID, messageID, emojiID, userID), nil, EndpointMessageReaction(channelID, "", "", ""))

	return err
}

// MessageReactionsRemoveAll deletes all reactions from a message
// channelID : The channel ID
// messageID : The message ID.
func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error {

	_, err := s.RequestWithBucketID("DELETE", EndpointMessageReactionsAll(channelID, messageID), nil, EndpointMessageReactionsAll(channelID, messageID))

	return err
}

// MessageReactions gets all the users reactions for a specific emoji.
// channelID : The channel ID.
// messageID : The message ID.
// emojiID   : Either the unicode emoji for the reaction, or a guild emoji identifier.
// limit    : max number of users to return (max 100)
func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int) (st []*User, err error) {
	uri := EndpointMessageReactions(channelID, messageID, emojiID)

	v := url.Values{}

	if limit > 0 {
		v.Set("limit", strconv.Itoa(limit))
	}

	if len(v) > 0 {
		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
	}

	body, err := s.RequestWithBucketID("GET", uri, nil, EndpointMessageReaction(channelID, "", "", ""))
	if err != nil {
		return
	}

	err = unmarshal(body, &st)
	return
}

// ------------------------------------------------------------------------------------------------
// Functions specific to user notes
// ------------------------------------------------------------------------------------------------

// UserNoteSet sets the note for a specific user.
func (s *Session) UserNoteSet(userID string, message string) (err error) {
	data := struct {
		Note string `json:"note"`
	}{message}

	_, err = s.RequestWithBucketID("PUT", EndpointUserNotes(userID), data, EndpointUserNotes(""))
	return
}

// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Relationships (Friends list)
// ------------------------------------------------------------------------------------------------

// RelationshipsGet returns an array of all the relationships of the user.
func (s *Session) RelationshipsGet() (r []*Relationship, err error) {
	body, err := s.RequestWithBucketID("GET", EndpointRelationships(), nil, EndpointRelationships())
	if err != nil {
		return
	}

	err = unmarshal(body, &r)
	return
}

// relationshipCreate creates a new relationship. (I.e. send or accept a friend request, block a user.)
// relationshipType : 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req
func (s *Session) relationshipCreate(userID string, relationshipType int) (err error) {
	data := struct {
		Type int `json:"type"`
	}{relationshipType}

	_, err = s.RequestWithBucketID("PUT", EndpointRelationship(userID), data, EndpointRelationships())
	return
}

// RelationshipFriendRequestSend sends a friend request to a user.
// userID: ID of the user.
func (s *Session) RelationshipFriendRequestSend(userID string) (err error) {
	err = s.relationshipCreate(userID, 4)
	return
}

// RelationshipFriendRequestAccept accepts a friend request from a user.
// userID: ID of the user.
func (s *Session) RelationshipFriendRequestAccept(userID string) (err error) {
	err = s.relationshipCreate(userID, 1)
	return
}

// RelationshipUserBlock blocks a user.
// userID: ID of the user.
func (s *Session) RelationshipUserBlock(userID string) (err error) {
	err = s.relationshipCreate(userID, 2)
	return
}

// RelationshipDelete removes the relationship with a user.
// userID: ID of the user.
func (s *Session) RelationshipDelete(userID string) (err error) {
	_, err = s.RequestWithBucketID("DELETE", EndpointRelationship(userID), nil, EndpointRelationships())
	return
}

// RelationshipsMutualGet returns an array of all the users both @me and the given user is friends with.
// userID: ID of the user.
func (s *Session) RelationshipsMutualGet(userID string) (mf []*User, err error) {
	body, err := s.RequestWithBucketID("GET", EndpointRelationshipsMutual(userID), nil, EndpointRelationshipsMutual(userID))
	if err != nil {
		return
	}

	err = unmarshal(body, &mf)
	return
}

M DiscordGo/restapi_test.go => DiscordGo/restapi_test.go +139 -3
@@ 24,14 24,22 @@ func TestChannelMessageSend(t *testing.T) {
	}
}

/*
// removed for now, only works on BOT accounts now
func TestUserAvatar(t *testing.T) {

	if dg == nil {
		t.Skip("Cannot TestUserAvatar, dg not set.")
	}

	a, err := dg.UserAvatar("@me")
	u, err := dg.User("@me")
	if err != nil {
		t.Error("error fetching @me user,", err)
	}

	a, err := dg.UserAvatar(u.ID)
	if err != nil {
		if err.Error() == `HTTP 404 NOT FOUND, {"message": ""}` {
		if err.Error() == `HTTP 404 NOT FOUND, {"code": 0, "message": "404: Not Found"}` {
			t.Skip("Skipped, @me doesn't have an Avatar")
		}
		t.Errorf(err.Error())


@@ 41,7 49,9 @@ func TestUserAvatar(t *testing.T) {
		t.Errorf("a == nil, should be image.Image")
	}
}
*/

/* Running this causes an error due to 2/hour rate limit on username changes
func TestUserUpdate(t *testing.T) {
	if dg == nil {
		t.Skip("Cannot test logout, dg not set.")


@@ 67,6 77,7 @@ func TestUserUpdate(t *testing.T) {
		t.Error("Username != " + u.Username)
	}
}
*/

//func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) {



@@ 103,7 114,7 @@ func TestUserGuilds(t *testing.T) {
		t.Skip("Cannot TestUserGuilds, dg not set.")
	}

	_, err := dg.UserGuilds()
	_, err := dg.UserGuilds(10, "", "")
	if err != nil {
		t.Errorf(err.Error())
	}


@@ 120,6 131,17 @@ func TestUserSettings(t *testing.T) {
	}
}

func TestUserUpdateStatus(t *testing.T) {
	if dg == nil {
		t.Skip("Cannot TestUserSettings, dg not set.")
	}

	_, err := dg.UserUpdateStatus(StatusDoNotDisturb)
	if err != nil {
		t.Errorf(err.Error())
	}
}

// TestLogout tests the Logout() function. This should not return an error.
func TestLogout(t *testing.T) {



@@ 133,6 155,84 @@ func TestLogout(t *testing.T) {
	}
}

func TestGateway(t *testing.T) {

	if dg == nil {
		t.Skip("Skipping, dg not set.")
	}
	_, err := dg.Gateway()
	if err != nil {
		t.Errorf("Gateway() returned error: %+v", err)
	}
}

func TestGatewayBot(t *testing.T) {

	if dgBot == nil {
		t.Skip("Skipping, dgBot not set.")
	}
	_, err := dgBot.GatewayBot()
	if err != nil {
		t.Errorf("GatewayBot() returned error: %+v", err)
	}
}

func TestVoiceICE(t *testing.T) {

	if dg == nil {
		t.Skip("Skipping, dg not set.")
	}

	_, err := dg.VoiceICE()
	if err != nil {
		t.Errorf("VoiceICE() returned error: %+v", err)
	}
}

func TestVoiceRegions(t *testing.T) {

	if dg == nil {
		t.Skip("Skipping, dg not set.")
	}

	_, err := dg.VoiceRegions()
	if err != nil {
		t.Errorf("VoiceRegions() returned error: %+v", err)
	}
}
func TestGuildRoles(t *testing.T) {

	if envGuild == "" {
		t.Skip("Skipping, DG_GUILD not set.")
	}

	if dg == nil {
		t.Skip("Skipping, dg not set.")
	}

	_, err := dg.GuildRoles(envGuild)
	if err != nil {
		t.Errorf("GuildRoles(envGuild) returned error: %+v", err)
	}

}

func TestGuildMemberNickname(t *testing.T) {

	if envGuild == "" {
		t.Skip("Skipping, DG_GUILD not set.")
	}

	if dg == nil {
		t.Skip("Skipping, dg not set.")
	}

	err := dg.GuildMemberNickname(envGuild, "@me/nick", "testnickname")
	if err != nil {
		t.Errorf("GuildNickname returned error: %+v", err)
	}
}

// TestChannelMessageSend2 tests the ChannelMessageSend() function. This should not return an error.
func TestChannelMessageSend2(t *testing.T) {



@@ 149,3 249,39 @@ func TestChannelMessageSend2(t *testing.T) {
		t.Errorf("ChannelMessageSend returned error: %+v", err)
	}
}

// TestGuildPruneCount tests GuildPruneCount() function. This should not return an error.
func TestGuildPruneCount(t *testing.T) {

	if envGuild == "" {
		t.Skip("Skipping, DG_GUILD not set.")
	}

	if dg == nil {
		t.Skip("Skipping, dg not set.")
	}

	_, err := dg.GuildPruneCount(envGuild, 1)
	if err != nil {
		t.Errorf("GuildPruneCount returned error: %+v", err)
	}
}

/*
// TestGuildPrune tests GuildPrune() function. This should not return an error.
func TestGuildPrune(t *testing.T) {

	if envGuild == "" {
		t.Skip("Skipping, DG_GUILD not set.")
	}

	if dg == nil {
		t.Skip("Skipping, dg not set.")
	}

	_, err := dg.GuildPrune(envGuild, 1)
	if err != nil {
		t.Errorf("GuildPrune returned error: %+v", err)
	}
}
*/

M DiscordGo/state.go => DiscordGo/state.go +602 -139
@@ 14,11 14,36 @@ package discordgo

import (
	"errors"
	"fmt"
	"sort"
	"sync"
)

// ErrNilState is returned when the state is nil.
var ErrNilState = errors.New("State not instantiated, please use discordgo.New() or assign Session.State.")
var ErrNilState = errors.New("state not instantiated, please use discordgo.New() or assign Session.State")

// ErrStateNotFound is returned when the state cache
// requested is not found
var ErrStateNotFound = errors.New("state cache not found")

// A State contains the current known state.
// As discord sends this in a READY blob, it seems reasonable to simply
// use that struct as the data store.
type State struct {
	sync.RWMutex
	Ready

	MaxMessageCount int
	TrackChannels   bool
	TrackEmojis     bool
	TrackMembers    bool
	TrackRoles      bool
	TrackVoice      bool
	TrackPresences  bool

	guildMap   map[string]*Guild
	channelMap map[string]*Channel
	memberMap  map[string]map[string]*Member
}

// NewState creates an empty state.
func NewState() *State {


@@ 27,20 52,24 @@ func NewState() *State {
			PrivateChannels: []*Channel{},
			Guilds:          []*Guild{},
		},
		TrackChannels:  true,
		TrackEmojis:    true,
		TrackMembers:   true,
		TrackRoles:     true,
		TrackVoice:     true,
		TrackPresences: true,
		guildMap:       make(map[string]*Guild),
		channelMap:     make(map[string]*Channel),
		memberMap:      make(map[string]map[string]*Member),
	}
}

// OnReady takes a Ready event and updates all internal state.
func (s *State) OnReady(r *Ready) error {
	if s == nil {
		return ErrNilState
func (s *State) createMemberMap(guild *Guild) {
	members := make(map[string]*Member)
	for _, m := range guild.Members {
		members[m.User.ID] = m
	}

	s.Lock()
	defer s.Unlock()

	s.Ready = *r
	return nil
	s.memberMap[guild.ID] = members
}

// GuildAdd adds a guild to the current world state, or


@@ 53,20 82,47 @@ func (s *State) GuildAdd(guild *Guild) error {
	s.Lock()
	defer s.Unlock()

	// If the guild exists, replace it.
	for i, g := range s.Guilds {
		if g.ID == guild.ID {
			// Don't stomp on properties that don't come in updates.
	// Update the channels to point to the right guild, adding them to the channelMap as we go
	for _, c := range guild.Channels {
		s.channelMap[c.ID] = c
	}

	// If this guild contains a new member slice, we must regenerate the member map so the pointers stay valid
	if guild.Members != nil {
		s.createMemberMap(guild)
	} else if _, ok := s.memberMap[guild.ID]; !ok {
		// Even if we have no new member slice, we still initialize the member map for this guild if it doesn't exist
		s.memberMap[guild.ID] = make(map[string]*Member)
	}

	if g, ok := s.guildMap[guild.ID]; ok {
		// We are about to replace `g` in the state with `guild`, but first we need to
		// make sure we preserve any fields that the `guild` doesn't contain from `g`.
		if guild.Roles == nil {
			guild.Roles = g.Roles
		}
		if guild.Emojis == nil {
			guild.Emojis = g.Emojis
		}
		if guild.Members == nil {
			guild.Members = g.Members
		}
		if guild.Presences == nil {
			guild.Presences = g.Presences
		}
		if guild.Channels == nil {
			guild.Channels = g.Channels
		}
		if guild.VoiceStates == nil {
			guild.VoiceStates = g.VoiceStates
			s.Guilds[i] = guild
			return nil
		}
		*g = *guild
		return nil
	}

	s.Guilds = append(s.Guilds, guild)
	s.guildMap[guild.ID] = guild

	return nil
}



@@ 76,9 132,16 @@ func (s *State) GuildRemove(guild *Guild) error {
		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:]...)


@@ 86,7 149,7 @@ func (s *State) GuildRemove(guild *Guild) error {
		}
	}

	return errors.New("Guild not found.")
	return nil
}

// Guild gets a guild by ID.


@@ 101,13 164,112 @@ func (s *State) Guild(guildID string) (*Guild, error) {
	s.RLock()
	defer s.RUnlock()

	for _, g := range s.Guilds {
		if g.ID == guildID {
			return g, nil
	if g, ok := s.guildMap[guildID]; ok {
		return g, nil
	}

	return nil, ErrStateNotFound
}

// PresenceAdd adds a presence to the current world state, or
// updates it if it already exists.
func (s *State) PresenceAdd(guildID string, presence *Presence) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, p := range guild.Presences {
		if p.User.ID == presence.User.ID {
			//guild.Presences[i] = presence

			//Update status
			guild.Presences[i].Game = presence.Game
			guild.Presences[i].Roles = presence.Roles
			if presence.Status != "" {
				guild.Presences[i].Status = presence.Status
			}
			if presence.Nick != "" {
				guild.Presences[i].Nick = presence.Nick
			}

			//Update the optionally sent user information
			//ID Is a mandatory field so you should not need to check if it is empty
			guild.Presences[i].User.ID = presence.User.ID

			if presence.User.Avatar != "" {
				guild.Presences[i].User.Avatar = presence.User.Avatar
			}
			if presence.User.Discriminator != "" {
				guild.Presences[i].User.Discriminator = presence.User.Discriminator
			}
			if presence.User.Email != "" {
				guild.Presences[i].User.Email = presence.User.Email
			}
			if presence.User.Token != "" {
				guild.Presences[i].User.Token = presence.User.Token
			}
			if presence.User.Username != "" {
				guild.Presences[i].User.Username = presence.User.Username
			}

			return nil
		}
	}

	return nil, errors.New("Guild not found.")
	guild.Presences = append(guild.Presences, presence)
	return nil
}

// PresenceRemove removes a presence from the current world state.
func (s *State) PresenceRemove(guildID string, presence *Presence) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, p := range guild.Presences {
		if p.User.ID == presence.User.ID {
			guild.Presences = append(guild.Presences[:i], guild.Presences[i+1:]...)
			return nil
		}
	}

	return ErrStateNotFound
}

// Presence gets a presence by ID from a guild.
func (s *State) Presence(guildID, userID string) (*Presence, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	for _, p := range guild.Presences {
		if p.User.ID == userID {
			return p, nil
		}
	}

	return nil, ErrStateNotFound
}

// TODO: Consider moving Guild state update methods onto *Guild.


@@ 127,14 289,19 @@ func (s *State) MemberAdd(member *Member) error {
	s.Lock()
	defer s.Unlock()

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members[i] = member
			return nil
		}
	members, ok := s.memberMap[member.GuildID]
	if !ok {
		return ErrStateNotFound
	}

	m, ok := members[member.User.ID]
	if !ok {
		members[member.User.ID] = member
		guild.Members = append(guild.Members, member)
	} else {
		*m = *member // Update the actual data, which will also update the member pointer in the slice
	}

	guild.Members = append(guild.Members, member)
	return nil
}



@@ 152,6 319,17 @@ func (s *State) MemberRemove(member *Member) error {
	s.Lock()
	defer s.Unlock()

	members, ok := s.memberMap[member.GuildID]
	if !ok {
		return ErrStateNotFound
	}

	_, ok = members[member.User.ID]
	if !ok {
		return ErrStateNotFound
	}
	delete(members, member.User.ID)

	for i, m := range guild.Members {
		if m.User.ID == member.User.ID {
			guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)


@@ 159,7 337,7 @@ func (s *State) MemberRemove(member *Member) error {
		}
	}

	return errors.New("Member not found.")
	return ErrStateNotFound
}

// Member gets a member by ID from a guild.


@@ 168,6 346,78 @@ func (s *State) Member(guildID, userID string) (*Member, error) {
		return nil, ErrNilState
	}

	s.RLock()
	defer s.RUnlock()

	members, ok := s.memberMap[guildID]
	if !ok {
		return nil, ErrStateNotFound
	}

	m, ok := members[userID]
	if ok {
		return m, nil
	}

	return nil, ErrStateNotFound
}

// RoleAdd adds a role to the current world state, or
// updates it if it already exists.
func (s *State) RoleAdd(guildID string, role *Role) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, r := range guild.Roles {
		if r.ID == role.ID {
			guild.Roles[i] = role
			return nil
		}
	}

	guild.Roles = append(guild.Roles, role)
	return nil
}

// RoleRemove removes a role from current world state by ID.
func (s *State) RoleRemove(guildID, roleID string) error {
	if s == nil {
		return ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	for i, r := range guild.Roles {
		if r.ID == roleID {
			guild.Roles = append(guild.Roles[:i], guild.Roles[i+1:]...)
			return nil
		}
	}

	return ErrStateNotFound
}

// Role gets a role by ID from a guild.
func (s *State) Role(guildID, roleID string) (*Role, error) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err


@@ 176,16 426,16 @@ func (s *State) Member(guildID, userID string) (*Member, error) {
	s.RLock()
	defer s.RUnlock()

	for _, m := range guild.Members {
		if m.User.ID == userID {
			return m, nil
	for _, r := range guild.Roles {
		if r.ID == roleID {
			return r, nil
		}
	}

	return nil, errors.New("Member not found.")
	return nil, ErrStateNotFound
}

// ChannelAdd adds a guild to the current world state, or
// ChannelAdd adds a channel to the current world state, or
// updates it if it already exists.
// Channels may exist either as PrivateChannels or inside
// a guild.


@@ 194,43 444,35 @@ func (s *State) ChannelAdd(channel *Channel) error {
		return ErrNilState
	}

	if channel.IsPrivate {
		s.Lock()
		defer s.Unlock()
	s.Lock()
	defer s.Unlock()

		// If the channel exists, replace it.
		for i, c := range s.PrivateChannels {
			if c.ID == channel.ID {
				// Don't stomp on messages.
				channel.Messages = c.Messages
				s.PrivateChannels[i] = channel
				return nil
			}
	// If the channel exists, replace it
	if c, ok := s.channelMap[channel.ID]; ok {
		if channel.Messages == nil {
			channel.Messages = c.Messages
		}

		s.PrivateChannels = append(s.PrivateChannels, channel)
	} else {
		guild, err := s.Guild(channel.GuildID)
		if err != nil {
			return err
		if channel.PermissionOverwrites == nil {
			channel.PermissionOverwrites = c.PermissionOverwrites
		}

		s.Lock()
		defer s.Unlock()
		*c = *channel
		return nil
	}

		// If the channel exists, replace it.
		for i, c := range guild.Channels {
			if c.ID == channel.ID {
				// Don't stomp on messages.
				channel.Messages = c.Messages
				guild.Channels[i] = channel
				return nil
			}
	if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
		s.PrivateChannels = append(s.PrivateChannels, channel)
	} else {
		guild, ok := s.guildMap[channel.GuildID]
		if !ok {
			return ErrStateNotFound
		}

		guild.Channels = append(guild.Channels, channel)
	}

	s.channelMap[channel.ID] = channel

	return nil
}



@@ 240,14 482,19 @@ func (s *State) ChannelRemove(channel *Channel) error {
		return ErrNilState
	}

	if channel.IsPrivate {
	_, err := s.Channel(channel.ID)
	if err != nil {
		return err
	}

	if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
		s.Lock()
		defer s.Unlock()

		for i, c := range s.PrivateChannels {
			if c.ID == channel.ID {
				s.PrivateChannels = append(s.PrivateChannels[:i], s.PrivateChannels[i+1:]...)
				return nil
				break
			}
		}
	} else {


@@ 262,53 509,26 @@ func (s *State) ChannelRemove(channel *Channel) error {
		for i, c := range guild.Channels {
			if c.ID == channel.ID {
				guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
				return nil
				break
			}
		}
	}

	return errors.New("Channel not found.")
	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) {
	if s == nil {
		return nil, ErrNilState
	}

	guild, err := s.Guild(guildID)
	if err != nil {
		return nil, err
	}

	s.RLock()
	defer s.RUnlock()

	for _, c := range guild.Channels {
		if c.ID == channelID {
			return c, nil
		}
	}

	return nil, errors.New("Channel not found.")
	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) {
	if s == nil {
		return nil, ErrNilState
	}

	s.RLock()
	defer s.RUnlock()

	for _, c := range s.PrivateChannels {
		if c.ID == channelID {
			return c, nil
		}
	}

	return nil, errors.New("Channel not found.")
	return s.Channel(channelID)
}

// Channel gets a channel by ID, it will look in all guilds an private channels.


@@ 317,19 537,14 @@ func (s *State) Channel(channelID string) (*Channel, error) {
		return nil, ErrNilState
	}

	c, err := s.PrivateChannel(channelID)
	if err == nil {
		return c, nil
	}
	s.RLock()
	defer s.RUnlock()

	for _, g := range s.Guilds {
		c, err := s.GuildChannel(g.ID, channelID)
		if err == nil {
			return c, nil
		}
	if c, ok := s.channelMap[channelID]; ok {
		return c, nil
	}

	return nil, errors.New("Channel not found.")
	return nil, ErrStateNotFound
}

// Emoji returns an emoji for a guild and emoji id.


@@ 352,7 567,7 @@ func (s *State) Emoji(guildID, emojiID string) (*Emoji, error) {
		}
	}

	return nil, errors.New("Emoji not found.")
	return nil, ErrStateNotFound
}

// EmojiAdd adds an emoji to the current world state.


@@ 406,10 621,31 @@ func (s *State) MessageAdd(message *Message) error {
	s.Lock()
	defer s.Unlock()

	// If the message exists, replace it.
	for i, m := range c.Messages {
	// If the message exists, merge in the new message contents.
	for _, m := range c.Messages {
		if m.ID == message.ID {
			c.Messages[i] = message
			if message.Content != "" {
				m.Content = message.Content
			}
			if message.EditedTimestamp != "" {
				m.EditedTimestamp = message.EditedTimestamp
			}
			if message.Mentions != nil {
				m.Mentions = message.Mentions
			}
			if message.Embeds != nil {
				m.Embeds = message.Embeds
			}
			if message.Attachments != nil {
				m.Attachments = message.Attachments
			}
			if message.Timestamp != "" {
				m.Timestamp = message.Timestamp
			}
			if message.Author != nil {
				m.Author = message.Author
			}

			return nil
		}
	}


@@ 417,14 653,7 @@ func (s *State) MessageAdd(message *Message) error {
	c.Messages = append(c.Messages, message)

	if len(c.Messages) > s.MaxMessageCount {
		s.Unlock()
		for len(c.Messages) > s.MaxMessageCount {
			err := s.MessageRemove(c.Messages[0])
			if err != nil {
				fmt.Println("message remove error: ", err)
			}
		}
		s.Lock()
		c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:]
	}
	return nil
}


@@ 435,7 664,12 @@ func (s *State) MessageRemove(message *Message) error {
		return ErrNilState
	}

	c, err := s.Channel(message.ChannelID)
	return s.messageRemoveByID(message.ChannelID, message.ID)
}

// messageRemoveByID removes a message by channelID and messageID from the world state.
func (s *State) messageRemoveByID(channelID, messageID string) error {
	c, err := s.Channel(channelID)
	if err != nil {
		return err
	}


@@ 444,13 678,44 @@ func (s *State) MessageRemove(message *Message) error {
	defer s.Unlock()

	for i, m := range c.Messages {
		if m.ID == message.ID {
		if m.ID == messageID {
			c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
			return nil
		}
	}

	return errors.New("Message not found.")
	return ErrStateNotFound
}

func (s *State) voiceStateUpdate(update *VoiceStateUpdate) error {
	guild, err := s.Guild(update.GuildID)
	if err != nil {
		return err
	}

	s.Lock()
	defer s.Unlock()

	// Handle Leaving Channel
	if update.ChannelID == "" {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates = append(guild.VoiceStates[:i], guild.VoiceStates[i+1:]...)
				return nil
			}
		}
	} else {
		for i, state := range guild.VoiceStates {
			if state.UserID == update.UserID {
				guild.VoiceStates[i] = update.VoiceState
				return nil
			}
		}

		guild.VoiceStates = append(guild.VoiceStates, update.VoiceState)
	}

	return nil
}

// Message gets a message by channel and message ID.


@@ 473,21 738,66 @@ func (s *State) Message(channelID, messageID string) (*Message, error) {
		}
	}

	return nil, errors.New("Message not found.")
	return nil, ErrStateNotFound
}

// onInterface handles all events related to states.
func (s *State) onInterface(se *Session, i interface{}) (err error) {
// OnReady takes a Ready event and updates all internal state.
func (s *State) onReady(se *Session, r *Ready) (err error) {
	if s == nil {
		return ErrNilState
	}

	s.Lock()
	defer s.Unlock()

	// We must track at least the current user for Voice, even
	// if state is disabled, store the bare essentials.
	if !se.StateEnabled {
		ready := Ready{
			Version:   r.Version,
			SessionID: r.SessionID,
			User:      r.User,
		}

		s.Ready = ready

		return nil
	}

	s.Ready = *r

	for _, g := range s.Guilds {
		s.guildMap[g.ID] = g
		s.createMemberMap(g)

		for _, c := range g.Channels {
			s.channelMap[c.ID] = c
		}
	}

	for _, c := range s.PrivateChannels {
		s.channelMap[c.ID] = c
	}

	return nil
}

// OnInterface handles all events related to states.
func (s *State) OnInterface(se *Session, i interface{}) (err error) {
	if s == nil {
		return ErrNilState
	}

	r, ok := i.(*Ready)
	if ok {
		return s.onReady(se, r)
	}

	if !se.StateEnabled {
		return nil
	}

	switch t := i.(type) {
	case *Ready:
		err = s.OnReady(t)
	case *GuildCreate:
		err = s.GuildAdd(t.Guild)
	case *GuildUpdate:


@@ 495,26 805,179 @@ func (s *State) onInterface(se *Session, i interface{}) (err error) {
	case *GuildDelete:
		err = s.GuildRemove(t.Guild)
	case *GuildMemberAdd:
		err = s.MemberAdd(t.Member)
		if s.TrackMembers {
			err = s.MemberAdd(t.Member)
		}
	case *GuildMemberUpdate:
		err = s.MemberAdd(t.Member)
		if s.TrackMembers {
			err = s.MemberAdd(t.Member)
		}
	case *GuildMemberRemove:
		err = s.MemberRemove(t.Member)
		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:
		err = s.EmojisAdd(t.GuildID, t.Emojis)
		if s.TrackEmojis {
			err = s.EmojisAdd(t.GuildID, t.Emojis)
		}
	case *ChannelCreate:
		err = s.ChannelAdd(t.Channel)
		if s.TrackChannels {
			err = s.ChannelAdd(t.Channel)
		}
	case *ChannelUpdate:
		err = s.ChannelAdd(t.Channel)
		if s.TrackChannels {
			err = s.ChannelAdd(t.Channel)
		}
	case *ChannelDelete:
		err = s.ChannelRemove(t.Channel)
		if s.TrackChannels {
			err = s.ChannelRemove(t.Channel)
		}
	case *MessageCreate:
		err = s.MessageAdd(t.Message)
		if s.MaxMessageCount != 0 {
			err = s.MessageAdd(t.Message)
		}
	case *MessageUpdate:
		err = s.MessageAdd(t.Message)
		if s.MaxMessageCount != 0 {
			err = s.MessageAdd(t.Message)
		}
	case *MessageDelete:
		err = s.MessageRemove(t.Message)
		if s.MaxMessageCount != 0 {
			err = s.MessageRemove(t.Message)
		}
	case *MessageDeleteBulk:
		if s.MaxMessageCount != 0 {
			for _, mID := range t.Messages {
				s.messageRemoveByID(t.ChannelID, mID)
			}
		}
	case *VoiceStateUpdate:
		if s.TrackVoice {
			err = s.voiceStateUpdate(t)
		}
	case *PresenceUpdate:
		if s.TrackPresences {
			s.PresenceAdd(t.GuildID, &t.Presence)
		}
		if s.TrackMembers {
			if t.Status == StatusOffline {
				return
			}

			var m *Member
			m, err = s.Member(t.GuildID, t.User.ID)

			if err != nil {
				// Member not found; this is a user coming online
				m = &Member{
					GuildID: t.GuildID,
					Nick:    t.Nick,
					User:    t.User,
					Roles:   t.Roles,
				}

			} else {

				if t.Nick != "" {
					m.Nick = t.Nick
				}

				if t.User.Username != "" {
					m.User.Username = t.User.Username
				}

				// PresenceUpdates always contain a list of roles, so there's no need to check for an empty list here
				m.Roles = t.Roles

			}

			err = s.MemberAdd(m)
		}

	}

	return
}

// UserChannelPermissions returns the permission of a user in a channel.
// userID    : The ID of the user to calculate permissions for.
// channelID : The ID of the channel to calculate permission for.
func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) {
	if s == nil {
		return 0, ErrNilState
	}

	channel, err := s.Channel(channelID)
	if err != nil {
		return
	}

	guild, err := s.Guild(channel.GuildID)
	if err != nil {
		return
	}

	if userID == guild.OwnerID {
		apermissions = PermissionAll
		return
	}

	member, err := s.Member(guild.ID, userID)
	if err != nil {
		return
	}

	return memberPermissions(guild, channel, member), nil
}

// UserColor returns the color of a user in a channel.
// While colors are defined at a Guild level, determining for a channel is more useful in message handlers.
// 0 is returned in cases of error, which is the color of @everyone.
// userID    : The ID of the user to calculate the color for.
// channelID   : The ID of the channel to calculate the color for.
func (s *State) UserColor(userID, channelID string) int {
	if s == nil {
		return 0
	}

	channel, err := s.Channel(channelID)
	if err != nil {
		return 0
	}

	guild, err := s.Guild(channel.GuildID)
	if err != nil {
		return 0
	}

	member, err := s.Member(guild.ID, userID)
	if err != nil {
		return 0
	}

	roles := Roles(guild.Roles)
	sort.Sort(roles)

	for _, role := range roles {
		for _, roleID := range member.Roles {
			if role.ID == roleID {
				if role.Color != 0 {
					return role.Color
				}
			}
		}
	}

	return 0
}

M DiscordGo/structs.go => DiscordGo/structs.go +466 -147
@@ 13,11 13,12 @@ package discordgo

import (
	"encoding/json"
	"reflect"
	"net/http"
	"strconv"
	"sync"
	"time"

	"bitbucket.org/henesy/disco/websocket"
	"github.com/gorilla/websocket"
)

// A Session represents a connection to the Discord API.


@@ 28,9 29,11 @@ type Session struct {

	// Authentication token for this session
	Token string
	MFA   bool

	// Debug for printing JSON request/responses
	Debug bool
	Debug    bool // Deprecated, will be removed.
	LogLevel int

	// Should the session reconnect the websocket on errors.
	ShouldReconnectOnError bool


@@ 38,42 41,75 @@ type Session struct {
	// Should the session request compressed websocket data.
	Compress bool

	// Sharding
	ShardID    int
	ShardCount int

	// Should state tracking be enabled.
	// State tracking is the best way for getting the the users
	// active guilds and the members of the guilds.
	StateEnabled bool

	// Whether or not to call event handlers synchronously.
	// e.g false = launch event handlers in their own goroutines.
	SyncEvents bool

	// Exposed but should not be modified by User.

	// Whether the Data Websocket is ready
	DataReady bool
	DataReady bool // NOTE: Maye be deprecated soon

	// Max number of REST API retries
	MaxRestRetries int

	// Status stores the currect status of the websocket connection
	// this is being tested, may stay, may go away.
	status int32

	// Whether the Voice Websocket is ready
	VoiceReady bool
	VoiceReady bool // NOTE: Deprecated.

	// Whether the UDP Connection is ready
	UDPReady bool
	UDPReady bool // NOTE: Deprecated

	// Stores all details related to voice connections
	Voice *Voice
	// 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 http client used for REST requests
	Client *http.Client

	// Stores the last HeartbeatAck that was recieved (in UTC)
	LastHeartbeatAck time.Time

	// Event handlers
	handlersMu   sync.RWMutex
	handlers     map[string][]*eventHandlerInstance
	onceHandlers map[string][]*eventHandlerInstance

	// The websocket connection.
	wsConn *websocket.Conn

	// When nil, the session is not listening.
	listening chan interface{}

	// used to deal with rate limits
	ratelimiter *RateLimiter

	// sequence tracks the current gateway api websocket sequence number
	sequence *int64

	// stores sessions current Discord Gateway
	gateway string

	// stores session ID of current Gateway connection
	sessionID string

	// used to make sure gateway websocket writes do not happen concurrently
	wsMutex sync.Mutex
}

// A VoiceRegion stores data for a specific voice region server.


@@ 99,33 135,45 @@ type ICEServer struct {

// 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  bool     `json:"xkcdpass"`
	Revoked   bool     `json:"revoked"`
	Temporary bool     `json:"temporary"`
	Guild     *Guild    `json:"guild"`
	Channel   *Channel  `json:"channel"`
	Inviter   *User     `json:"inviter"`
	Code      string    `json:"code"`
	CreatedAt Timestamp `json:"created_at"`
	MaxAge    int       `json:"max_age"`
	Uses      int       `json:"uses"`
	MaxUses   int       `json:"max_uses"`
	XkcdPass  string    `json:"xkcdpass"`
	Revoked   bool      `json:"revoked"`
	Temporary bool      `json:"temporary"`
}

// ChannelType is the type of a Channel
type ChannelType int

// Block contains known ChannelType values
const (
	ChannelTypeGuildText ChannelType = iota
	ChannelTypeDM
	ChannelTypeGuildVoice
	ChannelTypeGroupDM
	ChannelTypeGuildCategory
)

// A Channel holds all data related to an individual Discord channel.
type Channel struct {
	ID                   string                 `json:"id"`
	GuildID              string                 `json:"guild_id"`
	Name                 string                 `json:"name"`
	Topic                string                 `json:"topic"`
	Type                 ChannelType            `json:"type"`
	LastMessageID        string                 `json:"last_message_id"`
	NSFW                 bool                   `json:"nsfw"`
	Position             int                    `json:"position"`
	Bitrate              int                    `json:"bitrate"`
	Type                 string                 `json:"type"`
	PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
	IsPrivate            bool                   `json:"is_private"`
	LastMessageID        string                 `json:"last_message_id"`
	Recipient            *User                  `json:"recipient"`
	Recipients           []*User                `json:"recipient"`
	Messages             []*Message             `json:"-"`
	PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
}

// A PermissionOverwrite holds permission overwrite data for a Channel


@@ 145,27 193,75 @@ type Emoji struct {
	RequireColons bool     `json:"require_colons"`
}

// APIName returns an correctly formatted API name for use in the MessageReactions endpoints.
func (e *Emoji) APIName() string {
	if e.ID != "" && e.Name != "" {
		return e.Name + ":" + e.ID
	}
	if e.Name != "" {
		return e.Name
	}
	return e.ID
}

// VerificationLevel type 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"`
	EmbedEnabled   bool          `json:"embed_enabled"`
	Large          bool          `json:"large"` // ??
	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"`
	ID                          string            `json:"id"`
	Name                        string            `json:"name"`
	Icon                        string            `json:"icon"`
	Region                      string            `json:"region"`
	AfkChannelID                string            `json:"afk_channel_id"`
	EmbedChannelID              string            `json:"embed_channel_id"`
	OwnerID                     string            `json:"owner_id"`
	JoinedAt                    Timestamp         `json:"joined_at"`
	Splash                      string            `json:"splash"`
	AfkTimeout                  int               `json:"afk_timeout"`
	MemberCount                 int               `json:"member_count"`
	VerificationLevel           VerificationLevel `json:"verification_level"`
	EmbedEnabled                bool              `json:"embed_enabled"`
	Large                       bool              `json:"large"` // ??
	DefaultMessageNotifications int               `json:"default_message_notifications"`
	Roles                       []*Role           `json:"roles"`
	Emojis                      []*Emoji          `json:"emojis"`
	Members                     []*Member         `json:"members"`
	Presences                   []*Presence       `json:"presences"`
	Channels                    []*Channel        `json:"channels"`
	VoiceStates                 []*VoiceState     `json:"voice_states"`
	Unavailable                 bool              `json:"unavailable"`
}

// A UserGuild holds a brief version of a Guild
type UserGuild struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Icon        string `json:"icon"`
	Owner       bool   `json:"owner"`
	Permissions int    `json:"permissions"`
}

// A GuildParams stores all the data needed to update discord guild settings
type GuildParams struct {
	Name                        string             `json:"name,omitempty"`
	Region                      string             `json:"region,omitempty"`
	VerificationLevel           *VerificationLevel `json:"verification_level,omitempty"`
	DefaultMessageNotifications int                `json:"default_message_notifications,omitempty"` // TODO: Separate type?
	AfkChannelID                string             `json:"afk_channel_id,omitempty"`
	AfkTimeout                  int                `json:"afk_timeout,omitempty"`
	Icon                        string             `json:"icon,omitempty"`
	OwnerID                     string             `json:"owner_id,omitempty"`
	Splash                      string             `json:"splash,omitempty"`
}

// A Role stores information about Discord guild member roles.


@@ 173,17 269,34 @@ type Role struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Managed     bool   `json:"managed"`
	Mentionable bool   `json:"mentionable"`
	Hoist       bool   `json:"hoist"`
	Color       int    `json:"color"`
	Position    int    `json:"position"`
	Permissions int    `json:"permissions"`
}

// Roles are a collection of Role
type Roles []*Role

func (r Roles) Len() int {
	return len(r)
}

func (r Roles) Less(i, j int) bool {
	return r[i].Position > r[j].Position
}

func (r Roles) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

// A VoiceState stores the voice states of Guilds
type VoiceState struct {
	UserID    string `json:"user_id"`
	SessionID string `json:"session_id"`
	ChannelID string `json:"channel_id"`
	GuildID   string `json:"guild_id"`
	Suppress  bool   `json:"suppress"`
	SelfMute  bool   `json:"self_mute"`
	SelfDeaf  bool   `json:"self_deaf"`


@@ 193,80 306,112 @@ type VoiceState struct {

// 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"`
	User   *User    `json:"user"`
	Status Status   `json:"status"`
	Game   *Game    `json:"game"`
	Nick   string   `json:"nick"`
	Roles  []string `json:"roles"`
	Since  *int     `json:"since"`
}

// 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,omitempty"`
}

// UnmarshalJSON unmarshals json to Game struct
func (g *Game) UnmarshalJSON(bytes []byte) error {
	temp := &struct {
		Name json.Number     `json:"name"`
		Type json.RawMessage `json:"type"`
		URL  string          `json:"url"`
	}{}
	err := json.Unmarshal(bytes, temp)
	if err != nil {
		return err
	}
	g.URL = temp.URL
	g.Name = temp.Name.String()

	if temp.Type != nil {
		err = json.Unmarshal(temp.Type, &g.Type)
		if err == nil {
			return nil
		}

		s := ""
		err = json.Unmarshal(temp.Type, &s)
		if err == nil {
			g.Type, err = strconv.Atoi(s)
		}

		return err
	}

	return nil
}

// 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"`
	Verified bool   `json:"verified"`
	//Discriminator int    `json:"discriminator,string"` // TODO: See below
}

// TODO: Research issue.
// Discriminator sometimes comes as a string
// and sometimes it comes as a int.  Weird.
// to avoid errors I've just commented it out
// but it doesn't seem to just kill the whole
// program.  Heartbeat is taken on READY even
// with error and the system continues to read
// it just doesn't seem able to handle this one
// field correctly.  Need to research this more.

// 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"`
	EnableTtsCommand      bool     `json:"enable_tts_command"`
	MessageDisplayCompact bool     `json:"message_display_compact"`
	ShowCurrentGame       bool     `json:"show_current_game"`
	Locale                string   `json:"locale"`
	Theme                 string   `json:"theme"`
	MutedChannels         []string `json:"muted_channels"`
}

// An Event provides a basic initial struct for all websocket event.
type Event struct {
	Type      string          `json:"t"`
	State     int             `json:"s"`
	Operation int             `json:"op"`
	Direction int             `json:"dir"`
	RawData   json.RawMessage `json:"d"`
}

// 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"`
}

// A RateLimit struct holds information related to a specific rate limit.
type RateLimit struct {
	RenderEmbeds           bool               `json:"render_embeds"`
	InlineEmbedMedia       bool               `json:"inline_embed_media"`
	InlineAttachmentMedia  bool               `json:"inline_attachment_media"`
	EnableTtsCommand       bool               `json:"enable_tts_command"`
	MessageDisplayCompact  bool               `json:"message_display_compact"`
	ShowCurrentGame        bool               `json:"show_current_game"`
	ConvertEmoticons       bool               `json:"convert_emoticons"`
	Locale                 string             `json:"locale"`
	Theme                  string             `json:"theme"`
	GuildPositions         []string           `json:"guild_positions"`
	RestrictedGuilds       []string           `json:"restricted_guilds"`
	FriendSourceFlags      *FriendSourceFlags `json:"friend_source_flags"`
	Status                 Status             `json:"status"`
	DetectPlatformAccounts bool               `json:"detect_platform_accounts"`
	DeveloperMode          bool               `json:"developer_mode"`
}

// Status type defination
type Status string

// Constants for Status with the different current available status
const (
	StatusOnline       Status = "online"
	StatusIdle         Status = "idle"
	StatusDoNotDisturb Status = "dnd"
	StatusInvisible    Status = "invisible"
	StatusOffline      Status = "offline"
)

// FriendSourceFlags stores ... TODO :)
type FriendSourceFlags struct {
	All           bool `json:"all"`
	MutualGuilds  bool `json:"mutual_guilds"`
	MutualFriends bool `json:"mutual_friends"`
}

// A Relationship between the logged in user and Relationship.User
type Relationship struct {
	User *User  `json:"user"`
	Type int    `json:"type"` // 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req
	ID   string `json:"id"`
}

// A TooManyRequests struct holds information received from Discord
// when receiving a HTTP 429 response.
type TooManyRequests struct {
	Bucket     string        `json:"bucket"`
	Message    string        `json:"message"`
	RetryAfter time.Duration `json:"retry_after"`


@@ 274,68 419,242 @@ type RateLimit struct {

// A ReadState stores data on the read state of channels.
type ReadState struct {
	MentionCount  int
	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"`
// An Ack is used to ack messages
type Ack struct {
	Token string `json:"token"`
}

// A PresenceUpdate stores data for the pressence update websocket event.
type PresenceUpdate struct {
	User    *User    `json:"user"`
	Status  string   `json:"status"`
	Roles   []string `json:"roles"`
	GuildID string   `json:"guild_id"`
	Game    *Game    `json:"game"`
// A GuildRole stores data for guild roles.
type GuildRole struct {
	Role    *Role  `json:"role"`
	GuildID string `json:"guild_id"`
}

// A MessageAck stores data for the message ack websocket event.
type MessageAck struct {
	MessageID string `json:"message_id"`
// A GuildBan stores data for a guild ban.
type GuildBan struct {
	Reason string `json:"reason"`
	User   *User  `json:"user"`
}

// A GuildIntegration stores data for a guild integration.
type GuildIntegration struct {
	ID                string                   `json:"id"`
	Name              string                   `json:"name"`
	Type              string                   `json:"type"`
	Enabled           bool                     `json:"enabled"`
	Syncing           bool                     `json:"syncing"`
	RoleID            string                   `json:"role_id"`
	ExpireBehavior    int                      `json:"expire_behavior"`
	ExpireGracePeriod int                      `json:"expire_grace_period"`
	User              *User                    `json:"user"`
	Account           *GuildIntegrationAccount `json:"account"`
	SyncedAt          int                      `json:"synced_at"`
}

// A GuildIntegrationAccount stores data for a guild integration account.
type GuildIntegrationAccount struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

// A GuildEmbed stores data for a guild embed.
type GuildEmbed struct {
	Enabled   bool   `json:"enabled"`
	ChannelID string `json:"channel_id"`
}

// A GuildIntegrationsUpdate stores data for the guild integrations update
// websocket event.
type GuildIntegrationsUpdate struct {
	GuildID string `json:"guild_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 GuildRole stores data for guild role websocket events.
type GuildRole struct {
	Role    *Role  `json:"role"`
	GuildID string `json:"guild_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 GuildRoleDelete stores data for the guild role delete websocket event.
type GuildRoleDelete struct {
	RoleID  string `json:"role_id"`
	GuildID string `json:"guild_id"`
// 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"`
}

// A GuildBan stores data for a guild ban.
type GuildBan struct {
	User    *User  `json:"user"`
	GuildID string `json:"guild_id"`
// An APIErrorMessage is an api error message returned from discord
type APIErrorMessage struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// A GuildEmojisUpdate stores data for a guild emoji update event.
type GuildEmojisUpdate struct {
	GuildID string   `json:"guild_id"`
	Emojis  []*Emoji `json:"emojis"`
// Webhook stores the data for a webhook.
type Webhook struct {
	ID        string `json:"id"`
	GuildID   string `json:"guild_id"`
	ChannelID string `json:"channel_id"`
	User      *User  `json:"user"`
	Name      string `json:"name"`
	Avatar    string `json:"avatar"`
	Token     string `json:"token"`
}

// 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
// WebhookParams is a struct for webhook params, used in the WebhookExecute command.
type WebhookParams struct {
	Content   string          `json:"content,omitempty"`
	Username  string          `json:"username,omitempty"`
	AvatarURL string          `json:"avatar_url,omitempty"`
	TTS       bool            `json:"tts,omitempty"`
	File      string          `json:"file,omitempty"`
	Embeds    []*MessageEmbed `json:"embeds,omitempty"`
}

// MessageReaction stores the data for a message reaction.
type MessageReaction struct {
	UserID    string `json:"user_id"`
	MessageID string `json:"message_id"`
	Emoji     Emoji  `json:"emoji"`
	ChannelID string `json:"channel_id"`
}

// GatewayBotResponse stores the data for the gateway/bot response
type GatewayBotResponse struct {
	URL    string `json:"url"`
	Shards int    `json:"shards"`
}

// Constants for the different bit offsets of text channel permissions
const (
	PermissionReadMessages = 1 << (iota + 10)
	PermissionSendMessages
	PermissionSendTTSMessages
	PermissionManageMessages
	PermissionEmbedLinks
	PermissionAttachFiles
	PermissionReadMessageHistory
	PermissionMentionEveryone
	PermissionUseExternalEmojis
)

// Constants for the different bit offsets of voice permissions
const (
	PermissionVoiceConnect = 1 << (iota + 20)
	PermissionVoiceSpeak
	PermissionVoiceMuteMembers
	PermissionVoiceDeafenMembers
	PermissionVoiceMoveMembers
	PermissionVoiceUseVAD
)

// Constants for general management.
const (
	PermissionChangeNickname = 1 << (iota + 26)
	PermissionManageNicknames
	PermissionManageRoles
	PermissionManageWebhooks
	PermissionManageEmojis
)

// Constants for the different bit offsets of general permissions
const (
	PermissionCreateInstantInvite = 1 << iota
	PermissionKickMembers
	PermissionBanMembers
	PermissionAdministrator
	PermissionManageChannels
	PermissionManageServer
	PermissionAddReactions
	PermissionViewAuditLogs

	PermissionAllText = PermissionReadMessages |
		PermissionSendMessages |
		PermissionSendTTSMessages |
		PermissionManageMessages |
		PermissionEmbedLinks |
		PermissionAttachFiles |
		PermissionReadMessageHistory |
		PermissionMentionEveryone
	PermissionAllVoice = PermissionVoiceConnect |
		PermissionVoiceSpeak |
		PermissionVoiceMuteMembers |
		PermissionVoiceDeafenMembers |
		PermissionVoiceMoveMembers |
		PermissionVoiceUseVAD
	PermissionAllChannel = PermissionAllText |
		PermissionAllVoice |
		PermissionCreateInstantInvite |
		PermissionManageRoles |
		PermissionManageChannels |
		PermissionAddReactions |
		PermissionViewAuditLogs
	PermissionAll = PermissionAllChannel |
		PermissionKickMembers |
		PermissionBanMembers |
		PermissionManageServer |
		PermissionAdministrator
)

// Block contains Discord JSON Error Response codes
const (
	ErrCodeUnknownAccount     = 10001
	ErrCodeUnknownApplication = 10002
	ErrCodeUnknownChannel     = 10003
	ErrCodeUnknownGuild       = 10004
	ErrCodeUnknownIntegration = 10005
	ErrCodeUnknownInvite      = 10006
	ErrCodeUnknownMember      = 10007
	ErrCodeUnknownMessage     = 10008
	ErrCodeUnknownOverwrite   = 10009
	ErrCodeUnknownProvider    = 10010
	ErrCodeUnknownRole        = 10011
	ErrCodeUnknownToken       = 10012
	ErrCodeUnknownUser        = 10013
	ErrCodeUnknownEmoji       = 10014

	ErrCodeBotsCannotUseEndpoint  = 20001
	ErrCodeOnlyBotsCanUseEndpoint = 20002

	ErrCodeMaximumGuildsReached     = 30001
	ErrCodeMaximumFriendsReached    = 30002
	ErrCodeMaximumPinsReached       = 30003
	ErrCodeMaximumGuildRolesReached = 30005
	ErrCodeTooManyReactions         = 30010

	ErrCodeUnauthorized = 40001

	ErrCodeMissingAccess                             = 50001
	ErrCodeInvalidAccountType                        = 50002
	ErrCodeCannotExecuteActionOnDMChannel            = 50003
	ErrCodeEmbedCisabled                             = 50004
	ErrCodeCannotEditFromAnotherUser                 = 50005
	ErrCodeCannotSendEmptyMessage                    = 50006
	ErrCodeCannotSendMessagesToThisUser              = 50007
	ErrCodeCannotSendMessagesInVoiceChannel          = 50008
	ErrCodeChannelVerificationLevelTooHigh           = 50009
	ErrCodeOAuth2ApplicationDoesNotHaveBot           = 50010
	ErrCodeOAuth2ApplicationLimitReached             = 50011
	ErrCodeInvalidOAuthState                         = 50012
	ErrCodeMissingPermissions                        = 50013
	ErrCodeInvalidAuthenticationToken                = 50014
	ErrCodeNoteTooLong                               = 50015
	ErrCodeTooFewOrTooManyMessagesToDelete           = 50016
	ErrCodeCanOnlyPinMessageToOriginatingChannel     = 50019
	ErrCodeCannotExecuteActionOnSystemMessage        = 50021
	ErrCodeMessageProvidedTooOldForBulkDelete        = 50034
	ErrCodeInvalidFormBody                           = 50035
	ErrCodeInviteAcceptedToGuildApplicationsBotNotIn = 50036

	ErrCodeReactionBlocked = 90001
)

A DiscordGo/tools/cmd/eventhandlers/main.go => DiscordGo/tools/cmd/eventhandlers/main.go +124 -0
@@ 0,0 1,124 @@
package main

import (
	"bytes"
	"go/format"
	"go/parser"
	"go/token"
	"io/ioutil"
	"log"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"text/template"
)

var eventHandlerTmpl = template.Must(template.New("eventHandler").Funcs(template.FuncMap{
	"constName":      constName,
	"isDiscordEvent": isDiscordEvent,
	"privateName":    privateName,
}).Parse(`// Code generated by \"eventhandlers\"; DO NOT EDIT
// See events.go

package discordgo

// Following are all the event types.
// Event type values are used to match the events returned by Discord.
// EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
const ({{range .}}
  {{privateName .}}EventType = "{{constName .}}"{{end}}
)
{{range .}}
// {{privateName .}}EventHandler is an event handler for {{.}} events.
type {{privateName .}}EventHandler func(*Session, *{{.}})

// Type returns the event type for {{.}} events.
func (eh {{privateName .}}EventHandler) Type() string {
  return {{privateName .}}EventType
}
{{if isDiscordEvent .}}
// New returns a new instance of {{.}}.
func (eh {{privateName .}}EventHandler) New() interface{} {
  return &{{.}}{}
}{{end}}
// Handle is the handler for {{.}} events.
func (eh {{privateName .}}EventHandler) Handle(s *Session, i interface{}) {
  if t, ok := i.(*{{.}}); ok {
    eh(s, t)
  }
}

{{end}}
func handlerForInterface(handler interface{}) EventHandler {
  switch v := handler.(type) {
  case func(*Session, interface{}):
    return interfaceEventHandler(v){{range .}}
  case func(*Session, *{{.}}):
    return {{privateName .}}EventHandler(v){{end}}
  }

  return nil
}

func init() { {{range .}}{{if isDiscordEvent .}}
  registerInterfaceProvider({{privateName .}}EventHandler(nil)){{end}}{{end}}
}
`))

func main() {
	var buf bytes.Buffer
	dir := filepath.Dir(".")

	fs := token.NewFileSet()
	parsedFile, err := parser.ParseFile(fs, "events.go", nil, 0)
	if err != nil {
		log.Fatalf("warning: internal error: could not parse events.go: %s", err)
		return
	}

	names := []string{}
	for object := range parsedFile.Scope.Objects {
		names = append(names, object)
	}
	sort.Strings(names)
	eventHandlerTmpl.Execute(&buf, names)

	src, err := format.Source(buf.Bytes())
	if err != nil {
		log.Println("warning: internal error: invalid Go generated:", err)
		src = buf.Bytes()
	}

	err = ioutil.WriteFile(filepath.Join(dir, strings.ToLower("eventhandlers.go")), src, 0644)
	if err != nil {
		log.Fatal(buf, "writing output: %s", err)
	}
}

var constRegexp = regexp.MustCompile("([a-z])([A-Z])")

func constCase(name string) string {
	return strings.ToUpper(constRegexp.ReplaceAllString(name, "${1}_${2}"))
}

func isDiscordEvent(name string) bool {
	switch {
	case name == "Connect", name == "Disconnect", name == "Event", name == "RateLimit", name == "Interface":
		return false
	default:
		return true
	}
}

func constName(name string) string {
	if !isDiscordEvent(name) {
		return "__" + constCase(name) + "__"
	}

	return constCase(name)
}

func privateName(name string) string {
	return strings.ToLower(string(name[0])) + name[1:]
}

A DiscordGo/types.go => DiscordGo/types.go +58 -0
@@ 0,0 1,58 @@
// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains custom types, currently only a timestamp wrapper.

package discordgo

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

// Timestamp stores a timestamp, as sent by the Discord API.
type Timestamp string

// Parse parses a timestamp string into a time.Time object.
// The only time this can fail is if Discord changes their timestamp format.
func (t Timestamp) Parse() (time.Time, error) {
	return time.Parse(time.RFC3339, string(t))
}

// RESTError stores error information about a request with a bad response code.
// Message is not always present, there are cases where api calls can fail
// without returning a json message.
type RESTError struct {
	Request      *http.Request
	Response     *http.Response
	ResponseBody []byte

	Message *APIErrorMessage // Message may be nil.
}

func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTError {
	restErr := &RESTError{
		Request:      req,
		Response:     resp,
		ResponseBody: body,
	}

	// Attempt to decode the error and assume no message was provided if it fails
	var msg *APIErrorMessage
	err := json.Unmarshal(body, &msg)
	if err == nil {
		restErr.Message = msg
	}

	return restErr
}

func (r RESTError) Error() string {
	return fmt.Sprintf("HTTP %s, %s", r.Response.Status, r.ResponseBody)
}

A DiscordGo/types_test.go => DiscordGo/types_test.go +24 -0
@@ 0,0 1,24 @@
package discordgo

import (
	"testing"
	"time"
)

func TestTimestampParse(t *testing.T) {
	ts, err := Timestamp("2016-03-24T23:15:59.605000+00:00").Parse()
	if err != nil {
		t.Fatal(err)
	}
	if ts.Year() != 2016 || ts.Month() != time.March || ts.Day() != 24 {
		t.Error("Incorrect date")
	}
	if ts.Hour() != 23 || ts.Minute() != 15 || ts.Second() != 59 {
		t.Error("Incorrect time")
	}

	_, offset := ts.Zone()
	if offset != 0 {
		t.Error("Incorrect timezone")
	}
}

A DiscordGo/user.go => DiscordGo/user.go +42 -0
@@ 0,0 1,42 @@
package discordgo

import (
	"fmt"
	"strings"
)

// A User stores all data for an individual Discord user.
type User struct {
	ID            string `json:"id"`
	Email         string `json:"email"`
	Username      string `json:"username"`
	Avatar        string `json:"avatar"`
	Discriminator string `json:"discriminator"`
	Token         string `json:"token"`
	Verified      bool   `json:"verified"`
	MFAEnabled    bool   `json:"mfa_enabled"`
	Bot           bool   `json:"bot"`
}

// String returns a unique identifier of the form username#discriminator
func (u *User) String() string {
	return fmt.Sprintf("%s#%s", u.Username, u.Discriminator)
}

// Mention return a string which mentions the user
func (u *User) Mention() string {
	return fmt.Sprintf("<@%s>", u.ID)
}

// AvatarURL returns a URL to the user's avatar.
//		size:     The size of the user's avatar as a power of two
func (u *User) AvatarURL(size string) string {
	var URL string
	if strings.HasPrefix(u.Avatar, "a_") {
		URL = EndpointUserAvatarAnimated(u.ID, u.Avatar)
	} else {
		URL = EndpointUserAvatar(u.ID, u.Avatar)
	}

	return URL + "?size=" + size
}

A DiscordGo/user_test.go => DiscordGo/user_test.go +16 -0
@@ 0,0 1,16 @@
package discordgo

import "testing"

func TestUser(t *testing.T) {
	t.Parallel()

	user := &User{
		Username:      "bob",
		Discriminator: "8192",
	}

	if user.String() != "bob#8192" {
		t.Errorf("user.String() == %v", user.String())
	}
}

M DiscordGo/voice.go => DiscordGo/voice.go +463 -166
@@ 13,48 13,230 @@ import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"

	"bitbucket.org/henesy/disco/websocket"
	"github.com/gorilla/websocket"
	"golang.org/x/crypto/nacl/secretbox"
)

// ------------------------------------------------------------------------------------------------
// Code related to both Voice Websocket and UDP connections.
// Code related to both VoiceConnection Websocket and UDP connections.
// ------------------------------------------------------------------------------------------------

// A Voice struct holds all data and functions related to Discord Voice support.
type Voice struct {
	sync.Mutex              // future use
	Ready      bool         // If true, voice is ready to send/receive audio
	Debug      bool         // If true, print extra logging
	OP2        *voiceOP2    // exported for dgvoice, may change.
	OpusSend   chan []byte  // Chan for sending opus audio
	OpusRecv   chan *Packet // Chan for receiving opus audio
	//	FrameRate  int         // This can be used to set the FrameRate of Opus data
	//	FrameSize  int         // This can be used to set the FrameSize of Opus data
// 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
	UDPConn *net.UDPConn // this will become unexported soon.
	wsMutex sync.Mutex
	udpConn *net.UDPConn
	session *Session

	sessionID string
	token     string
	endpoint  string
	guildID   string
	channelID string
	userID    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()

	v.Lock()
	defer v.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.session.wsMutex.Lock()
		err = v.session.wsConn.WriteJSON(data)
		v.session.wsMutex.Unlock()
		v.sessionID = ""
	}

	// Close websocket and udp connections
	v.Close()

	v.log(LogInformational, "Deleting VoiceConnection %s", v.GuildID)

	v.session.Lock()
	delete(v.session.VoiceConnections, v.GuildID)
	v.session.Unlock()

	return
}

// Close closes the voice ws and udp connections
func (v *VoiceConnection) Close() {

	v.log(LogInformational, "called")

	v.Lock()
	defer v.Unlock()

	v.Ready = false
	v.speaking = false

	if v.close != nil {
		v.log(LogInformational, "closing v.close")
		close(v.close)
		v.close = nil
	}

	if v.udpConn != nil {
		v.log(LogInformational, "closing udp")
		err := v.udpConn.Close()
		if err != nil {
			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.
		v.wsMutex.Lock()
		err := v.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
		v.wsMutex.Unlock()
		if err != nil {
			v.log(LogError, "error closing websocket, %s", err)
		}

		// TODO: Wait for Discord to actually close the connection.
		time.Sleep(1 * time.Second)

		v.log(LogInformational, "closing websocket")
		err = v.wsConn.Close()
		if err != nil {
			v.log(LogError, "error closing websocket, %s", err)
		}

		v.wsConn = nil
	}
}

// AddHandler adds a Handler for VoiceSpeakingUpdate events.
func (v *VoiceConnection) AddHandler(h VoiceSpeakingUpdateHandler) {
	v.Lock()
	defer v.Unlock()

	v.voiceSpeakingUpdateHandlers = append(v.voiceSpeakingUpdateHandlers, h)
}

// VoiceSpeakingUpdate is a struct for a VoiceSpeakingUpdate event.
type VoiceSpeakingUpdate struct {
	UserID   string `json:"user_id"`
	SSRC     int    `json:"ssrc"`
	Speaking bool   `json:"speaking"`
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice websocket connection
// 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 {


@@ 64,67 246,119 @@ type voiceOP2 struct {
	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
}

type voiceHandshakeData struct {
	ServerID  string `json:"server_id"`
	UserID    string `json:"user_id"`
	SessionID string `json:"session_id"`
	Token     string `json:"token"`
}
// 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")

type voiceHandshakeOp struct {
	Op   int                `json:"op"` // Always 0
	Data voiceHandshakeData `json:"d"`
	i := 0
	for {
		v.RLock()
		ready := v.Ready
		v.RUnlock()
		if ready {
			return nil
		}

		if i > 10 {
			return fmt.Errorf("timeout waiting for voice")
		}

		time.Sleep(1 * time.Second)
		i++
	}
}

// Open opens a voice connection.  This should be called
// after VoiceChannelJoin is used and the data VOICE websocket events
// are captured.
func (v *Voice) Open() (err error) {
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
	}

	// Connect to Voice Websocket
	// 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 {
		fmt.Println("VOICE error opening websocket:", err)
		v.log(LogWarning, "error connecting to voice endpoint %s, %s", vg, err)
		v.log(LogDebug, "voice struct: %#v\n", v)
		return
	}

	data := voiceHandshakeOp{0, voiceHandshakeData{v.guildID, v.userID, v.sessionID, v.token}}
	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 {
		fmt.Println("VOICE error sending init packet:", err)
		v.log(LogWarning, "error sending init packet, %s", err)
		return
	}

	// Start a listening for voice websocket events
	// TODO add a check here to make sure Listen worked by monitoring
	// a chan or bool?
	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 *Voice) wsListen(wsConn *websocket.Conn, close <-chan struct{}) {
func (v *VoiceConnection) wsListen(wsConn *websocket.Conn, close <-chan struct{}) {

	v.log(LogInformational, "called")

	for {
		messageType, message, err := v.wsConn.ReadMessage()
		_, message, err := v.wsConn.ReadMessage()
		if err != nil {
			// TODO: add reconnect, matching wsapi.go:listen()
			// TODO: Handle this problem better.
			// TODO: needs proper logging
			fmt.Println("Voice Listen Error:", err)
			// 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
		}



@@ 133,23 367,20 @@ func (v *Voice) wsListen(wsConn *websocket.Conn, close <-chan struct{}) {
		case <-close:
			return
		default:
			go v.wsEvent(messageType, message)
			go v.onEvent(message)
		}
	}
}

// wsEvent handles any voice websocket events. This is only called by the
// wsListen() function.
func (v *Voice) wsEvent(messageType int, message []byte) {
func (v *VoiceConnection) onEvent(message []byte) {

	if v.Debug {
		fmt.Println("wsEvent received: ", messageType)
		printJSON(message)
	}
	v.log(LogDebug, "received: %s", string(message))

	var e Event
	if err := json.Unmarshal(message, &e); err != nil {
		fmt.Println("wsEvent Unmarshall error: ", err)
		v.log(LogError, "unmarshall error, %s", err)
		return
	}



@@ 157,21 388,19 @@ func (v *Voice) wsEvent(messageType int, message []byte) {

	case 2: // READY

		v.OP2 = &voiceOP2{}
		if err := json.Unmarshal(e.RawData, v.OP2); err != nil {
			fmt.Println("voiceWS.onEvent OP2 Unmarshall error: ", err)
			printJSON(e.RawData) // TODO: Better error logging
		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)
		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 {
			fmt.Println("Error opening udp connection: ", err)
			v.log(LogError, "error opening udp connection, %s", err)
			return
		}



@@ 180,35 409,51 @@ func (v *Voice) wsEvent(messageType int, message []byte) {
		if v.OpusSend == nil {
			v.OpusSend = make(chan []byte, 2)
		}
		go v.opusSender(v.UDPConn, v.close, v.OpusSend, 48000, 960)
		go v.opusSender(v.udpConn, v.close, v.OpusSend, 48000, 960)

		// Start the opusReceiver
		if v.OpusRecv == nil {
			v.OpusRecv = make(chan *Packet, 2)
		if !v.deaf {
			if v.OpusRecv == nil {
				v.OpusRecv = make(chan *Packet, 2)
			}

			go v.opusReceiver(v.udpConn, v.close, v.OpusRecv)
		}
		go v.opusReceiver(v.UDPConn, v.close, v.OpusRecv)

		return

	case 3: // HEARTBEAT response
		// add code to use this to track latency?
		return

	case 4:
		// TODO
	case 4: // udp encryption secret key
		v.Lock()
		defer v.Unlock()

		v.op4 = voiceOP4{}
		if err := json.Unmarshal(e.RawData, &v.op4); err != nil {
			v.log(LogError, "OP4 unmarshall error, %s, %s", err, string(e.RawData))
			return
		}
		return

	case 5:
		// SPEAKING TRUE/FALSE NOTIFICATION
		/*
			{
				"user_id": "1238921738912",
				"ssrc": 2,
				"speaking": false
			}
		*/
		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:
		fmt.Println("UNKNOWN VOICE OP: ", e.Operation)
		printJSON(e.RawData)
		v.log(LogDebug, "unknown voice operation, %d, %s", e.Operation, string(e.RawData))
	}

	return


@@ 225,7 470,7 @@ type voiceHeartbeatOp struct {
// 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 *Voice) wsHeartbeat(wsConn *websocket.Conn, close <-chan struct{}, i time.Duration) {
func (v *VoiceConnection) wsHeartbeat(wsConn *websocket.Conn, close <-chan struct{}, i time.Duration) {

	if close == nil || wsConn == nil {
		return


@@ 233,10 478,14 @@ func (v *Voice) wsHeartbeat(wsConn *websocket.Conn, close <-chan struct{}, i tim

	var err error
	ticker := time.NewTicker(i * time.Millisecond)
	defer ticker.Stop()
	for {
		v.log(LogDebug, "sending heartbeat packet")
		v.wsMutex.Lock()
		err = wsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())})
		v.wsMutex.Unlock()
		if err != nil {
			fmt.Println("wsHeartbeat send error: ", err)
			v.log(LogError, "error sending heartbeat to voice endpoint %s, %s", v.endpoint, err)
			return
		}



@@ 249,44 498,14 @@ func (v *Voice) wsHeartbeat(wsConn *websocket.Conn, close <-chan struct{}, i tim
	}
}

type voiceSpeakingData struct {
	Speaking bool `json:"speaking"`
	Delay    int  `json:"delay"`
}

type voiceSpeakingOp struct {
	Op   int               `json:"op"` // Always 5
	Data voiceSpeakingData `json:"d"`
}

// 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 *Voice) Speaking(b bool) (err error) {

	if v.wsConn == nil {
		return fmt.Errorf("No Voice websocket.")
	}

	data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}}
	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("Speaking() write json error:", err)
		return
	}

	return
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice UDP connection
// 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"`    // plain or ?  (plain or encrypted)
	Mode    string `json:"mode"`    // always "xsalsa20_poly1305"
}

type voiceUDPD struct {


@@ 303,7 522,7 @@ type voiceUDPOp struct {
// 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 *Voice) udpOpen() (err error) {
func (v *VoiceConnection) udpOpen() (err error) {

	v.Lock()
	defer v.Unlock()


@@ 312,7 531,7 @@ func (v *Voice) udpOpen() (err error) {
		return fmt.Errorf("nil voice websocket")
	}

	if v.UDPConn != nil {
	if v.udpConn != nil {
		return fmt.Errorf("udp connection already open")
	}



@@ 324,29 543,27 @@ func (v *Voice) udpOpen() (err error) {
		return fmt.Errorf("empty endpoint")
	}

	host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.OP2.Port)
	host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.op2.Port)
	addr, err := net.ResolveUDPAddr("udp", host)
	if err != nil {
		fmt.Println("udpOpen resolve addr error: ", err)
		// TODO better logging
		v.log(LogWarning, "error resolving udp host %s, %s", host, err)
		return
	}

	v.UDPConn, err = net.DialUDP("udp", nil, addr)
	v.log(LogInformational, "connecting to udp addr %s", addr.String())
	v.udpConn, err = net.DialUDP("udp", nil, addr)
	if err != nil {
		fmt.Println("udpOpen dial udp error: ", err)
		// TODO better logging
		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 Voice event
	// 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)
	binary.BigEndian.PutUint32(sb, v.op2.SSRC)
	_, err = v.udpConn.Write(sb)
	if err != nil {
		fmt.Println("udpOpen udp write error : ", err)
		// TODO better logging
		v.log(LogWarning, "udp write error to %s, %s", addr.String(), err)
		return
	}



@@ 355,17 572,18 @@ func (v *Voice) udpOpen() (err error) {
	// 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)
	rlen, _, err := v.udpConn.ReadFromUDP(rb)
	if err != nil {
		fmt.Println("udpOpen udp read error : ", err)
		// TODO better logging
		v.log(LogWarning, "udp read error, %s, %s", addr.String(), err)
		return
	}

	if rlen < 70 {
		fmt.Println("Voice RLEN should be 70 but isn't")
		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
	// Loop over position 4 through 20 to grab the IP address
	// Should never be beyond position 20.
	var ip string
	for i := 4; i < 20; i++ {


@@ 380,16 598,18 @@ func (v *Voice) udpOpen() (err error) {

	// 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, "plain"}}}
	data := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "xsalsa20_poly1305"}}}

	v.wsMutex.Lock()
	err = v.wsConn.WriteJSON(data)
	v.wsMutex.Unlock()
	if err != nil {
		fmt.Println("udpOpen write json error:", err)
		v.log(LogWarning, "udp write error, %#v, %s", data, err)
		return
	}

	// start udpKeepAlive
	go v.udpKeepAlive(v.UDPConn, v.close, 5*time.Second)
	go v.udpKeepAlive(v.udpConn, v.close, 5*time.Second)
	// TODO: find a way to check that it fired off okay

	return


@@ 397,9 617,9 @@ func (v *Voice) udpOpen() (err error) {

// udpKeepAlive sends a udp packet to keep the udp connection open
// This is still a bit of a "proof of concept"
func (v *Voice) udpKeepAlive(UDPConn *net.UDPConn, close <-chan struct{}, i time.Duration) {
func (v *VoiceConnection) udpKeepAlive(udpConn *net.UDPConn, close <-chan struct{}, i time.Duration) {

	if UDPConn == nil || close == nil {
	if udpConn == nil || close == nil {
		return
	}



@@ 409,14 629,15 @@ func (v *Voice) udpKeepAlive(UDPConn *net.UDPConn, close <-chan struct{}, i time
	packet := make([]byte, 8)

	ticker := time.NewTicker(i)
	defer ticker.Stop()
	for {

		binary.LittleEndian.PutUint64(packet, sequence)
		sequence++

		_, err = UDPConn.Write(packet)
		_, err = udpConn.Write(packet)
		if err != nil {
			fmt.Println("udpKeepAlive udp write error : ", err)
			v.log(LogError, "write error, %s", err)
			return
		}



@@ 431,32 652,38 @@ func (v *Voice) udpKeepAlive(UDPConn *net.UDPConn, close <-chan struct{}, i time

// opusSender will listen on the given channel and send any
// pre-encoded opus audio to Discord.  Supposedly.
func (v *Voice) opusSender(UDPConn *net.UDPConn, close <-chan struct{}, opus <-chan []byte, rate, size int) {
func (v *VoiceConnection) opusSender(udpConn *net.UDPConn, close <-chan struct{}, opus <-chan []byte, rate, size int) {

	if UDPConn == nil || close == nil {
	if udpConn == nil || close == nil {
		return
	}

	runtime.LockOSThread()

	// Voice is now ready to receive audio packets
	// VoiceConnection is now ready to receive audio packets
	// TODO: this needs reviewed as I think there must be a better way.
	v.Lock()
	v.Ready = true
	defer func() { v.Ready = false }()
	v.Unlock()
	defer func() {
		v.Lock()
		v.Ready = false
		v.Unlock()
	}()

	var sequence uint16
	var timestamp uint32
	var recvbuf []byte
	var ok bool
	udpHeader := make([]byte, 12)
	var nonce [24]byte

	// build the parts that don't change in the udpHeader
	udpHeader[0] = 0x80
	udpHeader[1] = 0x78
	binary.BigEndian.PutUint32(udpHeader[8:], v.OP2.SSRC)
	binary.BigEndian.PutUint32(udpHeader[8:], v.op2.SSRC)

	// start a send loop that loops until buf chan is closed
	ticker := time.NewTicker(time.Millisecond * time.Duration(size/(rate/1000)))
	defer ticker.Stop()
	for {

		// Get data from chan.  If chan is closed, return.


@@ 470,12 697,25 @@ func (v *Voice) opusSender(UDPConn *net.UDPConn, close <-chan struct{}, opus <-c
			// else, continue loop
		}

		v.RLock()
		speaking := v.speaking
		v.RUnlock()
		if !speaking {
			err := v.Speaking(true)
			if err != nil {
				v.log(LogError, "error sending speaking packet, %s", err)
			}
		}

		// Add sequence and timestamp to udpPacket
		binary.BigEndian.PutUint16(udpHeader[2:], sequence)
		binary.BigEndian.PutUint32(udpHeader[4:], timestamp)

		// Combine the UDP Header and the opus data
		sendbuf := append(udpHeader, recvbuf...)
		// encrypt the opus data
		copy(nonce[:], udpHeader)
		v.RLock()
		sendbuf := secretbox.Seal(udpHeader, recvbuf, &nonce, &v.op4.SecretKey)
		v.RUnlock()

		// block here until we're exactly at the right time :)
		// Then send rtp audio packet to Discord over UDP


@@ 485,10 725,11 @@ func (v *Voice) opusSender(UDPConn *net.UDPConn, close <-chan struct{}, opus <-c
		case <-ticker.C:
			// continue
		}
		_, err := UDPConn.Write(sendbuf)
		_, err := udpConn.Write(sendbuf)

		if err != nil {
			fmt.Println("error writing to udp connection: ", err)
			v.log(LogError, "udp write error, %s", err)
			v.log(LogDebug, "voice struct: %#v\n", v)
			return
		}



@@ 519,19 760,31 @@ type Packet struct {
// 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 *Voice) opusReceiver(UDPConn *net.UDPConn, close <-chan struct{}, c chan *Packet) {
func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct{}, c chan *Packet) {

	if UDPConn == nil || close == nil {
	if udpConn == nil || close == nil {
		return
	}

	p := Packet{}
	recvbuf := make([]byte, 1024)
	var nonce [24]byte

	for {
		rlen, err := UDPConn.Read(recvbuf)
		rlen, err := udpConn.Read(recvbuf)
		if err != nil {
			fmt.Println("opusReceiver UDP Read error:", err)
			// 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
		}



@@ 543,48 796,92 @@ func (v *Voice) opusReceiver(UDPConn *net.UDPConn, close <-chan struct{}, c chan
		}

		// For now, skip anything except audio.
		if rlen < 12 || recvbuf[0] != 0x80 {
		if rlen < 12 || (recvbuf[0] != 0x80 && recvbuf[0] != 0x90) {
			continue
		}

		// build a audio packet struct
		p := Packet{}
		p.Type = recvbuf[0:2]
		p.Sequence = binary.BigEndian.Uint16(recvbuf[2:4])
		p.Timestamp = binary.BigEndian.Uint32(recvbuf[4:8])
		p.SSRC = binary.BigEndian.Uint32(recvbuf[8:12])
		p.Opus = recvbuf[12:rlen]
		// decrypt opus data
		copy(nonce[:], recvbuf[0:12])
		p.Opus, _ = secretbox.Open(nil, recvbuf[12:rlen], &nonce, &v.op4.SecretKey)

		if len(p.Opus) > 8 && recvbuf[0] == 0x90 {
			// Extension bit is set, first 8 bytes is the extended header
			p.Opus = p.Opus[8:]
		}

		if c != nil {
			c <- &p
			select {
			case c <- &p:
			case <-close:
				return
			}
		}
	}
}

// Close closes the voice ws and udp connections
func (v *Voice) Close() {
// 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()
	defer v.Unlock()
	if v.reconnecting {
		v.log(LogInformational, "already reconnecting to channel %s, exiting", v.ChannelID)
		v.Unlock()
		return
	}
	v.reconnecting = true
	v.Unlock()

	v.Ready = false
	defer func() { v.reconnecting = false }()

	if v.close != nil {
		close(v.close)
		v.close = nil
	}
	// Close any currently open connections
	v.Close()

	if v.UDPConn != nil {
		err := v.UDPConn.Close()
		if err != nil {
			fmt.Println("error closing udp connection: ", err)
	wait := time.Duration(1)
	for {

		<-time.After(wait * time.Second)
		wait *= 2
		if wait > 600 {
			wait = 600
		}
		v.UDPConn = nil
	}

	if v.wsConn != nil {
		err := v.wsConn.Close()
		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
		}

		v.log(LogInformational, "error reconnecting to channel %s, %s", v.ChannelID, err)

		// if the reconnect above didn't work lets just send a disconnect
		// packet to reset things.
		// Send a OP4 with a nil channel to disconnect
		data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}}
		v.session.wsMutex.Lock()
		err = v.session.wsConn.WriteJSON(data)
		v.session.wsMutex.Unlock()
		if err != nil {
			fmt.Println("error closing websocket connection: ", err)
			v.log(LogError, "error sending disconnect packet, %s", err)
		}
		v.wsConn = nil

	}
}

M DiscordGo/wsapi.go => DiscordGo/wsapi.go +553 -205
@@ 15,39 15,41 @@ import (
	"compress/zlib"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"runtime"
	"sync/atomic"
	"time"

	"bitbucket.org/henesy/disco/websocket"
	"github.com/gorilla/websocket"
)

type handshakeProperties struct {
	OS              string `json:"$os"`
	Browser         string `json:"$browser"`
	Device          string `json:"$device"`
	Referer         string `json:"$referer"`
	ReferringDomain string `json:"$referring_domain"`
}
// ErrWSAlreadyOpen is thrown when you attempt to open
// a websocket that already is open.
var ErrWSAlreadyOpen = errors.New("web socket already opened")

type handshakeData struct {
	Version        int                 `json:"v"`
	Token          string              `json:"token"`
	Properties     handshakeProperties `json:"properties"`
	LargeThreshold int                 `json:"large_threshold"`
	Compress       bool                `json:"compress"`
}
// ErrWSNotFound is thrown when you attempt to use a websocket
// that doesn't exist
var ErrWSNotFound = errors.New("no websocket connection exists")

// ErrWSShardBounds is thrown when you try to use a shard ID that is
// less than the total shard count
var ErrWSShardBounds = errors.New("ShardID must be less than ShardCount")

type handshakeOp struct {
	Op   int           `json:"op"`
	Data handshakeData `json:"d"`
type resumePacket struct {
	Op   int `json:"op"`
	Data struct {
		Token     string `json:"token"`
		SessionID string `json:"session_id"`
		Sequence  int64  `json:"seq"`
	} `json:"d"`
}

// Open opens a websocket connection to Discord.
func (s *Session) Open() (err error) {

	s.log(LogInformational, "called")

	s.Lock()
	defer func() {
		if err != nil {


@@ 55,142 57,188 @@ func (s *Session) Open() (err error) {
		}
	}()

	// A basic state is a hard requirement for Voice.
	if s.State == nil {
		state := NewState()
		state.TrackChannels = false
		state.TrackEmojis = false
		state.TrackMembers = false
		state.TrackRoles = false
		state.TrackVoice = false
		s.State = state
	}

	if s.wsConn != nil {
		err = errors.New("Web socket already opened.")
		err = ErrWSAlreadyOpen
		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
	g, err := s.Gateway()
	if err != nil {
		return
	if s.gateway == "" {
		s.gateway, err = s.Gateway()
		if err != nil {
			return
		}

		// Add the version and encoding to the URL
		s.gateway = s.gateway + "?v=" + APIVersion + "&encoding=json"
	}

	header := http.Header{}
	header.Add("accept-encoding", "zlib")

	// TODO: See if there's a use for the http response.
	// conn, response, err := websocket.DefaultDialer.Dial(session.Gateway, nil)
	s.wsConn, _, err = websocket.DefaultDialer.Dial(g, header)
	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
	}

	err = s.wsConn.WriteJSON(handshakeOp{2, handshakeData{3, s.Token, handshakeProperties{runtime.GOOS, "Discordgo v" + VERSION, "", "", ""}, 250, s.Compress}})
	if err != nil {
		return
	sequence := atomic.LoadInt64(s.sequence)
	if s.sessionID != "" && sequence > 0 {

		p := resumePacket{}
		p.Op = 6
		p.Data.Token = s.Token
		p.Data.SessionID = s.sessionID
		p.Data.Sequence = sequence

		s.log(LogInformational, "sending resume packet to gateway")
		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.LastHeartbeatAck = time.Now().UTC()

	s.Unlock()

	s.initialize()
	s.handle(&Connect{})
	s.log(LogInformational, "emit connect event")
	s.handleEvent(connectEventType, &Connect{})

	s.log(LogInformational, "exiting")
	return
}

// 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.Lock()

	s.DataReady = false

	if s.listening != nil {
		close(s.listening)
		s.listening = nil
	}

	if s.wsConn != nil {
		err = s.wsConn.Close()
		s.wsConn = nil
	}

	s.Unlock()

	s.handle(&Disconnect{})
// 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{}) {

	return
}
	s.log(LogInformational, "called")

// 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{}) {
	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.
			// happened, the websocket we are listening on will be different to
			// the current session.
			s.RLock()
			sameConnection := s.wsConn == wsConn
			s.RUnlock()

			if sameConnection {
				// There has been an error reading, Close() the websocket so that
				// OnDisconnect is fired.

				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 {
					fmt.Println("error closing session connection: ", err)
					s.log(LogWarning, "error closing session connection, %s", err)
				}

				// Attempt to reconnect, with expenonential backoff up to 10 minutes.
				if s.ShouldReconnectOnError {
					wait := time.Duration(1)
					for {
						if s.Open() == nil {
							return
						}
						<-time.After(wait * time.Second)
						wait *= 2
						if wait > 600 {
							wait = 600
						}
					}
				}
				s.log(LogInformational, "calling reconnect() now")
				s.reconnect()
			}

			return
		}

		select {

		case <-listening:
			return

		default:
			go s.event(messageType, message)
			s.onEvent(messageType, message)

		}
	}
}

type heartbeatOp struct {
	Op   int `json:"op"`
	Data int `json:"d"`
	Op   int   `json:"op"`
	Data int64 `json:"d"`
}

type helloOp struct {
	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
	Trace             []string      `json:"_trace"`
}

// FailedHeartbeatAcks is the Number of heartbeat intervals to wait until forcing a connection restart.
const FailedHeartbeatAcks time.Duration = 5 * time.Millisecond

// 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) {
func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}, heartbeatIntervalMsec time.Duration) {

	s.log(LogInformational, "called")

	if listening == nil || wsConn == nil {
		return
	}

	s.Lock()
	s.DataReady = true
	s.Unlock()

	var err error
	ticker := time.NewTicker(i * time.Millisecond)
	ticker := time.NewTicker(heartbeatIntervalMsec * time.Millisecond)
	defer ticker.Stop()

	for {
		err = wsConn.WriteJSON(heartbeatOp{1, int(time.Now().Unix())})
		if err != nil {
			fmt.Println("Error sending heartbeat:", err)
		s.RLock()
		last := s.LastHeartbeatAck
		s.RUnlock()
		sequence := atomic.LoadInt64(s.sequence)
		s.log(LogInformational, "sending gateway websocket heartbeat seq %d", sequence)
		s.wsMutex.Lock()
		err = wsConn.WriteJSON(heartbeatOp{1, sequence})
		s.wsMutex.Unlock()
		if err != nil || time.Now().UTC().Sub(last) > (heartbeatIntervalMsec*FailedHeartbeatAcks) {
			if err != nil {
				s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err)
			} else {
				s.log(LogError, "haven't gotten a heartbeat ACK in %v, triggering a reconnection", time.Now().UTC().Sub(last))
			}
			s.Close()
			s.reconnect()
			return
		}
		s.Lock()
		s.DataReady = true
		s.Unlock()

		select {
		case <-ticker.C:


@@ 201,13 249,11 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
	}
}

type updateStatusGame struct {
	Name string `json:"name"`
}

type updateStatusData struct {
	IdleSince *int              `json:"idle_since"`
	Game      *updateStatusGame `json:"game"`
	IdleSince *int   `json:"since"`
	Game      *Game  `json:"game"`
	AFK       bool   `json:"afk"`
	Status    string `json:"status"`
}

type updateStatusOp struct {


@@ 215,101 261,236 @@ type updateStatusOp struct {
	Data updateStatusData `json:"d"`
}

// UpdateStatus is used to update the authenticated user's status.
// If idle>0 then set status to idle.  If game>0 then set game.
// UpdateStreamingStatus is used to update the user's streaming status.
// If idle>0 then set status to idle.
// If game!="" then set game.
// If game!="" and url!="" then set the status type to streaming with the URL set.
// if otherwise, set status to active, and no game.
func (s *Session) UpdateStatus(idle int, game string) (err error) {
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.")
		return ErrWSNotFound
	}

	usd := updateStatusData{
		Status: "online",
	}

	var usd updateStatusData
	if idle > 0 {
		usd.IdleSince = &idle
	}

	if game != "" {
		usd.Game = &updateStatusGame{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
}

// Front line handler for all Websocket Events.  Determines the
// event type and passes the message along to the next handler.
// UpdateStatus is used to update the user's status.
// If idle>0 then set status to idle.
// If game!="" then set game.
// if otherwise, set status to active, and no game.
func (s *Session) UpdateStatus(idle int, game string) (err error) {
	return s.UpdateStreamingStatus(idle, game, "")
}

type requestGuildMembersData struct {
	GuildID string `json:"guild_id"`
	Query   string `json:"query"`
	Limit   int    `json:"limit"`
}

type requestGuildMembersOp struct {
	Op   int                     `json:"op"`
	Data requestGuildMembersData `json:"d"`
}

// RequestGuildMembers requests guild members from the gateway
// The gateway responds with GuildMembersChunk events
// guildID  : The ID of the guild to request members of
// query    : String that username starts with, leave empty to return all members
// limit    : Max number of items to return, or 0 to request all members matched
func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err error) {
	s.log(LogInformational, "called")

	s.RLock()
	defer s.RUnlock()
	if s.wsConn == nil {
		return ErrWSNotFound
	}

	data := requestGuildMembersData{
		GuildID: guildID,
		Query:   query,
		Limit:   limit,
	}

	s.wsMutex.Lock()
	err = s.wsConn.WriteJSON(requestGuildMembersOp{8, data})
	s.wsMutex.Unlock()

	return
}

// onEvent is the "event handler" for all messages received on the
// Discord Gateway API websocket connection.
//
// If you use the AddHandler() function to register a handler for a
// specific event this function will pass the event along to that handler.
//
// If you use the AddHandler() function to register a handler for the
// "OnEvent" event then all events will be passed to that handler.
//
// TODO: You may also register a custom event handler entirely using...
func (s *Session) onEvent(messageType int, message []byte) {

// event is the front line handler for all events.  This needs to be
// broken up into smaller functions to be more idiomatic Go.
// Events will be handled by any implemented handler in Session.
// All unhandled events will then be handled by OnEvent.
func (s *Session) event(messageType int, message []byte) {
	var err error
	var reader io.Reader
	reader = bytes.NewBuffer(message)

	if messageType == 2 {
		z, err1 := zlib.NewReader(reader)
		if err1 != nil {
			fmt.Println(err1)
	// 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() {
			err := z.Close()
			if err != nil {
				fmt.Println("error closing zlib:", err)
			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 {
		fmt.Println(err)
		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, atomic.LoadInt64(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 {
		s.log(LogInformational, "Closing and reconnecting in response to Op7")
		s.Close()
		s.reconnect()
		return
	}

	if s.Debug {
		printEvent(e)
	// 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
	}

	if e.Operation == 10 {
		var h helloOp
		if err = json.Unmarshal(e.RawData, &h); err != nil {
			s.log(LogError, "error unmarshalling helloOp, %s", err)
		} else {
			go s.heartbeat(s.wsConn, s.listening, h.HeartbeatInterval)
		}
		return
	}

	if e.Operation == 11 {
		s.Lock()
		s.LastHeartbeatAck = time.Now().UTC()
		s.Unlock()
		s.log(LogInformational, "got heartbeat ACK")
		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
	}

	i := eventToInterface[e.Type]
	if i != nil {
		// Create a new instance of the event type.
		i = reflect.New(reflect.TypeOf(i)).Interface()
	// Store the message sequence
	atomic.StoreInt64(s.sequence, e.Sequence)

	// Map event to registered event handlers and pass it along to any registered handlers.
	if eh, ok := registeredInterfaceProviders[e.Type]; ok {
		e.Struct = eh.New()

		// Attempt to unmarshal our event.
		// If there is an error we should handle the event itself.
		if err = unmarshal(e.RawData, i); err != nil {
			fmt.Println("Unable to unmarshal event data.")
			i = e
		if err = json.Unmarshal(e.RawData, e.Struct); err != nil {
			s.log(LogError, "error unmarshalling %s event, %s", e.Type, err)
		}

		// Send event to any registered event handlers for it's type.
		// Because the above doesn't cancel this, in case of an error
		// the struct could be partially populated or at default values.
		// However, most errors are due to a single field and I feel
		// it's better to pass along what we received than nothing at all.
		// TODO: Think about that decision :)
		// Either way, READY events must fire, even with errors.
		s.handleEvent(e.Type, e.Struct)
	} else {
		//fmt.Println("Unknown event.")
		i = e
		s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData))
	}

	s.handle(i)

	return
	// For legacy reasons, we send the raw event also, this could be useful for handling unknown events.
	s.handleEvent(eventEventType, 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"`


@@ 322,113 503,280 @@ type voiceChannelJoinOp struct {
	Data voiceChannelJoinData `json:"d"`
}

// ChannelVoiceJoin joins the session user to a voice channel. After calling
// this func please monitor the Session.Voice.Ready bool to determine when
// it is ready and able to send/receive audio, that should happen quickly.
// 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) (err error) {
//    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")

	// Create new voice{} struct if one does not exist.
	// If you create this prior to calling this func then you can manually
	// set some variables if needed, such as to enable debugging.
	if s.Voice == nil {
		s.Voice = &Voice{}
	s.RLock()
	voice, _ = s.VoiceConnections[gID]
	s.RUnlock()

	if voice == nil {
		voice = &VoiceConnection{}
		s.Lock()
		s.VoiceConnections[gID] = voice
		s.Unlock()
	}

	voice.Lock()
	voice.GuildID = gID
	voice.ChannelID = cID
	voice.deaf = deaf
	voice.mute = mute
	voice.session = s
	voice.Unlock()

	// 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
	}

	// Store gID and cID for later use
	s.Voice.guildID = gID
	s.Voice.channelID = cID

	return
}

// ChannelVoiceLeave disconnects from the currently connected
// voice channel.
func (s *Session) ChannelVoiceLeave() (err error) {

	if s.Voice == nil {
		return
	}

	// Send the request to Discord that we want to leave voice
	data := voiceChannelJoinOp{4, voiceChannelJoinData{nil, nil, true, true}}
	err = s.wsConn.WriteJSON(data)
	// 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
	}

	// Close voice and nil data struct
	s.Voice.Close()
	s.Voice = nil

	return
}

// onVoiceStateUpdate handles Voice State Update events on the data
// websocket.  This comes immediately after the call to VoiceChannelJoin
// for the session user.
func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
// onVoiceStateUpdate handles Voice State Update events on the data websocket.
func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) {

	// Ignore if Voice is nil
	if s.Voice == nil {
	// If we don't have a connection for the channel, don't bother
	if st.ChannelID == "" {
		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 {
		fmt.Println(err)
	// Check if we have a voice connection to update
	s.RLock()
	voice, exists := s.VoiceConnections[st.GuildID]
	s.RUnlock()
	if !exists {
		return
	}

	// This event comes for all users, if it's not for the session
	// user just ignore it.
	// TODO Move this IF to the event() func
	if st.UserID != self.ID {
	// We only care about events that are about us.
	if s.State.User.ID != st.UserID {
		return
	}

	// Store the SessionID for later use.
	s.Voice.userID = self.ID // TODO: Review
	s.Voice.sessionID = st.SessionID
	voice.Lock()
	voice.UserID = st.UserID
	voice.sessionID = st.SessionID
	voice.ChannelID = st.ChannelID
	voice.Unlock()
}

// onVoiceServerUpdate handles the Voice Server Update data websocket event.
// This event tells us the information needed to open a voice websocket
// connection and should happen after the VOICE_STATE 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) {
func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {

	// Store values for later use
	s.Voice.token = st.Token
	s.Voice.endpoint = st.Endpoint
	s.Voice.guildID = st.GuildID
	s.log(LogInformational, "called")

	s.RLock()
	voice, exists := s.VoiceConnections[st.GuildID]
	s.RUnlock()

	// If no VoiceConnection exists, just skip this
	if !exists {
		return
	}

	// If currently connected to voice ws/udp, then disconnect.
	// Has no effect if not connected.
	s.Voice.Close()
	voice.Close()

	// We now have enough information to open a voice websocket conenction
	// so, that's what the next call does.
	err := s.Voice.Open()
	// Store values for later use
	voice.Lock()
	voice.token = st.Token
	voice.endpoint = st.Endpoint
	voice.GuildID = st.GuildID
	voice.Unlock()

	// Open a conenction to the voice server
	err := voice.open()
	if err != nil {
		fmt.Println("onVoiceServerUpdate Voice.Open error: ", err)
		// TODO better logging
		s.log(LogError, "onVoiceServerUpdate voice.open, %s", err)
	}
}

type identifyProperties struct {
	OS              string `json:"$os"`
	Browser         string `json:"$browser"`
	Device          string `json:"$device"`
	Referer         string `json:"$referer"`
	ReferringDomain string `json:"$referring_domain"`
}

type identifyData struct {
	Token          string             `json:"token"`
	Properties     identifyProperties `json:"properties"`
	LargeThreshold int                `json:"large_threshold"`
	Compress       bool               `json:"compress"`
	Shard          *[2]int            `json:"shard,omitempty"`
}

type identifyOp struct {
	Op   int          `json:"op"`
	Data identifyData `json:"d"`
}

// identify sends the identify packet to the gateway
func (s *Session) identify() error {

	properties := identifyProperties{runtime.GOOS,
		"Discordgo v" + VERSION,
		"",
		"",
		"",
	}

	data := identifyData{s.Token,
		properties,
		250,
		s.Compress,
		nil,
	}

	if s.ShardCount > 1 {

		if s.ShardID >= s.ShardCount {
			return ErrWSShardBounds
		}

		data.Shard = &[2]int{s.ShardID, s.ShardCount}
	}

	op := identifyOp{2, data}

	s.wsMutex.Lock()
	err := s.wsConn.WriteJSON(op)
	s.wsMutex.Unlock()
	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.
				s.RLock()
				defer s.RUnlock()
				for _, v := range s.VoiceConnections {

					s.log(LogInformational, "reconnecting voice connection to guild %s", v.GuildID)
					go v.reconnect()

					// This is here just to prevent violently spamming the
					// voice reconnects
					time.Sleep(1 * time.Second)

				}
				return
			}

			// Certain race conditions can call reconnect() twice. If this happens, we
			// just break out of the reconnect loop
			if err == ErrWSAlreadyOpen {
				s.log(LogInformational, "Websocket already exists, no need to reconnect")
				return
			}

			s.log(LogError, "error reconnecting to gateway, %s", err)

			<-time.After(wait * time.Second)
			wait *= 2
			if wait > 600 {
				wait = 600
			}
		}
	}
}

// Close closes a websocket and stops all listening/heartbeat goroutines.
// TODO: Add support for Voice WS/UDP connections
func (s *Session) Close() (err error) {

	s.log(LogInformational, "called")
	s.Lock()

	s.DataReady = false

	if s.listening != nil {
		s.log(LogInformational, "closing listening channel")
		close(s.listening)
		s.listening = nil
	}

	// TODO: Close all active Voice Connections too
	// this should force stop any reconnecting voice channels too

	if s.wsConn != nil {

		s.log(LogInformational, "sending close frame")
		// To cleanly close a connection, a client should send a close
		// frame and wait for the server to close the connection.
		s.wsMutex.Lock()
		err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
		s.wsMutex.Unlock()
		if err != nil {
			s.log(LogInformational, "error closing websocket, %s", err)
		}

		// TODO: Wait for Discord to actually close the connection.
		time.Sleep(1 * time.Second)

		s.log(LogInformational, "closing gateway websocket")
		err = s.wsConn.Close()
		if err != nil {
			s.log(LogInformational, "error closing websocket, %s", err)
		}

		s.wsConn = nil
	}

	s.Unlock()

	s.log(LogInformational, "emit disconnect event")
	s.handleEvent(disconnectEventType, &Disconnect{})

	return
}