-
Notifications
You must be signed in to change notification settings - Fork 1
/
session.go
105 lines (89 loc) · 2.93 KB
/
session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package voice
import (
"context"
gateway2 "github.com/BOOMfinity/bfcord/gateway"
"github.com/BOOMfinity/go-utils/broadcaster"
"github.com/BOOMfinity/golog"
"github.com/BOOMfinity/wshelper"
"time"
)
type Session struct {
config *ConnectOptions
state *gateway2.VoiceStateUpdateEvent
server *gateway2.VoiceServerUpdateEvent
ws *wshelper.Connection
logger golog.Logger
eventChannel *broadcaster.Group[OPCode] // code 111: disconnection, 101: connection done
udp *udpConnection
ready *ReadyPayload
reconnectAttempt uint8
}
// NewSession function creates new session and issues first connection. Blocks until connection is finished (or until timeout occurs)
func NewSession(ctx context.Context, opts ConnectOptions) (*Session, error) {
conn := Session{logger: golog.New("voice").Module(opts.GuildID.String()), eventChannel: broadcaster.NewGroup[OPCode](), config: &opts}
if opts.Debug {
conn.logger.SetLevel(golog.LevelDebug)
}
conn.logger.Info().Send("Connection requested")
return &conn, conn.connect(ctx, &opts)
}
// connect method requests connection from main gateway, opens voice gateway connection and blocks until further connection steps are finished. Throws an error after 10s timeout.
func (s *Session) connect(ctx context.Context, opts *ConnectOptions) error {
start := time.Now()
events := s.eventChannel.Join() // upon successful connection 101 code is sent to this channel
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer func() {
cancel()
events.Close()
}()
s.server = opts.VoiceServer
s.state = opts.VoiceState
err := s.openGateway(ctx)
if err != nil {
return err
}
wait:
for {
select {
case op := <-events.Out:
if op.Data() == 101 {
break wait
}
case <-ctx.Done():
return ctx.Err()
}
}
s.logger.Info().Send("Connected in %vms", time.Now().Sub(start).Milliseconds())
return s.SendSpeaking(Microphone)
}
// Destroy method disconnects session from discord. Using SendOpusFrame after destroying will throw errors.
func (s *Session) Destroy() {
_ = s.ws.Close(1000, "bfcord-voice: destroy connection")
}
// internal destroy
func (s *Session) destroy(errored bool) {
s.udp.Close()
if errored {
s.logger.Warn().Send("Connection closed due to error")
} else {
s.logger.Info().Send("Connection closed")
}
s.config.OnClose()
}
// SendOpusFrame method writes raw audio frame to discord.
//
// Frame MUST be 20ms in length. We may allow changing the value later if needed.
//
// This method should not be used concurrently, as it will cause audio overlapping.
//
// Will throw ConnectionClosedError if connection is closed permanently.
func (s *Session) SendOpusFrame(frame []byte) error {
if s.udp.isClosed.Load() {
return ConnectionClosedError
}
return s.udp.WriteOpusFrame(frame)
}
// IsClosed will return true if session is fully closed.
func (s *Session) IsClosed() bool {
return s.udp.isClosed.Load()
}