github.com/diamondburned/arikawa/v2@v2.1.0/voice/voicegateway/commands.go (about) 1 package voicegateway 2 3 import ( 4 "context" 5 "time" 6 7 "github.com/diamondburned/arikawa/v2/discord" 8 "github.com/pkg/errors" 9 ) 10 11 var ( 12 // ErrMissingForIdentify is an error when we are missing information to identify. 13 ErrMissingForIdentify = errors.New("missing GuildID, UserID, SessionID, or Token for identify") 14 15 // ErrMissingForResume is an error when we are missing information to resume. 16 ErrMissingForResume = errors.New("missing GuildID, SessionID, or Token for resuming") 17 ) 18 19 // OPCode 0 20 // https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-websocket-connection-example-voice-identify-payload 21 type IdentifyData struct { 22 GuildID discord.GuildID `json:"server_id"` // yes, this should be "server_id" 23 UserID discord.UserID `json:"user_id"` 24 SessionID string `json:"session_id"` 25 Token string `json:"token"` 26 } 27 28 // Identify sends an Identify operation (opcode 0) to the Gateway Gateway. 29 func (c *Gateway) Identify() error { 30 ctx, cancel := context.WithTimeout(context.Background(), c.Timeout) 31 defer cancel() 32 33 return c.IdentifyCtx(ctx) 34 } 35 36 // IdentifyCtx sends an Identify operation (opcode 0) to the Gateway Gateway. 37 func (c *Gateway) IdentifyCtx(ctx context.Context) error { 38 guildID := c.state.GuildID 39 userID := c.state.UserID 40 sessionID := c.state.SessionID 41 token := c.state.Token 42 43 if guildID == 0 || userID == 0 || sessionID == "" || token == "" { 44 return ErrMissingForIdentify 45 } 46 47 return c.SendCtx(ctx, IdentifyOP, IdentifyData{ 48 GuildID: guildID, 49 UserID: userID, 50 SessionID: sessionID, 51 Token: token, 52 }) 53 } 54 55 // OPCode 1 56 // https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-example-select-protocol-payload 57 type SelectProtocol struct { 58 Protocol string `json:"protocol"` 59 Data SelectProtocolData `json:"data"` 60 } 61 62 type SelectProtocolData struct { 63 Address string `json:"address"` 64 Port uint16 `json:"port"` 65 Mode string `json:"mode"` 66 } 67 68 // SelectProtocol sends a Select Protocol operation (opcode 1) to the Gateway Gateway. 69 func (c *Gateway) SelectProtocol(data SelectProtocol) error { 70 ctx, cancel := context.WithTimeout(context.Background(), c.Timeout) 71 defer cancel() 72 73 return c.SelectProtocolCtx(ctx, data) 74 } 75 76 // SelectProtocolCtx sends a Select Protocol operation (opcode 1) to the Gateway Gateway. 77 func (c *Gateway) SelectProtocolCtx(ctx context.Context, data SelectProtocol) error { 78 return c.SendCtx(ctx, SelectProtocolOP, data) 79 } 80 81 // OPCode 3 82 // https://discord.com/developers/docs/topics/voice-connections#heartbeating-example-heartbeat-payload 83 // type Heartbeat uint64 84 85 // Heartbeat sends a Heartbeat operation (opcode 3) to the Gateway Gateway. 86 func (c *Gateway) Heartbeat() error { 87 ctx, cancel := context.WithTimeout(context.Background(), c.Timeout) 88 defer cancel() 89 90 return c.HeartbeatCtx(ctx) 91 } 92 93 // HeartbeatCtx sends a Heartbeat operation (opcode 3) to the Gateway Gateway. 94 func (c *Gateway) HeartbeatCtx(ctx context.Context) error { 95 return c.SendCtx(ctx, HeartbeatOP, time.Now().UnixNano()) 96 } 97 98 // https://discord.com/developers/docs/topics/voice-connections#speaking 99 type SpeakingFlag uint64 100 101 const ( 102 NotSpeaking SpeakingFlag = 0 103 Microphone SpeakingFlag = 1 << iota 104 Soundshare 105 Priority 106 ) 107 108 // OPCode 5 109 // https://discord.com/developers/docs/topics/voice-connections#speaking-example-speaking-payload 110 type SpeakingData struct { 111 Speaking SpeakingFlag `json:"speaking"` 112 Delay int `json:"delay"` 113 SSRC uint32 `json:"ssrc"` 114 UserID discord.UserID `json:"user_id,omitempty"` 115 } 116 117 // Speaking sends a Speaking operation (opcode 5) to the Gateway Gateway. 118 func (c *Gateway) Speaking(flag SpeakingFlag) error { 119 ctx, cancel := context.WithTimeout(context.Background(), c.Timeout) 120 defer cancel() 121 122 return c.SpeakingCtx(ctx, flag) 123 } 124 125 // SpeakingCtx sends a Speaking operation (opcode 5) to the Gateway Gateway. 126 func (c *Gateway) SpeakingCtx(ctx context.Context, flag SpeakingFlag) error { 127 // How do we allow a user to stop speaking? 128 // Also: https://discordapp.com/developers/docs/topics/voice-connections#voice-data-interpolation 129 130 return c.SendCtx(ctx, SpeakingOP, SpeakingData{ 131 Speaking: flag, 132 Delay: 0, 133 SSRC: c.ready.SSRC, 134 }) 135 } 136 137 // OPCode 7 138 // https://discord.com/developers/docs/topics/voice-connections#resuming-voice-connection-example-resume-connection-payload 139 type ResumeData struct { 140 GuildID discord.GuildID `json:"server_id"` // yes, this should be "server_id" 141 SessionID string `json:"session_id"` 142 Token string `json:"token"` 143 } 144 145 // Resume sends a Resume operation (opcode 7) to the Gateway Gateway. 146 func (c *Gateway) Resume() error { 147 ctx, cancel := context.WithTimeout(context.Background(), c.Timeout) 148 defer cancel() 149 return c.ResumeCtx(ctx) 150 } 151 152 // ResumeCtx sends a Resume operation (opcode 7) to the Gateway Gateway. 153 func (c *Gateway) ResumeCtx(ctx context.Context) error { 154 guildID := c.state.GuildID 155 sessionID := c.state.SessionID 156 token := c.state.Token 157 158 if !guildID.IsValid() || sessionID == "" || token == "" { 159 return ErrMissingForResume 160 } 161 162 return c.SendCtx(ctx, ResumeOP, ResumeData{ 163 GuildID: guildID, 164 SessionID: sessionID, 165 Token: token, 166 }) 167 }