mirror of
https://github.com/cwinfo/matterbridge.git
synced 2024-11-23 00:01:37 +00:00
Update vendor (bwmarrin/discordgo) apiv6
This commit is contained in:
parent
812db2d267
commit
49204cafcc
3
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
3
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
@ -21,7 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
||||||
const VERSION = "0.16.0"
|
const VERSION = "0.17.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")
|
||||||
@ -59,6 +59,7 @@ func New(args ...interface{}) (s *Session, err error) {
|
|||||||
MaxRestRetries: 3,
|
MaxRestRetries: 3,
|
||||||
Client: &http.Client{Timeout: (20 * time.Second)},
|
Client: &http.Client{Timeout: (20 * time.Second)},
|
||||||
sequence: new(int64),
|
sequence: new(int64),
|
||||||
|
LastHeartbeatAck: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no arguments are passed return the empty Session interface.
|
// If no arguments are passed return the empty Session interface.
|
||||||
|
10
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
10
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
package discordgo
|
package discordgo
|
||||||
|
|
||||||
|
// APIVersion is the Discord API version used for the REST and Websocket API.
|
||||||
|
var APIVersion = "6"
|
||||||
|
|
||||||
// Known Discord API Endpoints.
|
// Known Discord API Endpoints.
|
||||||
var (
|
var (
|
||||||
EndpointStatus = "https://status.discordapp.com/api/v2/"
|
EndpointStatus = "https://status.discordapp.com/api/v2/"
|
||||||
@ -19,11 +22,12 @@ var (
|
|||||||
EndpointSmUpcoming = EndpointSm + "upcoming.json"
|
EndpointSmUpcoming = EndpointSm + "upcoming.json"
|
||||||
|
|
||||||
EndpointDiscord = "https://discordapp.com/"
|
EndpointDiscord = "https://discordapp.com/"
|
||||||
EndpointAPI = EndpointDiscord + "api/"
|
EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
|
||||||
EndpointGuilds = EndpointAPI + "guilds/"
|
EndpointGuilds = EndpointAPI + "guilds/"
|
||||||
EndpointChannels = EndpointAPI + "channels/"
|
EndpointChannels = EndpointAPI + "channels/"
|
||||||
EndpointUsers = EndpointAPI + "users/"
|
EndpointUsers = EndpointAPI + "users/"
|
||||||
EndpointGateway = EndpointAPI + "gateway"
|
EndpointGateway = EndpointAPI + "gateway"
|
||||||
|
EndpointGatewayBot = EndpointGateway + "/bot"
|
||||||
EndpointWebhooks = EndpointAPI + "webhooks/"
|
EndpointWebhooks = EndpointAPI + "webhooks/"
|
||||||
|
|
||||||
EndpointCDN = "https://cdn.discordapp.com/"
|
EndpointCDN = "https://cdn.discordapp.com/"
|
||||||
@ -56,6 +60,7 @@ var (
|
|||||||
|
|
||||||
EndpointUser = func(uID string) string { return EndpointUsers + uID }
|
EndpointUser = func(uID string) string { return EndpointUsers + uID }
|
||||||
EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
|
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" }
|
EndpointUserSettings = func(uID string) string { return EndpointUsers + uID + "/settings" }
|
||||||
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
||||||
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
||||||
@ -103,6 +108,9 @@ var (
|
|||||||
EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
|
EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
|
||||||
EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
|
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 {
|
EndpointMessageReactions = func(cID, mID, eID string) string {
|
||||||
return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
|
return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
|
||||||
}
|
}
|
||||||
|
10
vendor/github.com/bwmarrin/discordgo/event.go
generated
vendored
10
vendor/github.com/bwmarrin/discordgo/event.go
generated
vendored
@ -156,13 +156,21 @@ func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance
|
|||||||
// Handles calling permanent and once handlers for an event type.
|
// Handles calling permanent and once handlers for an event type.
|
||||||
func (s *Session) handle(t string, i interface{}) {
|
func (s *Session) handle(t string, i interface{}) {
|
||||||
for _, eh := range s.handlers[t] {
|
for _, eh := range s.handlers[t] {
|
||||||
|
if s.SyncEvents {
|
||||||
|
eh.eventHandler.Handle(s, i)
|
||||||
|
} else {
|
||||||
go eh.eventHandler.Handle(s, i)
|
go eh.eventHandler.Handle(s, i)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(s.onceHandlers[t]) > 0 {
|
if len(s.onceHandlers[t]) > 0 {
|
||||||
for _, eh := range s.onceHandlers[t] {
|
for _, eh := range s.onceHandlers[t] {
|
||||||
|
if s.SyncEvents {
|
||||||
|
eh.eventHandler.Handle(s, i)
|
||||||
|
} else {
|
||||||
go eh.eventHandler.Handle(s, i)
|
go eh.eventHandler.Handle(s, i)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
s.onceHandlers[t] = nil
|
s.onceHandlers[t] = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,7 +224,7 @@ func (s *Session) onInterface(i interface{}) {
|
|||||||
case *VoiceStateUpdate:
|
case *VoiceStateUpdate:
|
||||||
go s.onVoiceStateUpdate(t)
|
go s.onVoiceStateUpdate(t)
|
||||||
}
|
}
|
||||||
err := s.State.onInterface(s, i)
|
err := s.State.OnInterface(s, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log(LogDebug, "error dispatching internal event, %s", err)
|
s.log(LogDebug, "error dispatching internal event, %s", err)
|
||||||
}
|
}
|
||||||
|
90
vendor/github.com/bwmarrin/discordgo/message.go
generated
vendored
90
vendor/github.com/bwmarrin/discordgo/message.go
generated
vendored
@ -10,9 +10,24 @@
|
|||||||
package discordgo
|
package discordgo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"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.
|
// A Message stores all data related to a specific Discord message.
|
||||||
@ -30,11 +45,13 @@ type Message struct {
|
|||||||
Embeds []*MessageEmbed `json:"embeds"`
|
Embeds []*MessageEmbed `json:"embeds"`
|
||||||
Mentions []*User `json:"mentions"`
|
Mentions []*User `json:"mentions"`
|
||||||
Reactions []*MessageReactions `json:"reactions"`
|
Reactions []*MessageReactions `json:"reactions"`
|
||||||
|
Type MessageType `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// File stores info about files you e.g. send in messages.
|
// File stores info about files you e.g. send in messages.
|
||||||
type File struct {
|
type File struct {
|
||||||
Name string
|
Name string
|
||||||
|
ContentType string
|
||||||
Reader io.Reader
|
Reader io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +60,10 @@ 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"`
|
||||||
File *File `json:"file"`
|
Files []*File `json:"-"`
|
||||||
|
|
||||||
|
// TODO: Remove this when compatibility is not required.
|
||||||
|
File *File `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
|
// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
|
||||||
@ -168,13 +188,65 @@ type MessageReactions struct {
|
|||||||
|
|
||||||
// ContentWithMentionsReplaced will replace all @<id> mentions with the
|
// ContentWithMentionsReplaced will replace all @<id> mentions with the
|
||||||
// username of the mention.
|
// username of the mention.
|
||||||
func (m *Message) ContentWithMentionsReplaced() string {
|
func (m *Message) ContentWithMentionsReplaced() (content string) {
|
||||||
if m.Mentions == nil {
|
content = m.Content
|
||||||
return m.Content
|
|
||||||
}
|
|
||||||
content := m.Content
|
|
||||||
for _, user := range m.Mentions {
|
for _, user := range m.Mentions {
|
||||||
content = regexp.MustCompile(fmt.Sprintf("<@!?(%s)>", user.ID)).ReplaceAllString(content, "@"+user.Username)
|
content = strings.NewReplacer(
|
||||||
|
"<@"+user.ID+">", "@"+user.Username,
|
||||||
|
"<@!"+user.ID+">", "@"+user.Username,
|
||||||
|
).Replace(content)
|
||||||
}
|
}
|
||||||
return 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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
41
vendor/github.com/bwmarrin/discordgo/ratelimit.go
generated
vendored
41
vendor/github.com/bwmarrin/discordgo/ratelimit.go
generated
vendored
@ -3,17 +3,26 @@ package discordgo
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"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
|
// RateLimiter holds all ratelimit buckets
|
||||||
type RateLimiter struct {
|
type RateLimiter struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
global *int64
|
global *int64
|
||||||
buckets map[string]*Bucket
|
buckets map[string]*Bucket
|
||||||
globalRateLimit time.Duration
|
globalRateLimit time.Duration
|
||||||
|
customRateLimits []*customRateLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRatelimiter returns a new RateLimiter
|
// NewRatelimiter returns a new RateLimiter
|
||||||
@ -22,6 +31,13 @@ func NewRatelimiter() *RateLimiter {
|
|||||||
return &RateLimiter{
|
return &RateLimiter{
|
||||||
buckets: make(map[string]*Bucket),
|
buckets: make(map[string]*Bucket),
|
||||||
global: new(int64),
|
global: new(int64),
|
||||||
|
customRateLimits: []*customRateLimit{
|
||||||
|
&customRateLimit{
|
||||||
|
suffix: "//reactions//",
|
||||||
|
requests: 1,
|
||||||
|
reset: 200 * time.Millisecond,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +56,14 @@ func (r *RateLimiter) getBucket(key string) *Bucket {
|
|||||||
global: r.global,
|
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
|
r.buckets[key] = b
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
@ -76,13 +100,28 @@ type Bucket struct {
|
|||||||
limit int
|
limit int
|
||||||
reset time.Time
|
reset time.Time
|
||||||
global *int64
|
global *int64
|
||||||
|
|
||||||
|
lastReset time.Time
|
||||||
|
customRateLimit *customRateLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
|
// 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.
|
// and locks up the whole thing in case if there's a global ratelimit.
|
||||||
func (b *Bucket) Release(headers http.Header) error {
|
func (b *Bucket) Release(headers http.Header) error {
|
||||||
|
|
||||||
defer b.Unlock()
|
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 {
|
if headers == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
138
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
138
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
@ -23,6 +23,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -309,8 +310,8 @@ func (s *Session) UserUpdate(email, password, username, avatar, newPassword stri
|
|||||||
// If left blank, avatar will be set to null/blank
|
// If left blank, avatar will be set to null/blank
|
||||||
|
|
||||||
data := struct {
|
data := struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email,omitempty"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password,omitempty"`
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
Avatar string `json:"avatar,omitempty"`
|
Avatar string `json:"avatar,omitempty"`
|
||||||
NewPassword string `json:"new_password,omitempty"`
|
NewPassword string `json:"new_password,omitempty"`
|
||||||
@ -763,7 +764,21 @@ func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) {
|
|||||||
// userID : The ID of a User
|
// userID : The ID of a User
|
||||||
func (s *Session) GuildMemberDelete(guildID, userID string) (err error) {
|
func (s *Session) GuildMemberDelete(guildID, userID string) (err error) {
|
||||||
|
|
||||||
_, err = s.RequestWithBucketID("DELETE", EndpointGuildMember(guildID, userID), nil, EndpointGuildMember(guildID, ""))
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1316,6 +1331,8 @@ func (s *Session) ChannelMessageSend(channelID string, content string) (*Message
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||||
|
|
||||||
// ChannelMessageSendComplex sends a message to the given channel.
|
// ChannelMessageSendComplex sends a message to the given channel.
|
||||||
// channelID : The ID of a Channel.
|
// channelID : The ID of a Channel.
|
||||||
// data : The message struct to send.
|
// data : The message struct to send.
|
||||||
@ -1326,48 +1343,62 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend)
|
|||||||
|
|
||||||
endpoint := EndpointChannelMessages(channelID)
|
endpoint := EndpointChannelMessages(channelID)
|
||||||
|
|
||||||
var response []byte
|
// TODO: Remove this when compatibility is not required.
|
||||||
|
files := data.Files
|
||||||
if data.File != nil {
|
if data.File != nil {
|
||||||
|
if files == nil {
|
||||||
|
files = []*File{data.File}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("cannot specify both File and Files")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var response []byte
|
||||||
|
if len(files) > 0 {
|
||||||
body := &bytes.Buffer{}
|
body := &bytes.Buffer{}
|
||||||
bodywriter := multipart.NewWriter(body)
|
bodywriter := multipart.NewWriter(body)
|
||||||
|
|
||||||
// What's a better way of doing this? Reflect? Generator? I'm open to suggestions
|
var payload []byte
|
||||||
|
payload, err = json.Marshal(data)
|
||||||
if data.Content != "" {
|
|
||||||
if err = bodywriter.WriteField("content", data.Content); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.Embed != nil {
|
|
||||||
var embed []byte
|
|
||||||
embed, err = json.Marshal(data.Embed)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = bodywriter.WriteField("embed", string(embed))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.Tts {
|
|
||||||
if err = bodywriter.WriteField("tts", "true"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var writer io.Writer
|
|
||||||
writer, err = bodywriter.CreateFormFile("file", data.File.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.Copy(writer, data.File.Reader)
|
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 {
|
if err != nil {
|
||||||
return
|
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()
|
err = bodywriter.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -1685,6 +1716,28 @@ func (s *Session) Gateway() (gateway string, err error) {
|
|||||||
return
|
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
|
// Functions specific to Webhooks
|
||||||
|
|
||||||
// WebhookCreate returns a new Webhook.
|
// WebhookCreate returns a new Webhook.
|
||||||
@ -1810,14 +1863,9 @@ func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string) (s
|
|||||||
|
|
||||||
// WebhookDelete deletes a webhook for a given ID
|
// WebhookDelete deletes a webhook for a given ID
|
||||||
// webhookID: The ID of a webhook.
|
// webhookID: The ID of a webhook.
|
||||||
func (s *Session) WebhookDelete(webhookID string) (st *Webhook, err error) {
|
func (s *Session) WebhookDelete(webhookID string) (err error) {
|
||||||
|
|
||||||
body, err := s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks)
|
_, err = s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks)
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = unmarshal(body, &st)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1875,6 +1923,16 @@ func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID st
|
|||||||
return err
|
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.
|
// MessageReactions gets all the users reactions for a specific emoji.
|
||||||
// channelID : The channel ID.
|
// channelID : The channel ID.
|
||||||
// messageID : The message ID.
|
// messageID : The message ID.
|
||||||
|
68
vendor/github.com/bwmarrin/discordgo/state.go
generated
vendored
68
vendor/github.com/bwmarrin/discordgo/state.go
generated
vendored
@ -42,6 +42,7 @@ type State struct {
|
|||||||
|
|
||||||
guildMap map[string]*Guild
|
guildMap map[string]*Guild
|
||||||
channelMap map[string]*Channel
|
channelMap map[string]*Channel
|
||||||
|
memberMap map[string]map[string]*Member
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewState creates an empty state.
|
// NewState creates an empty state.
|
||||||
@ -59,9 +60,18 @@ func NewState() *State {
|
|||||||
TrackPresences: true,
|
TrackPresences: true,
|
||||||
guildMap: make(map[string]*Guild),
|
guildMap: make(map[string]*Guild),
|
||||||
channelMap: make(map[string]*Channel),
|
channelMap: make(map[string]*Channel),
|
||||||
|
memberMap: make(map[string]map[string]*Member),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *State) createMemberMap(guild *Guild) {
|
||||||
|
members := make(map[string]*Member)
|
||||||
|
for _, m := range guild.Members {
|
||||||
|
members[m.User.ID] = m
|
||||||
|
}
|
||||||
|
s.memberMap[guild.ID] = members
|
||||||
|
}
|
||||||
|
|
||||||
// GuildAdd adds a guild to the current world state, or
|
// GuildAdd adds a guild to the current world state, or
|
||||||
// updates it if it already exists.
|
// updates it if it already exists.
|
||||||
func (s *State) GuildAdd(guild *Guild) error {
|
func (s *State) GuildAdd(guild *Guild) error {
|
||||||
@ -77,6 +87,14 @@ func (s *State) GuildAdd(guild *Guild) error {
|
|||||||
s.channelMap[c.ID] = c
|
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 {
|
if g, ok := s.guildMap[guild.ID]; ok {
|
||||||
// We are about to replace `g` in the state with `guild`, but first we need to
|
// 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`.
|
// make sure we preserve any fields that the `guild` doesn't contain from `g`.
|
||||||
@ -271,14 +289,19 @@ func (s *State) MemberAdd(member *Member) error {
|
|||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
for i, m := range guild.Members {
|
members, ok := s.memberMap[member.GuildID]
|
||||||
if m.User.ID == member.User.ID {
|
if !ok {
|
||||||
guild.Members[i] = member
|
return ErrStateNotFound
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m, ok := members[member.User.ID]
|
||||||
|
if !ok {
|
||||||
|
members[member.User.ID] = member
|
||||||
guild.Members = append(guild.Members, member)
|
guild.Members = append(guild.Members, member)
|
||||||
|
} else {
|
||||||
|
*m = *member // Update the actual data, which will also update the member pointer in the slice
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,6 +319,17 @@ func (s *State) MemberRemove(member *Member) error {
|
|||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
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 {
|
for i, m := range guild.Members {
|
||||||
if m.User.ID == member.User.ID {
|
if m.User.ID == member.User.ID {
|
||||||
guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)
|
guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)
|
||||||
@ -312,18 +346,17 @@ func (s *State) Member(guildID, userID string) (*Member, error) {
|
|||||||
return nil, ErrNilState
|
return nil, ErrNilState
|
||||||
}
|
}
|
||||||
|
|
||||||
guild, err := s.Guild(guildID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|
||||||
for _, m := range guild.Members {
|
members, ok := s.memberMap[guildID]
|
||||||
if m.User.ID == userID {
|
if !ok {
|
||||||
return m, nil
|
return nil, ErrStateNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m, ok := members[userID]
|
||||||
|
if ok {
|
||||||
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrStateNotFound
|
return nil, ErrStateNotFound
|
||||||
@ -427,7 +460,7 @@ func (s *State) ChannelAdd(channel *Channel) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.IsPrivate {
|
if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
|
||||||
s.PrivateChannels = append(s.PrivateChannels, channel)
|
s.PrivateChannels = append(s.PrivateChannels, channel)
|
||||||
} else {
|
} else {
|
||||||
guild, ok := s.guildMap[channel.GuildID]
|
guild, ok := s.guildMap[channel.GuildID]
|
||||||
@ -454,7 +487,7 @@ func (s *State) ChannelRemove(channel *Channel) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.IsPrivate {
|
if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
@ -735,6 +768,7 @@ func (s *State) onReady(se *Session, r *Ready) (err error) {
|
|||||||
|
|
||||||
for _, g := range s.Guilds {
|
for _, g := range s.Guilds {
|
||||||
s.guildMap[g.ID] = g
|
s.guildMap[g.ID] = g
|
||||||
|
s.createMemberMap(g)
|
||||||
|
|
||||||
for _, c := range g.Channels {
|
for _, c := range g.Channels {
|
||||||
s.channelMap[c.ID] = c
|
s.channelMap[c.ID] = c
|
||||||
@ -748,8 +782,8 @@ func (s *State) onReady(se *Session, r *Ready) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// onInterface handles all events related to states.
|
// OnInterface handles all events related to states.
|
||||||
func (s *State) onInterface(se *Session, i interface{}) (err error) {
|
func (s *State) OnInterface(se *Session, i interface{}) (err error) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return ErrNilState
|
return ErrNilState
|
||||||
}
|
}
|
||||||
|
87
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
87
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
@ -50,6 +50,10 @@ type Session struct {
|
|||||||
// active guilds and the members of the guilds.
|
// active guilds and the members of the guilds.
|
||||||
StateEnabled bool
|
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.
|
// Exposed but should not be modified by User.
|
||||||
|
|
||||||
// Whether the Data Websocket is ready
|
// Whether the Data Websocket is ready
|
||||||
@ -78,6 +82,9 @@ type Session struct {
|
|||||||
// The http client used for REST requests
|
// The http client used for REST requests
|
||||||
Client *http.Client
|
Client *http.Client
|
||||||
|
|
||||||
|
// Stores the last HeartbeatAck that was recieved (in UTC)
|
||||||
|
LastHeartbeatAck time.Time
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
handlersMu sync.RWMutex
|
handlersMu sync.RWMutex
|
||||||
handlers map[string][]*eventHandlerInstance
|
handlers map[string][]*eventHandlerInstance
|
||||||
@ -141,18 +148,30 @@ type Invite struct {
|
|||||||
Temporary bool `json:"temporary"`
|
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.
|
// A Channel holds all data related to an individual Discord channel.
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
GuildID string `json:"guild_id"`
|
GuildID string `json:"guild_id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
Type string `json:"type"`
|
Type ChannelType `json:"type"`
|
||||||
LastMessageID string `json:"last_message_id"`
|
LastMessageID string `json:"last_message_id"`
|
||||||
|
NSFW bool `json:"nsfw"`
|
||||||
Position int `json:"position"`
|
Position int `json:"position"`
|
||||||
Bitrate int `json:"bitrate"`
|
Bitrate int `json:"bitrate"`
|
||||||
IsPrivate bool `json:"is_private"`
|
Recipients []*User `json:"recipient"`
|
||||||
Recipient *User `json:"recipient"`
|
|
||||||
Messages []*Message `json:"-"`
|
Messages []*Message `json:"-"`
|
||||||
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
|
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
|
||||||
}
|
}
|
||||||
@ -292,13 +311,14 @@ type Presence struct {
|
|||||||
Game *Game `json:"game"`
|
Game *Game `json:"game"`
|
||||||
Nick string `json:"nick"`
|
Nick string `json:"nick"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
|
Since *int `json:"since"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
type Game struct {
|
type Game struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type int `json:"type"`
|
Type int `json:"type"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals json to Game struct
|
// UnmarshalJSON unmarshals json to Game struct
|
||||||
@ -509,6 +529,12 @@ type MessageReaction struct {
|
|||||||
ChannelID string `json:"channel_id"`
|
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
|
// Constants for the different bit offsets of text channel permissions
|
||||||
const (
|
const (
|
||||||
PermissionReadMessages = 1 << (iota + 10)
|
PermissionReadMessages = 1 << (iota + 10)
|
||||||
@ -579,3 +605,56 @@ const (
|
|||||||
PermissionManageServer |
|
PermissionManageServer |
|
||||||
PermissionAdministrator
|
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
|
||||||
|
)
|
||||||
|
18
vendor/github.com/bwmarrin/discordgo/user.go
generated
vendored
18
vendor/github.com/bwmarrin/discordgo/user.go
generated
vendored
@ -1,6 +1,9 @@
|
|||||||
package discordgo
|
package discordgo
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// A User stores all data for an individual Discord user.
|
// A User stores all data for an individual Discord user.
|
||||||
type User struct {
|
type User struct {
|
||||||
@ -24,3 +27,16 @@ func (u *User) String() string {
|
|||||||
func (u *User) Mention() string {
|
func (u *User) Mention() string {
|
||||||
return fmt.Sprintf("<@%s>", u.ID)
|
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
|
||||||
|
}
|
||||||
|
13
vendor/github.com/bwmarrin/discordgo/voice.go
generated
vendored
13
vendor/github.com/bwmarrin/discordgo/voice.go
generated
vendored
@ -796,7 +796,7 @@ func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For now, skip anything except audio.
|
// For now, skip anything except audio.
|
||||||
if rlen < 12 || recvbuf[0] != 0x80 {
|
if rlen < 12 || (recvbuf[0] != 0x80 && recvbuf[0] != 0x90) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -810,8 +810,17 @@ func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct
|
|||||||
copy(nonce[:], recvbuf[0:12])
|
copy(nonce[:], recvbuf[0:12])
|
||||||
p.Opus, _ = secretbox.Open(nil, recvbuf[12:rlen], &nonce, &v.op4.SecretKey)
|
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 {
|
if c != nil {
|
||||||
c <- &p
|
select {
|
||||||
|
case c <- &p:
|
||||||
|
case <-close:
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
52
vendor/github.com/bwmarrin/discordgo/wsapi.go
generated
vendored
52
vendor/github.com/bwmarrin/discordgo/wsapi.go
generated
vendored
@ -15,7 +15,6 @@ import (
|
|||||||
"compress/zlib"
|
"compress/zlib"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -87,7 +86,7 @@ func (s *Session) Open() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the version and encoding to the URL
|
// Add the version and encoding to the URL
|
||||||
s.gateway = fmt.Sprintf("%s?v=5&encoding=json", s.gateway)
|
s.gateway = s.gateway + "?v=" + APIVersion + "&encoding=json"
|
||||||
}
|
}
|
||||||
|
|
||||||
header := http.Header{}
|
header := http.Header{}
|
||||||
@ -131,6 +130,7 @@ func (s *Session) Open() (err error) {
|
|||||||
// lock.
|
// lock.
|
||||||
s.listening = make(chan interface{})
|
s.listening = make(chan interface{})
|
||||||
go s.listen(s.wsConn, s.listening)
|
go s.listen(s.wsConn, s.listening)
|
||||||
|
s.LastHeartbeatAck = time.Now().UTC()
|
||||||
|
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
|
||||||
@ -199,10 +199,13 @@ type helloOp struct {
|
|||||||
Trace []string `json:"_trace"`
|
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
|
// heartbeat sends regular heartbeats to Discord so it knows the client
|
||||||
// is still connected. If you do not send these heartbeats Discord will
|
// is still connected. If you do not send these heartbeats Discord will
|
||||||
// disconnect the websocket connection after a few seconds.
|
// 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")
|
s.log(LogInformational, "called")
|
||||||
|
|
||||||
@ -211,20 +214,26 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ticker := time.NewTicker(i * time.Millisecond)
|
ticker := time.NewTicker(heartbeatIntervalMsec * time.Millisecond)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
s.RLock()
|
||||||
|
last := s.LastHeartbeatAck
|
||||||
|
s.RUnlock()
|
||||||
sequence := atomic.LoadInt64(s.sequence)
|
sequence := atomic.LoadInt64(s.sequence)
|
||||||
s.log(LogInformational, "sending gateway websocket heartbeat seq %d", sequence)
|
s.log(LogInformational, "sending gateway websocket heartbeat seq %d", sequence)
|
||||||
s.wsMutex.Lock()
|
s.wsMutex.Lock()
|
||||||
err = wsConn.WriteJSON(heartbeatOp{1, sequence})
|
err = wsConn.WriteJSON(heartbeatOp{1, sequence})
|
||||||
s.wsMutex.Unlock()
|
s.wsMutex.Unlock()
|
||||||
|
if err != nil || time.Now().UTC().Sub(last) > (heartbeatIntervalMsec*FailedHeartbeatAcks) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err)
|
s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err)
|
||||||
s.Lock()
|
} else {
|
||||||
s.DataReady = false
|
s.log(LogError, "haven't gotten a heartbeat ACK in %v, triggering a reconnection", time.Now().UTC().Sub(last))
|
||||||
s.Unlock()
|
}
|
||||||
|
s.Close()
|
||||||
|
s.reconnect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Lock()
|
s.Lock()
|
||||||
@ -241,8 +250,10 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
type updateStatusData struct {
|
type updateStatusData struct {
|
||||||
IdleSince *int `json:"idle_since"`
|
IdleSince *int `json:"since"`
|
||||||
Game *Game `json:"game"`
|
Game *Game `json:"game"`
|
||||||
|
AFK bool `json:"afk"`
|
||||||
|
Status string `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type updateStatusOp struct {
|
type updateStatusOp struct {
|
||||||
@ -265,7 +276,10 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
|
|||||||
return ErrWSNotFound
|
return ErrWSNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
var usd updateStatusData
|
usd := updateStatusData{
|
||||||
|
Status: "online",
|
||||||
|
}
|
||||||
|
|
||||||
if idle > 0 {
|
if idle > 0 {
|
||||||
usd.IdleSince = &idle
|
usd.IdleSince = &idle
|
||||||
}
|
}
|
||||||
@ -398,7 +412,10 @@ func (s *Session) onEvent(messageType int, message []byte) {
|
|||||||
// Reconnect
|
// Reconnect
|
||||||
// Must immediately disconnect from gateway and reconnect to new gateway.
|
// Must immediately disconnect from gateway and reconnect to new gateway.
|
||||||
if e.Operation == 7 {
|
if e.Operation == 7 {
|
||||||
// TODO
|
s.log(LogInformational, "Closing and reconnecting in response to Op7")
|
||||||
|
s.Close()
|
||||||
|
s.reconnect()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid Session
|
// Invalid Session
|
||||||
@ -426,6 +443,14 @@ func (s *Session) onEvent(messageType int, message []byte) {
|
|||||||
return
|
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
|
// Do not try to Dispatch a non-Dispatch Message
|
||||||
if e.Operation != 0 {
|
if e.Operation != 0 {
|
||||||
// But we probably should be doing something with them.
|
// But we probably should be doing something with them.
|
||||||
@ -688,6 +713,13 @@ func (s *Session) reconnect() {
|
|||||||
return
|
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)
|
s.log(LogError, "error reconnecting to gateway, %s", err)
|
||||||
|
|
||||||
<-time.After(wait * time.Second)
|
<-time.After(wait * time.Second)
|
||||||
|
2
vendor/manifest
vendored
2
vendor/manifest
vendored
@ -61,7 +61,7 @@
|
|||||||
"importpath": "github.com/bwmarrin/discordgo",
|
"importpath": "github.com/bwmarrin/discordgo",
|
||||||
"repository": "https://github.com/bwmarrin/discordgo",
|
"repository": "https://github.com/bwmarrin/discordgo",
|
||||||
"vcs": "git",
|
"vcs": "git",
|
||||||
"revision": "d420e28024ad527390b43aa7f64e029083e11989",
|
"revision": "2fda7ce223a66a5b70b66987c22c3c94d022ee66",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"notests": true
|
"notests": true
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user