Release version v0.21.0
This commit is contained in:
commit
1294b313b9
21 changed files with 350 additions and 104 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# IDE-specific metadata
|
||||||
|
.idea/
|
|
@ -1,8 +1,9 @@
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
- 1.10.x
|
||||||
- 1.11.x
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
- 1.13.x
|
||||||
install:
|
install:
|
||||||
- go get github.com/bwmarrin/discordgo
|
- go get github.com/bwmarrin/discordgo
|
||||||
- go get -v .
|
- go get -v .
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# DiscordGo
|
# DiscordGo
|
||||||
|
|
||||||
[](https://godoc.org/github.com/bwmarrin/discordgo) [](http://goreportcard.com/report/bwmarrin/discordgo) [](https://travis-ci.org/bwmarrin/discordgo) [](https://discord.gg/0f1SbxBZjYoCtNPP) [](https://discordapp.com/invite/discord-api)
|
[](https://godoc.org/github.com/bwmarrin/discordgo) [](http://goreportcard.com/report/bwmarrin/discordgo) [](https://travis-ci.org/bwmarrin/discordgo) [](https://discord.gg/0f1SbxBZjYoCtNPP) [](https://discord.com/invite/discord-api)
|
||||||
|
|
||||||
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">
|
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">
|
||||||
|
|
||||||
DiscordGo is a [Go](https://golang.org/) package that provides low level
|
DiscordGo is a [Go](https://golang.org/) package that provides low level
|
||||||
bindings to the [Discord](https://discordapp.com/) chat client API. DiscordGo
|
bindings to the [Discord](https://discord.com/) chat client API. DiscordGo
|
||||||
has nearly complete support for all of the Discord API endpoints, websocket
|
has nearly complete support for all of the Discord API endpoints, websocket
|
||||||
interface, and voice interface.
|
interface, and voice interface.
|
||||||
|
|
||||||
If you would like to help the DiscordGo package please use
|
If you would like to help the DiscordGo package please use
|
||||||
[this link](https://discordapp.com/oauth2/authorize?client_id=173113690092994561&scope=bot)
|
[this link](https://discord.com/oauth2/authorize?client_id=173113690092994561&scope=bot)
|
||||||
to add the official DiscordGo test bot **dgo** to your server. This provides
|
to add the official DiscordGo test bot **dgo** to your server. This provides
|
||||||
indispensable help to this project.
|
indispensable help to this project.
|
||||||
|
|
||||||
|
|
32
discord.go
32
discord.go
|
@ -17,11 +17,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
||||||
const VERSION = "0.20.3"
|
const VERSION = "0.21.0"
|
||||||
|
|
||||||
// ErrMFA will be risen by New when the user has 2FA.
|
// ErrMFA will be risen by New when the user has 2FA.
|
||||||
var ErrMFA = errors.New("account has 2FA enabled")
|
var ErrMFA = errors.New("account has 2FA enabled")
|
||||||
|
@ -30,10 +31,13 @@ var ErrMFA = errors.New("account has 2FA enabled")
|
||||||
// tasks if given enough information to do so. Currently you can pass zero
|
// tasks if given enough information to do so. Currently you can pass zero
|
||||||
// arguments and it will return an empty Discord session.
|
// arguments and it will return an empty Discord session.
|
||||||
// There are 3 ways to call New:
|
// There are 3 ways to call New:
|
||||||
// With a single auth token - All requests will use the token blindly,
|
// With a single auth token - All requests will use the token blindly
|
||||||
|
// (just tossing it into the HTTP Authorization header);
|
||||||
// no verification of the token will be done and requests may fail.
|
// 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 `
|
// IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
|
||||||
// eg: `"Bot <token>"`
|
// eg: `"Bot <token>"`
|
||||||
|
// IF IT IS AN OAUTH2 ACCESS TOKEN, IT MUST BE PREFIXED WITH `Bearer `
|
||||||
|
// eg: `"Bearer <token>"`
|
||||||
// With an email and password - Discord will sign in with the provided
|
// With an email and password - Discord will sign in with the provided
|
||||||
// credentials.
|
// credentials.
|
||||||
// With an email, password and auth token - Discord will verify the auth
|
// With an email, password and auth token - Discord will verify the auth
|
||||||
|
@ -63,6 +67,15 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||||
LastHeartbeatAck: time.Now().UTC(),
|
LastHeartbeatAck: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initilize the Identify Package with defaults
|
||||||
|
// These can be modified prior to calling Open()
|
||||||
|
s.Identify.Compress = true
|
||||||
|
s.Identify.LargeThreshold = 250
|
||||||
|
s.Identify.GuildSubscriptions = true
|
||||||
|
s.Identify.Properties.OS = runtime.GOOS
|
||||||
|
s.Identify.Properties.Browser = "DiscordGo v" + VERSION
|
||||||
|
s.Identify.Intents = MakeIntent(IntentsAllWithoutPrivileged)
|
||||||
|
|
||||||
// If no arguments are passed return the empty Session interface.
|
// If no arguments are passed return the empty Session interface.
|
||||||
if args == nil {
|
if args == nil {
|
||||||
return
|
return
|
||||||
|
@ -94,7 +107,8 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||||
|
|
||||||
// If third string exists, it must be an auth token.
|
// If third string exists, it must be an auth token.
|
||||||
if len(v) > 2 {
|
if len(v) > 2 {
|
||||||
s.Token = v[2]
|
s.Identify.Token = v[2]
|
||||||
|
s.Token = v[2] // TODO: Remove, Deprecated - Kept for backwards compatibility.
|
||||||
}
|
}
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
|
@ -107,7 +121,8 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||||
} else if pass == "" {
|
} else if pass == "" {
|
||||||
pass = v
|
pass = v
|
||||||
} else if s.Token == "" {
|
} else if s.Token == "" {
|
||||||
s.Token = v
|
s.Identify.Token = v
|
||||||
|
s.Token = v // TODO: Remove, Deprecated - Kept for backwards compatibility.
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("too many string parameters provided")
|
err = fmt.Errorf("too many string parameters provided")
|
||||||
return
|
return
|
||||||
|
@ -127,10 +142,12 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||||
// Discord will verify it for free, or log the user in if it is
|
// Discord will verify it for free, or log the user in if it is
|
||||||
// invalid.
|
// invalid.
|
||||||
if pass == "" {
|
if pass == "" {
|
||||||
s.Token = auth
|
s.Identify.Token = auth
|
||||||
|
s.Token = auth // TODO: Remove, Deprecated - Kept for backwards compatibility.
|
||||||
} else {
|
} else {
|
||||||
err = s.Login(auth, pass)
|
err = s.Login(auth, pass)
|
||||||
if err != nil || s.Token == "" {
|
// TODO: Remove last s.Token part, Deprecated - Kept for backwards compatibility.
|
||||||
|
if err != nil || s.Identify.Token == "" || s.Token == "" {
|
||||||
if s.MFA {
|
if s.MFA {
|
||||||
err = ErrMFA
|
err = ErrMFA
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,8 +157,5 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Session is now able to have RestAPI methods called on it.
|
|
||||||
// It is recommended that you now call Open() so that events will trigger.
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ support multi-server voice connections and some other features that are
|
||||||
exclusive to Bot accounts only.
|
exclusive to Bot accounts only.
|
||||||
|
|
||||||
To create a new user account (if you have not done so already) visit the
|
To create a new user account (if you have not done so already) visit the
|
||||||
[Discord](https://discordapp.com/) website and click on the
|
[Discord](https://discord.com/) website and click on the
|
||||||
**Try Discord Now, It's Free** button then follow the steps to setup your
|
**Try Discord Now, It's Free** button then follow the steps to setup your
|
||||||
new account.
|
new account.
|
||||||
|
|
||||||
|
@ -77,12 +77,12 @@ have access to some user client specific features however they gain access to
|
||||||
many Bot specific features.
|
many Bot specific features.
|
||||||
|
|
||||||
To create a new bot account first create yourself a normal user account on
|
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)
|
Discord then visit the [My Applications](https://discord.com/developers/applications/me)
|
||||||
page and click on the **New Application** box. Follow the prompts from there
|
page and click on the **New Application** box. Follow the prompts from there
|
||||||
to finish creating your account.
|
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)**
|
**More information about Bots vs Client accounts can be found [here](https://discord.com/developers/docs/topics/oauth2#bot-vs-user-accounts)**
|
||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
<hr>
|
<hr>
|
||||||
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">
|
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">
|
||||||
|
|
||||||
[Go](https://golang.org/) (golang) interface for the [Discord](https://discordapp.com/)
|
[Go](https://golang.org/) (golang) interface for the [Discord](https://discord.com/)
|
||||||
chat service. Provides both low-level direct bindings to the
|
chat service. Provides both low-level direct bindings to the
|
||||||
Discord API and helper functions that allow you to make custom clients and chat
|
Discord API and helper functions that allow you to make custom clients and chat
|
||||||
bot applications easily.
|
bot applications easily.
|
||||||
|
|
||||||
[Discord](https://discordapp.com/) is an all-in-one voice and text chat for
|
[Discord](https://discord.com/) is an all-in-one voice and text chat for
|
||||||
gamers that's free, secure, and works on both your desktop and phone.
|
gamers that's free, secure, and works on both your desktop and phone.
|
||||||
|
|
||||||
### Why DiscordGo?
|
### Why DiscordGo?
|
||||||
|
@ -30,4 +30,4 @@ information and support for DiscordGo. There's also a chance to make some
|
||||||
friends :)
|
friends :)
|
||||||
|
|
||||||
* Join the [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP) chat server dedicated to Go programming.
|
* Join the [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP) chat server dedicated to Go programming.
|
||||||
* Join the [Discord API](https://discordapp.com/invite/discord-API) chat server dedicated to the Discord API.
|
* Join the [Discord API](https://discord.com/invite/discord-API) chat server dedicated to the Discord API.
|
||||||
|
|
|
@ -18,12 +18,12 @@ var APIVersion = "6"
|
||||||
|
|
||||||
// Known Discord API Endpoints.
|
// Known Discord API Endpoints.
|
||||||
var (
|
var (
|
||||||
EndpointStatus = "https://status.discordapp.com/api/v2/"
|
EndpointStatus = "https://status.discord.com/api/v2/"
|
||||||
EndpointSm = EndpointStatus + "scheduled-maintenances/"
|
EndpointSm = EndpointStatus + "scheduled-maintenances/"
|
||||||
EndpointSmActive = EndpointSm + "active.json"
|
EndpointSmActive = EndpointSm + "active.json"
|
||||||
EndpointSmUpcoming = EndpointSm + "upcoming.json"
|
EndpointSmUpcoming = EndpointSm + "upcoming.json"
|
||||||
|
|
||||||
EndpointDiscord = "https://discordapp.com/"
|
EndpointDiscord = "https://discord.com/"
|
||||||
EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
|
EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
|
||||||
EndpointGuilds = EndpointAPI + "guilds/"
|
EndpointGuilds = EndpointAPI + "guilds/"
|
||||||
EndpointChannels = EndpointAPI + "channels/"
|
EndpointChannels = EndpointAPI + "channels/"
|
||||||
|
|
2
event.go
2
event.go
|
@ -110,7 +110,7 @@ func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
|
||||||
// })
|
// })
|
||||||
//
|
//
|
||||||
// List of events can be found at this page, with corresponding names in the
|
// List of events can be found at this page, with corresponding names in the
|
||||||
// library for each event: https://discordapp.com/developers/docs/topics/gateway#event-names
|
// library for each event: https://discord.com/developers/docs/topics/gateway#event-names
|
||||||
// There are also synthetic events fired by the library internally which are
|
// There are also synthetic events fired by the library internally which are
|
||||||
// available for handling, like Connect, Disconnect, and RateLimit.
|
// available for handling, like Connect, Disconnect, and RateLimit.
|
||||||
// events.go contains all of the Discord WSAPI and synthetic events that can be handled.
|
// events.go contains all of the Discord WSAPI and synthetic events that can be handled.
|
||||||
|
|
|
@ -141,6 +141,9 @@ type GuildEmojisUpdate struct {
|
||||||
type GuildMembersChunk struct {
|
type GuildMembersChunk struct {
|
||||||
GuildID string `json:"guild_id"`
|
GuildID string `json:"guild_id"`
|
||||||
Members []*Member `json:"members"`
|
Members []*Member `json:"members"`
|
||||||
|
ChunkIndex int `json:"chunk_index"`
|
||||||
|
ChunkCount int `json:"chunk_count"`
|
||||||
|
Presences []*Presence `json:"presences,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
|
// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
|
||||||
|
@ -169,6 +172,7 @@ type MessageUpdate struct {
|
||||||
// MessageDelete is the data for a MessageDelete event.
|
// MessageDelete is the data for a MessageDelete event.
|
||||||
type MessageDelete struct {
|
type MessageDelete struct {
|
||||||
*Message
|
*Message
|
||||||
|
BeforeDelete *Message `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageReactionAdd is the data for a MessageReactionAdd event.
|
// MessageReactionAdd is the data for a MessageReactionAdd event.
|
||||||
|
|
|
@ -6,7 +6,7 @@ This example demonstrates how to utilize DiscordGo to create, view, and delete
|
||||||
Bot Applications on your account.
|
Bot Applications on your account.
|
||||||
|
|
||||||
These tasks are normally accomplished from the
|
These tasks are normally accomplished from the
|
||||||
[Discord Developers](https://discordapp.com/developers/applications/me) site.
|
[Discord Developers](https://discord.com/developers/applications/me) site.
|
||||||
|
|
||||||
**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
|
**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
|
||||||
Discord chat channel for support.**
|
Discord chat channel for support.**
|
||||||
|
|
|
@ -51,7 +51,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function will be called (due to AddHandler above) every time a new
|
// 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.
|
// message is created on any channel that the authenticated bot has access to.
|
||||||
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
|
|
||||||
// Ignore all messages created by the bot itself
|
// Ignore all messages created by the bot itself
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -4,3 +4,5 @@ require (
|
||||||
github.com/gorilla/websocket v1.4.0
|
github.com/gorilla/websocket v1.4.0
|
||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go 1.10
|
||||||
|
|
|
@ -37,7 +37,7 @@ const (
|
||||||
// Logger can be used to replace the standard logging for discordgo
|
// Logger can be used to replace the standard logging for discordgo
|
||||||
var Logger func(msgL, caller int, format string, a ...interface{})
|
var Logger func(msgL, caller int, format string, a ...interface{})
|
||||||
|
|
||||||
// msglog provides package wide logging consistancy for discordgo
|
// msglog provides package wide logging consistency for discordgo
|
||||||
// the format, a... portion this command follows that of fmt.Printf
|
// the format, a... portion this command follows that of fmt.Printf
|
||||||
// msgL : LogLevel of the message
|
// msgL : LogLevel of the message
|
||||||
// caller : 1 + the number of callers away from the message source
|
// caller : 1 + the number of callers away from the message source
|
||||||
|
|
42
message.go
42
message.go
|
@ -63,7 +63,7 @@ type Message struct {
|
||||||
MentionRoles []string `json:"mention_roles"`
|
MentionRoles []string `json:"mention_roles"`
|
||||||
|
|
||||||
// Whether the message is text-to-speech.
|
// Whether the message is text-to-speech.
|
||||||
Tts bool `json:"tts"`
|
TTS bool `json:"tts"`
|
||||||
|
|
||||||
// Whether the message mentions everyone.
|
// Whether the message mentions everyone.
|
||||||
MentionEveryone bool `json:"mention_everyone"`
|
MentionEveryone bool `json:"mention_everyone"`
|
||||||
|
@ -131,8 +131,9 @@ type File struct {
|
||||||
type MessageSend struct {
|
type MessageSend struct {
|
||||||
Content string `json:"content,omitempty"`
|
Content string `json:"content,omitempty"`
|
||||||
Embed *MessageEmbed `json:"embed,omitempty"`
|
Embed *MessageEmbed `json:"embed,omitempty"`
|
||||||
Tts bool `json:"tts"`
|
TTS bool `json:"tts"`
|
||||||
Files []*File `json:"-"`
|
Files []*File `json:"-"`
|
||||||
|
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||||
|
|
||||||
// TODO: Remove this when compatibility is not required.
|
// TODO: Remove this when compatibility is not required.
|
||||||
File *File `json:"-"`
|
File *File `json:"-"`
|
||||||
|
@ -143,6 +144,7 @@ type MessageSend struct {
|
||||||
type MessageEdit struct {
|
type MessageEdit struct {
|
||||||
Content *string `json:"content,omitempty"`
|
Content *string `json:"content,omitempty"`
|
||||||
Embed *MessageEmbed `json:"embed,omitempty"`
|
Embed *MessageEmbed `json:"embed,omitempty"`
|
||||||
|
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||||
|
|
||||||
ID string
|
ID string
|
||||||
Channel string
|
Channel string
|
||||||
|
@ -171,6 +173,42 @@ func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllowedMentionType describes the types of mentions used
|
||||||
|
// in the MessageAllowedMentions type.
|
||||||
|
type AllowedMentionType string
|
||||||
|
|
||||||
|
// The types of mentions used in MessageAllowedMentions.
|
||||||
|
const (
|
||||||
|
AllowedMentionTypeRoles AllowedMentionType = "roles"
|
||||||
|
AllowedMentionTypeUsers AllowedMentionType = "users"
|
||||||
|
AllowedMentionTypeEveryone AllowedMentionType = "everyone"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MessageAllowedMentions allows the user to specify which mentions
|
||||||
|
// Discord is allowed to parse in this message. This is useful when
|
||||||
|
// sending user input as a message, as it prevents unwanted mentions.
|
||||||
|
// If this type is used, all mentions must be explicitly whitelisted,
|
||||||
|
// either by putting an AllowedMentionType in the Parse slice
|
||||||
|
// (allowing all mentions of that type) or, in the case of roles and
|
||||||
|
// users, explicitly allowing those mentions on an ID-by-ID basis.
|
||||||
|
// For more information on this functionality, see:
|
||||||
|
// https://discordapp.com/developers/docs/resources/channel#allowed-mentions-object-allowed-mentions-reference
|
||||||
|
type MessageAllowedMentions struct {
|
||||||
|
// The mention types that are allowed to be parsed in this message.
|
||||||
|
// Please note that this is purposely **not** marked as omitempty,
|
||||||
|
// so if a zero-value MessageAllowedMentions object is provided no
|
||||||
|
// mentions will be allowed.
|
||||||
|
Parse []AllowedMentionType `json:"parse"`
|
||||||
|
|
||||||
|
// A list of role IDs to allow. This cannot be used when specifying
|
||||||
|
// AllowedMentionTypeRoles in the Parse slice.
|
||||||
|
Roles []string `json:"roles,omitempty"`
|
||||||
|
|
||||||
|
// A list of user IDs to allow. This cannot be used when specifying
|
||||||
|
// AllowedMentionTypeUsers in the Parse slice.
|
||||||
|
Users []string `json:"users,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// A MessageAttachment stores data for message attachments.
|
// A MessageAttachment stores data for message attachments.
|
||||||
type MessageAttachment struct {
|
type MessageAttachment struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
|
27
restapi.go
27
restapi.go
|
@ -38,7 +38,7 @@ var (
|
||||||
ErrPruneDaysBounds = errors.New("the number of days should be more than or equal to 1")
|
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")
|
ErrGuildNoIcon = errors.New("guild does not have an icon set")
|
||||||
ErrGuildNoSplash = errors.New("guild does not have a splash set")
|
ErrGuildNoSplash = errors.New("guild does not have a splash set")
|
||||||
ErrUnauthorized = errors.New("HTTP request was unauthorized. This could be because the provided token was not a bot token. Please add \"Bot \" to the start of your token. https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header")
|
ErrUnauthorized = errors.New("HTTP request was unauthorized. This could be because the provided token was not a bot token. Please add \"Bot \" to the start of your token. https://discord.com/developers/docs/reference#authentication-example-bot-token-authorization-header")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr
|
// Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr
|
||||||
|
@ -506,7 +506,7 @@ func (s *Session) UserChannelPermissions(userID, channelID string) (apermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculates the permissions for a member.
|
// Calculates the permissions for a member.
|
||||||
// https://support.discordapp.com/hc/en-us/articles/206141927-How-is-the-permission-hierarchy-structured-
|
// https://support.discord.com/hc/en-us/articles/206141927-How-is-the-permission-hierarchy-structured-
|
||||||
func memberPermissions(guild *Guild, channel *Channel, member *Member) (apermissions int) {
|
func memberPermissions(guild *Guild, channel *Channel, member *Member) (apermissions int) {
|
||||||
userID := member.User.ID
|
userID := member.User.ID
|
||||||
|
|
||||||
|
@ -583,14 +583,6 @@ func memberPermissions(guild *Guild, channel *Channel, member *Member) (apermiss
|
||||||
// Guild returns a Guild structure of a specific Guild.
|
// Guild returns a Guild structure of a specific Guild.
|
||||||
// guildID : The ID of a Guild
|
// guildID : The ID of a Guild
|
||||||
func (s *Session) Guild(guildID string) (st *Guild, err error) {
|
func (s *Session) Guild(guildID string) (st *Guild, err error) {
|
||||||
if s.StateEnabled {
|
|
||||||
// Attempt to grab the guild from State first.
|
|
||||||
st, err = s.State.Guild(guildID)
|
|
||||||
if err == nil && !st.Unavailable {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID), nil, EndpointGuild(guildID))
|
body, err := s.RequestWithBucketID("GET", EndpointGuild(guildID), nil, EndpointGuild(guildID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -931,6 +923,8 @@ type GuildChannelCreateData struct {
|
||||||
Topic string `json:"topic,omitempty"`
|
Topic string `json:"topic,omitempty"`
|
||||||
Bitrate int `json:"bitrate,omitempty"`
|
Bitrate int `json:"bitrate,omitempty"`
|
||||||
UserLimit int `json:"user_limit,omitempty"`
|
UserLimit int `json:"user_limit,omitempty"`
|
||||||
|
RateLimitPerUser int `json:"rate_limit_per_user,omitempty"`
|
||||||
|
Position int `json:"position,omitempty"`
|
||||||
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
|
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
|
||||||
ParentID string `json:"parent_id,omitempty"`
|
ParentID string `json:"parent_id,omitempty"`
|
||||||
NSFW bool `json:"nsfw,omitempty"`
|
NSFW bool `json:"nsfw,omitempty"`
|
||||||
|
@ -1593,7 +1587,7 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend)
|
||||||
func (s *Session) ChannelMessageSendTTS(channelID string, content string) (*Message, error) {
|
func (s *Session) ChannelMessageSendTTS(channelID string, content string) (*Message, error) {
|
||||||
return s.ChannelMessageSendComplex(channelID, &MessageSend{
|
return s.ChannelMessageSendComplex(channelID, &MessageSend{
|
||||||
Content: content,
|
Content: content,
|
||||||
Tts: true,
|
TTS: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2132,7 +2126,9 @@ func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error {
|
||||||
// messageID : The message ID.
|
// messageID : The message ID.
|
||||||
// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier.
|
// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier.
|
||||||
// limit : max number of users to return (max 100)
|
// limit : max number of users to return (max 100)
|
||||||
func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int) (st []*User, err error) {
|
// beforeID : If provided all reactions returned will be before given ID.
|
||||||
|
// afterID : If provided all reactions returned will be after given ID.
|
||||||
|
func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit int, beforeID, afterID string) (st []*User, err error) {
|
||||||
// emoji such as #⃣ need to have # escaped
|
// emoji such as #⃣ need to have # escaped
|
||||||
emojiID = strings.Replace(emojiID, "#", "%23", -1)
|
emojiID = strings.Replace(emojiID, "#", "%23", -1)
|
||||||
uri := EndpointMessageReactions(channelID, messageID, emojiID)
|
uri := EndpointMessageReactions(channelID, messageID, emojiID)
|
||||||
|
@ -2143,6 +2139,13 @@ func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit i
|
||||||
v.Set("limit", strconv.Itoa(limit))
|
v.Set("limit", strconv.Itoa(limit))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if afterID != "" {
|
||||||
|
v.Set("after", afterID)
|
||||||
|
}
|
||||||
|
if beforeID != "" {
|
||||||
|
v.Set("before", beforeID)
|
||||||
|
}
|
||||||
|
|
||||||
if len(v) > 0 {
|
if len(v) > 0 {
|
||||||
uri += "?" + v.Encode()
|
uri += "?" + v.Encode()
|
||||||
}
|
}
|
||||||
|
|
13
state.go
13
state.go
|
@ -848,6 +848,12 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
|
||||||
err = s.MemberAdd(t.Members[i])
|
err = s.MemberAdd(t.Members[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.TrackPresences {
|
||||||
|
for _, p := range t.Presences {
|
||||||
|
err = s.PresenceAdd(t.GuildID, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
case *GuildRoleCreate:
|
case *GuildRoleCreate:
|
||||||
if s.TrackRoles {
|
if s.TrackRoles {
|
||||||
err = s.RoleAdd(t.GuildID, t.Role)
|
err = s.RoleAdd(t.GuildID, t.Role)
|
||||||
|
@ -893,6 +899,13 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
|
||||||
}
|
}
|
||||||
case *MessageDelete:
|
case *MessageDelete:
|
||||||
if s.MaxMessageCount != 0 {
|
if s.MaxMessageCount != 0 {
|
||||||
|
var old *Message
|
||||||
|
old, err = s.Message(t.ChannelID, t.ID)
|
||||||
|
if err == nil {
|
||||||
|
oldCopy := *old
|
||||||
|
t.BeforeDelete = &oldCopy
|
||||||
|
}
|
||||||
|
|
||||||
err = s.MessageRemove(t.Message)
|
err = s.MessageRemove(t.Message)
|
||||||
}
|
}
|
||||||
case *MessageDeleteBulk:
|
case *MessageDeleteBulk:
|
||||||
|
|
118
structs.go
118
structs.go
|
@ -29,7 +29,9 @@ type Session struct {
|
||||||
// General configurable settings.
|
// General configurable settings.
|
||||||
|
|
||||||
// Authentication token for this session
|
// Authentication token for this session
|
||||||
|
// TODO: Remove Below, Deprecated, Use Identify struct
|
||||||
Token string
|
Token string
|
||||||
|
|
||||||
MFA bool
|
MFA bool
|
||||||
|
|
||||||
// Debug for printing JSON request/responses
|
// Debug for printing JSON request/responses
|
||||||
|
@ -39,6 +41,11 @@ type Session struct {
|
||||||
// Should the session reconnect the websocket on errors.
|
// Should the session reconnect the websocket on errors.
|
||||||
ShouldReconnectOnError bool
|
ShouldReconnectOnError bool
|
||||||
|
|
||||||
|
// Identify is sent during initial handshake with the discord gateway.
|
||||||
|
// https://discord.com/developers/docs/topics/gateway#identify
|
||||||
|
Identify Identify
|
||||||
|
|
||||||
|
// TODO: Remove Below, Deprecated, Use Identify struct
|
||||||
// Should the session request compressed websocket data.
|
// Should the session request compressed websocket data.
|
||||||
Compress bool
|
Compress bool
|
||||||
|
|
||||||
|
@ -590,6 +597,7 @@ type Presence struct {
|
||||||
User *User `json:"user"`
|
User *User `json:"user"`
|
||||||
Status Status `json:"status"`
|
Status Status `json:"status"`
|
||||||
Game *Game `json:"game"`
|
Game *Game `json:"game"`
|
||||||
|
Activities []*Game `json:"activities"`
|
||||||
Nick string `json:"nick"`
|
Nick string `json:"nick"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
Since *int `json:"since"`
|
Since *int `json:"since"`
|
||||||
|
@ -604,6 +612,7 @@ const (
|
||||||
GameTypeStreaming
|
GameTypeStreaming
|
||||||
GameTypeListening
|
GameTypeListening
|
||||||
GameTypeWatching
|
GameTypeWatching
|
||||||
|
GameTypeCustom
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Game struct holds the name of the "playing .." game for a user
|
// A Game struct holds the name of the "playing .." game for a user
|
||||||
|
@ -687,7 +696,7 @@ type Settings struct {
|
||||||
RenderEmbeds bool `json:"render_embeds"`
|
RenderEmbeds bool `json:"render_embeds"`
|
||||||
InlineEmbedMedia bool `json:"inline_embed_media"`
|
InlineEmbedMedia bool `json:"inline_embed_media"`
|
||||||
InlineAttachmentMedia bool `json:"inline_attachment_media"`
|
InlineAttachmentMedia bool `json:"inline_attachment_media"`
|
||||||
EnableTtsCommand bool `json:"enable_tts_command"`
|
EnableTTSCommand bool `json:"enable_tts_command"`
|
||||||
MessageDisplayCompact bool `json:"message_display_compact"`
|
MessageDisplayCompact bool `json:"message_display_compact"`
|
||||||
ShowCurrentGame bool `json:"show_current_game"`
|
ShowCurrentGame bool `json:"show_current_game"`
|
||||||
ConvertEmoticons bool `json:"convert_emoticons"`
|
ConvertEmoticons bool `json:"convert_emoticons"`
|
||||||
|
@ -909,8 +918,63 @@ type GatewayBotResponse struct {
|
||||||
Shards int `json:"shards"`
|
Shards int `json:"shards"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GatewayStatusUpdate is sent by the client to indicate a presence or status update
|
||||||
|
// https://discord.com/developers/docs/topics/gateway#update-status-gateway-status-update-structure
|
||||||
|
type GatewayStatusUpdate struct {
|
||||||
|
Since int `json:"since"`
|
||||||
|
Game Activity `json:"game"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
AFK bool `json:"afk"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activity defines the Activity sent with GatewayStatusUpdate
|
||||||
|
// https://discord.com/developers/docs/topics/gateway#activity-object
|
||||||
|
type Activity struct {
|
||||||
|
Name string
|
||||||
|
Type ActivityType
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActivityType is the type of Activity (see ActivityType* consts) in the Activity struct
|
||||||
|
// https://discord.com/developers/docs/topics/gateway#activity-object-activity-types
|
||||||
|
type ActivityType int
|
||||||
|
|
||||||
|
// Valid ActivityType values
|
||||||
|
// https://discord.com/developers/docs/topics/gateway#activity-object-activity-types
|
||||||
|
const (
|
||||||
|
ActivityTypeGame GameType = iota
|
||||||
|
ActivityTypeStreaming
|
||||||
|
ActivityTypeListening
|
||||||
|
// ActivityTypeWatching // not valid in this use case?
|
||||||
|
ActivityTypeCustom = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Identify is sent during initial handshake with the discord gateway.
|
||||||
|
// https://discord.com/developers/docs/topics/gateway#identify
|
||||||
|
type Identify struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
Properties IdentifyProperties `json:"properties"`
|
||||||
|
Compress bool `json:"compress"`
|
||||||
|
LargeThreshold int `json:"large_threshold"`
|
||||||
|
Shard *[2]int `json:"shard,omitempty"`
|
||||||
|
Presence GatewayStatusUpdate `json:"presence,omitempty"`
|
||||||
|
GuildSubscriptions bool `json:"guild_subscriptions"`
|
||||||
|
Intents *Intent `json:"intents,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdentifyProperties contains the "properties" portion of an Identify packet
|
||||||
|
// https://discord.com/developers/docs/topics/gateway#identify-identify-connection-properties
|
||||||
|
type IdentifyProperties struct {
|
||||||
|
OS string `json:"$os"`
|
||||||
|
Browser string `json:"$browser"`
|
||||||
|
Device string `json:"$device"`
|
||||||
|
Referer string `json:"$referer"`
|
||||||
|
ReferringDomain string `json:"$referring_domain"`
|
||||||
|
}
|
||||||
|
|
||||||
// Constants for the different bit offsets of text channel permissions
|
// Constants for the different bit offsets of text channel permissions
|
||||||
const (
|
const (
|
||||||
|
// Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels
|
||||||
PermissionReadMessages = 1 << (iota + 10)
|
PermissionReadMessages = 1 << (iota + 10)
|
||||||
PermissionSendMessages
|
PermissionSendMessages
|
||||||
PermissionSendTTSMessages
|
PermissionSendTTSMessages
|
||||||
|
@ -952,8 +1016,9 @@ const (
|
||||||
PermissionManageServer
|
PermissionManageServer
|
||||||
PermissionAddReactions
|
PermissionAddReactions
|
||||||
PermissionViewAuditLogs
|
PermissionViewAuditLogs
|
||||||
|
PermissionViewChannel = 1 << (iota + 2)
|
||||||
|
|
||||||
PermissionAllText = PermissionReadMessages |
|
PermissionAllText = PermissionViewChannel |
|
||||||
PermissionSendMessages |
|
PermissionSendMessages |
|
||||||
PermissionSendTTSMessages |
|
PermissionSendTTSMessages |
|
||||||
PermissionManageMessages |
|
PermissionManageMessages |
|
||||||
|
@ -961,7 +1026,8 @@ const (
|
||||||
PermissionAttachFiles |
|
PermissionAttachFiles |
|
||||||
PermissionReadMessageHistory |
|
PermissionReadMessageHistory |
|
||||||
PermissionMentionEveryone
|
PermissionMentionEveryone
|
||||||
PermissionAllVoice = PermissionVoiceConnect |
|
PermissionAllVoice = PermissionViewChannel |
|
||||||
|
PermissionVoiceConnect |
|
||||||
PermissionVoiceSpeak |
|
PermissionVoiceSpeak |
|
||||||
PermissionVoiceMuteMembers |
|
PermissionVoiceMuteMembers |
|
||||||
PermissionVoiceDeafenMembers |
|
PermissionVoiceDeafenMembers |
|
||||||
|
@ -1037,3 +1103,49 @@ const (
|
||||||
|
|
||||||
ErrCodeReactionBlocked = 90001
|
ErrCodeReactionBlocked = 90001
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Intent is the type of a Gateway Intent
|
||||||
|
// https://discord.com/developers/docs/topics/gateway#gateway-intents
|
||||||
|
type Intent int
|
||||||
|
|
||||||
|
// Constants for the different bit offsets of intents
|
||||||
|
const (
|
||||||
|
IntentsGuilds Intent = 1 << iota
|
||||||
|
IntentsGuildMembers
|
||||||
|
IntentsGuildBans
|
||||||
|
IntentsGuildEmojis
|
||||||
|
IntentsGuildIntegrations
|
||||||
|
IntentsGuildWebhooks
|
||||||
|
IntentsGuildInvites
|
||||||
|
IntentsGuildVoiceStates
|
||||||
|
IntentsGuildPresences
|
||||||
|
IntentsGuildMessages
|
||||||
|
IntentsGuildMessageReactions
|
||||||
|
IntentsGuildMessageTyping
|
||||||
|
IntentsDirectMessages
|
||||||
|
IntentsDirectMessageReactions
|
||||||
|
IntentsDirectMessageTyping
|
||||||
|
|
||||||
|
IntentsAllWithoutPrivileged = IntentsGuilds |
|
||||||
|
IntentsGuildBans |
|
||||||
|
IntentsGuildEmojis |
|
||||||
|
IntentsGuildIntegrations |
|
||||||
|
IntentsGuildWebhooks |
|
||||||
|
IntentsGuildInvites |
|
||||||
|
IntentsGuildVoiceStates |
|
||||||
|
IntentsGuildMessages |
|
||||||
|
IntentsGuildMessageReactions |
|
||||||
|
IntentsGuildMessageTyping |
|
||||||
|
IntentsDirectMessages |
|
||||||
|
IntentsDirectMessageReactions |
|
||||||
|
IntentsDirectMessageTyping
|
||||||
|
IntentsAll = IntentsAllWithoutPrivileged |
|
||||||
|
IntentsGuildMembers |
|
||||||
|
IntentsGuildPresences
|
||||||
|
IntentsNone Intent = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// MakeIntent helps convert a gateway intent value for use in the Identify structure.
|
||||||
|
func MakeIntent(intents Intent) *Intent {
|
||||||
|
return &intents
|
||||||
|
}
|
||||||
|
|
2
util.go
2
util.go
|
@ -12,6 +12,6 @@ func SnowflakeTimestamp(ID string) (t time.Time, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
timestamp := (i >> 22) + 1420070400000
|
timestamp := (i >> 22) + 1420070400000
|
||||||
t = time.Unix(timestamp/1000, 0)
|
t = time.Unix(0, timestamp*1000000)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
21
util_test.go
Normal file
21
util_test.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package discordgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSnowflakeTimestamp(t *testing.T) {
|
||||||
|
// #discordgo channel ID :)
|
||||||
|
id := "155361364909621248"
|
||||||
|
parsedTimestamp, err := SnowflakeTimestamp(id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("returned error incorrect: got %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
correctTimestamp := time.Date(2016, time.March, 4, 17, 10, 35, 869*1000000, time.UTC)
|
||||||
|
if !parsedTimestamp.Equal(correctTimestamp) {
|
||||||
|
t.Errorf("parsed time incorrect: got %v, want %v", parsedTimestamp, correctTimestamp)
|
||||||
|
}
|
||||||
|
}
|
19
voice.go
19
voice.go
|
@ -346,6 +346,25 @@ func (v *VoiceConnection) wsListen(wsConn *websocket.Conn, close <-chan struct{}
|
||||||
for {
|
for {
|
||||||
_, message, err := v.wsConn.ReadMessage()
|
_, message, err := v.wsConn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// 4014 indicates a manual disconnection by someone in the guild;
|
||||||
|
// we shouldn't reconnect.
|
||||||
|
if websocket.IsCloseError(err, 4014) {
|
||||||
|
v.log(LogInformational, "received 4014 manual disconnection")
|
||||||
|
|
||||||
|
// Abandon the voice WS connection
|
||||||
|
v.Lock()
|
||||||
|
v.wsConn = nil
|
||||||
|
v.Unlock()
|
||||||
|
|
||||||
|
v.session.Lock()
|
||||||
|
delete(v.session.VoiceConnections, v.GuildID)
|
||||||
|
v.session.Unlock()
|
||||||
|
|
||||||
|
v.Close()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Detect if we have been closed manually. If a Close() has already
|
// Detect if we have been closed manually. If a Close() has already
|
||||||
// happened, the websocket we are listening on will be different to the
|
// happened, the websocket we are listening on will be different to the
|
||||||
// current session.
|
// current session.
|
||||||
|
|
101
wsapi.go
101
wsapi.go
|
@ -18,7 +18,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ type resumePacket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open creates a websocket connection to Discord.
|
// Open creates a websocket connection to Discord.
|
||||||
// See: https://discordapp.com/developers/docs/topics/gateway#connecting
|
// See: https://discord.com/developers/docs/topics/gateway#connecting
|
||||||
func (s *Session) Open() error {
|
func (s *Session) Open() error {
|
||||||
s.log(LogInformational, "called")
|
s.log(LogInformational, "called")
|
||||||
|
|
||||||
|
@ -80,7 +79,7 @@ func (s *Session) Open() error {
|
||||||
header.Add("accept-encoding", "zlib")
|
header.Add("accept-encoding", "zlib")
|
||||||
s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header)
|
s.wsConn, _, err = websocket.DefaultDialer.Dial(s.gateway, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log(LogWarning, "error connecting to gateway %s, %s", s.gateway, err)
|
s.log(LogError, "error connecting to gateway %s, %s", s.gateway, err)
|
||||||
s.gateway = "" // clear cached gateway
|
s.gateway = "" // clear cached gateway
|
||||||
s.wsConn = nil // Just to be safe.
|
s.wsConn = nil // Just to be safe.
|
||||||
return err
|
return err
|
||||||
|
@ -399,9 +398,10 @@ func (s *Session) UpdateStatusComplex(usd UpdateStatusData) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type requestGuildMembersData struct {
|
type requestGuildMembersData struct {
|
||||||
GuildID string `json:"guild_id"`
|
GuildIDs []string `json:"guild_id"`
|
||||||
Query string `json:"query"`
|
Query string `json:"query"`
|
||||||
Limit int `json:"limit"`
|
Limit int `json:"limit"`
|
||||||
|
Presences bool `json:"presences"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type requestGuildMembersOp struct {
|
type requestGuildMembersOp struct {
|
||||||
|
@ -411,10 +411,39 @@ type requestGuildMembersOp struct {
|
||||||
|
|
||||||
// RequestGuildMembers requests guild members from the gateway
|
// RequestGuildMembers requests guild members from the gateway
|
||||||
// The gateway responds with GuildMembersChunk events
|
// The gateway responds with GuildMembersChunk events
|
||||||
// guildID : The ID of the guild to request members of
|
// guildID : Single Guild ID to request members of
|
||||||
// query : String that username starts with, leave empty to return all members
|
// 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
|
// 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) {
|
// presences : Whether to request presences of guild members
|
||||||
|
func (s *Session) RequestGuildMembers(guildID string, query string, limit int, presences bool) (err error) {
|
||||||
|
data := requestGuildMembersData{
|
||||||
|
GuildIDs: []string{guildID},
|
||||||
|
Query: query,
|
||||||
|
Limit: limit,
|
||||||
|
Presences: presences,
|
||||||
|
}
|
||||||
|
err = s.requestGuildMembers(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestGuildMembersBatch requests guild members from the gateway
|
||||||
|
// The gateway responds with GuildMembersChunk events
|
||||||
|
// guildID : Slice of guild IDs 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
|
||||||
|
// presences : Whether to request presences of guild members
|
||||||
|
func (s *Session) RequestGuildMembersBatch(guildIDs []string, query string, limit int, presences bool) (err error) {
|
||||||
|
data := requestGuildMembersData{
|
||||||
|
GuildIDs: guildIDs,
|
||||||
|
Query: query,
|
||||||
|
Limit: limit,
|
||||||
|
Presences: presences,
|
||||||
|
}
|
||||||
|
err = s.requestGuildMembers(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) requestGuildMembers(data requestGuildMembersData) (err error) {
|
||||||
s.log(LogInformational, "called")
|
s.log(LogInformational, "called")
|
||||||
|
|
||||||
s.RLock()
|
s.RLock()
|
||||||
|
@ -423,12 +452,6 @@ func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err err
|
||||||
return ErrWSNotFound
|
return ErrWSNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
data := requestGuildMembersData{
|
|
||||||
GuildID: guildID,
|
|
||||||
Query: query,
|
|
||||||
Limit: limit,
|
|
||||||
}
|
|
||||||
|
|
||||||
s.wsMutex.Lock()
|
s.wsMutex.Lock()
|
||||||
err = s.wsConn.WriteJSON(requestGuildMembersOp{8, data})
|
err = s.wsConn.WriteJSON(requestGuildMembersOp{8, data})
|
||||||
s.wsMutex.Unlock()
|
s.wsMutex.Unlock()
|
||||||
|
@ -722,55 +745,42 @@ func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
type identifyOp struct {
|
||||||
Op int `json:"op"`
|
Op int `json:"op"`
|
||||||
Data identifyData `json:"d"`
|
Data Identify `json:"d"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// identify sends the identify packet to the gateway
|
// identify sends the identify packet to the gateway
|
||||||
func (s *Session) identify() error {
|
func (s *Session) identify() error {
|
||||||
|
s.log(LogDebug, "called")
|
||||||
|
|
||||||
properties := identifyProperties{runtime.GOOS,
|
// TODO: This is a temporary block of code to help
|
||||||
"Discordgo v" + VERSION,
|
// maintain backwards compatability
|
||||||
"",
|
if s.Compress == false {
|
||||||
"",
|
s.Identify.Compress = false
|
||||||
"",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data := identifyData{s.Token,
|
// TODO: This is a temporary block of code to help
|
||||||
properties,
|
// maintain backwards compatability
|
||||||
250,
|
if s.Token != "" && s.Identify.Token == "" {
|
||||||
s.Compress,
|
s.Identify.Token = s.Token
|
||||||
nil,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Below block should be refactored so ShardID and ShardCount
|
||||||
|
// can be deprecated and their usage moved to the Session.Identify
|
||||||
|
// struct
|
||||||
if s.ShardCount > 1 {
|
if s.ShardCount > 1 {
|
||||||
|
|
||||||
if s.ShardID >= s.ShardCount {
|
if s.ShardID >= s.ShardCount {
|
||||||
return ErrWSShardBounds
|
return ErrWSShardBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Shard = &[2]int{s.ShardID, s.ShardCount}
|
s.Identify.Shard = &[2]int{s.ShardID, s.ShardCount}
|
||||||
}
|
}
|
||||||
|
|
||||||
op := identifyOp{2, data}
|
// Send Identify packet to Discord
|
||||||
|
op := identifyOp{2, s.Identify}
|
||||||
|
s.log(LogDebug, "Identify Packet: \n%#v", op)
|
||||||
s.wsMutex.Lock()
|
s.wsMutex.Lock()
|
||||||
err := s.wsConn.WriteJSON(op)
|
err := s.wsConn.WriteJSON(op)
|
||||||
s.wsMutex.Unlock()
|
s.wsMutex.Unlock()
|
||||||
|
@ -838,6 +848,13 @@ func (s *Session) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes a websocket and stops all listening/heartbeat goroutines.
|
// Close closes a websocket and stops all listening/heartbeat goroutines.
|
||||||
|
// TODO: Add support for Voice WS/UDP
|
||||||
|
func (s *Session) Close() error {
|
||||||
|
return s.CloseWithCode(websocket.CloseNormalClosure)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWithCode closes a websocket using the provided closeCode and stops all
|
||||||
|
// listening/heartbeat goroutines.
|
||||||
// TODO: Add support for Voice WS/UDP connections
|
// TODO: Add support for Voice WS/UDP connections
|
||||||
func (s *Session) CloseWithCode(closeCode int) (err error) {
|
func (s *Session) CloseWithCode(closeCode int) (err error) {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue