2020-01-15 04:43:34 +00:00
|
|
|
package gateway
|
|
|
|
|
|
|
|
import (
|
2020-01-15 07:34:18 +00:00
|
|
|
"context"
|
|
|
|
|
2020-10-28 22:39:59 +00:00
|
|
|
"github.com/diamondburned/arikawa/v2/discord"
|
2020-01-15 07:34:18 +00:00
|
|
|
"github.com/pkg/errors"
|
2020-01-15 04:43:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Rules: VOICE_STATE_UPDATE -> VoiceStateUpdateEvent
|
|
|
|
|
2020-02-22 07:35:11 +00:00
|
|
|
// Identify structure is at identify.go
|
2020-01-15 04:43:34 +00:00
|
|
|
|
2020-11-29 01:22:03 +00:00
|
|
|
// Identify sends off the Identify command with the Gateway's IdentifyData.
|
2020-01-15 04:43:34 +00:00
|
|
|
func (g *Gateway) Identify() error {
|
2020-01-15 07:34:18 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
2020-07-11 19:50:32 +00:00
|
|
|
return g.IdentifyCtx(ctx)
|
|
|
|
}
|
|
|
|
|
2020-11-29 01:22:03 +00:00
|
|
|
// IdentifyCtx sends off the Identify command with the Gateway's IdentifyData
|
|
|
|
// with the given context for time out.
|
2020-07-11 19:50:32 +00:00
|
|
|
func (g *Gateway) IdentifyCtx(ctx context.Context) error {
|
2020-01-15 07:34:18 +00:00
|
|
|
if err := g.Identifier.Wait(ctx); err != nil {
|
2020-05-16 21:14:49 +00:00
|
|
|
return errors.Wrap(err, "can't wait for identify()")
|
2020-01-15 07:34:18 +00:00
|
|
|
}
|
|
|
|
|
2020-07-11 19:50:32 +00:00
|
|
|
return g.SendCtx(ctx, IdentifyOP, g.Identifier)
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ResumeData struct {
|
|
|
|
Token string `json:"token"`
|
|
|
|
SessionID string `json:"session_id"`
|
|
|
|
Sequence int64 `json:"seq"`
|
|
|
|
}
|
|
|
|
|
2020-01-16 03:28:21 +00:00
|
|
|
// Resume sends to the Websocket a Resume OP, but it doesn't actually resume
|
|
|
|
// from a dead connection. Start() resumes from a dead connection.
|
|
|
|
func (g *Gateway) Resume() error {
|
2020-07-11 19:50:32 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
return g.ResumeCtx(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResumeCtx sends to the Websocket a Resume OP, but it doesn't actually resume
|
|
|
|
// from a dead connection. Start() resumes from a dead connection.
|
|
|
|
func (g *Gateway) ResumeCtx(ctx context.Context) error {
|
2020-01-16 03:28:21 +00:00
|
|
|
var (
|
2020-12-31 08:35:37 +00:00
|
|
|
ses = g.SessionID()
|
2020-01-16 03:28:21 +00:00
|
|
|
seq = g.Sequence.Get()
|
|
|
|
)
|
|
|
|
|
|
|
|
if ses == "" || seq == 0 {
|
|
|
|
return ErrMissingForResume
|
|
|
|
}
|
|
|
|
|
2020-07-11 19:50:32 +00:00
|
|
|
return g.SendCtx(ctx, ResumeOP, ResumeData{
|
2020-01-16 03:28:21 +00:00
|
|
|
Token: g.Identifier.Token,
|
|
|
|
SessionID: ses,
|
|
|
|
Sequence: seq,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
// HeartbeatData is the last sequence number to be sent.
|
|
|
|
type HeartbeatData int
|
|
|
|
|
|
|
|
func (g *Gateway) Heartbeat() error {
|
2020-07-11 19:50:32 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
return g.HeartbeatCtx(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Gateway) HeartbeatCtx(ctx context.Context) error {
|
|
|
|
return g.SendCtx(ctx, HeartbeatOP, g.Sequence.Get())
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type RequestGuildMembersData struct {
|
2020-07-21 20:27:59 +00:00
|
|
|
GuildID []discord.GuildID `json:"guild_id"`
|
|
|
|
UserIDs []discord.UserID `json:"user_ids,omitempty"`
|
2020-01-15 04:43:34 +00:00
|
|
|
|
2020-10-16 09:17:22 +00:00
|
|
|
Query string `json:"query"`
|
2020-01-15 04:43:34 +00:00
|
|
|
Limit uint `json:"limit"`
|
|
|
|
Presences bool `json:"presences,omitempty"`
|
2020-07-11 19:50:32 +00:00
|
|
|
Nonce string `json:"nonce,omitempty"`
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
|
2020-01-18 22:35:07 +00:00
|
|
|
func (g *Gateway) RequestGuildMembers(data RequestGuildMembersData) error {
|
2020-07-11 19:50:32 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
return g.RequestGuildMembersCtx(ctx, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Gateway) RequestGuildMembersCtx(
|
|
|
|
ctx context.Context, data RequestGuildMembersData) error {
|
|
|
|
|
|
|
|
return g.SendCtx(ctx, RequestGuildMembersOP, data)
|
2020-01-18 22:35:07 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
type UpdateVoiceStateData struct {
|
2020-07-21 20:27:59 +00:00
|
|
|
GuildID discord.GuildID `json:"guild_id"`
|
|
|
|
ChannelID discord.ChannelID `json:"channel_id"` // nullable
|
2020-05-08 21:25:07 +00:00
|
|
|
SelfMute bool `json:"self_mute"`
|
|
|
|
SelfDeaf bool `json:"self_deaf"`
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
|
|
|
|
2020-01-18 22:35:07 +00:00
|
|
|
func (g *Gateway) UpdateVoiceState(data UpdateVoiceStateData) error {
|
2020-07-11 19:50:32 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
return g.UpdateVoiceStateCtx(ctx, data)
|
|
|
|
}
|
|
|
|
|
2020-11-29 01:22:03 +00:00
|
|
|
func (g *Gateway) UpdateVoiceStateCtx(ctx context.Context, data UpdateVoiceStateData) error {
|
2020-07-11 19:50:32 +00:00
|
|
|
return g.SendCtx(ctx, VoiceStateUpdateOP, data)
|
2020-01-18 22:35:07 +00:00
|
|
|
}
|
|
|
|
|
2020-11-29 01:22:03 +00:00
|
|
|
// UpdateStatusData is sent by this client to indicate a presence or status
|
|
|
|
// update.
|
2020-01-15 04:43:34 +00:00
|
|
|
type UpdateStatusData struct {
|
2020-04-01 03:45:56 +00:00
|
|
|
Since discord.UnixMsTimestamp `json:"since"` // 0 if not idle
|
|
|
|
|
2020-11-29 01:22:03 +00:00
|
|
|
// Activities can either be null, an empty slice, or be omitted.
|
2020-04-01 03:45:56 +00:00
|
|
|
Activities *[]discord.Activity `json:"activities,omitempty"`
|
2020-01-15 04:43:34 +00:00
|
|
|
|
2020-11-29 01:22:03 +00:00
|
|
|
Status Status `json:"status"`
|
|
|
|
AFK bool `json:"afk"`
|
2020-01-15 04:43:34 +00:00
|
|
|
}
|
2020-01-18 22:35:07 +00:00
|
|
|
|
|
|
|
func (g *Gateway) UpdateStatus(data UpdateStatusData) error {
|
2020-07-11 19:50:32 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
return g.UpdateStatusCtx(ctx, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Gateway) UpdateStatusCtx(ctx context.Context, data UpdateStatusData) error {
|
|
|
|
return g.SendCtx(ctx, StatusUpdateOP, data)
|
2020-01-18 22:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Undocumented
|
|
|
|
type GuildSubscribeData struct {
|
2020-07-21 20:27:59 +00:00
|
|
|
Typing bool `json:"typing"`
|
|
|
|
Activities bool `json:"activities"`
|
|
|
|
GuildID discord.GuildID `json:"guild_id"`
|
2020-03-16 05:55:45 +00:00
|
|
|
|
|
|
|
// Channels is not documented. It's used to fetch the right members sidebar.
|
2020-10-17 10:18:35 +00:00
|
|
|
Channels map[discord.ChannelID][][2]int `json:"channels,omitempty"`
|
2020-01-18 22:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Gateway) GuildSubscribe(data GuildSubscribeData) error {
|
2020-07-11 19:50:32 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
return g.GuildSubscribeCtx(ctx, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Gateway) GuildSubscribeCtx(ctx context.Context, data GuildSubscribeData) error {
|
|
|
|
return g.SendCtx(ctx, GuildSubscriptionsOP, data)
|
2020-01-18 22:35:07 +00:00
|
|
|
}
|